From: Simon Glass <sjg@chromium.org>
Add a -L flag to the ut command that checks for memory leaks around each
test. When enabled, mallinfo() is called at the start and end of
ut_run_test() and any difference in allocated bytes is reported.
This is useful for finding memory leaks in driver model and other
subsystems that should be fully cleaned up between tests.
Usage: ut -L dm [test_name]
Signed-off-by: Simon Glass <sjg@chromium.org>
---
doc/develop/malloc.rst | 22 +++++++++++++++++++++-
include/test/test.h | 2 ++
test/cmd_ut.c | 8 +++++++-
test/test-main.c | 15 +++++++++++++++
4 files changed, 45 insertions(+), 2 deletions(-)
@@ -500,9 +500,29 @@ by checking ``malloc_get_info()`` before and after::
assert(before.malloc_count == after.malloc_count);
assert(before.in_use_bytes == after.in_use_bytes);
+**Automatic leak checking with ut -L**
+
+The ``ut`` command accepts a ``-L`` flag that checks for memory leaks around
+each test. It takes a ``mallinfo()`` snapshot at the start of ``ut_run_test()``
+and compares it at the end, after both ``test_pre_run()`` and
+``test_post_run()`` have completed::
+
+ => ut -L dm dm_test_acpi_bgrt
+ Test: acpi_bgrt: acpi.c
+ Leak: 448 bytes: acpi_bgrt
+ ...
+
+When using uman, pass ``--leak-check``::
+
+ $ um t --leak-check dm_test_acpi_bgrt
+
+This makes it easy to scan an entire test suite for leaks::
+
+ $ um t --leak-check -V dm
+
**Practical workflow**
-1. Add ``ut_check_delta()`` assertions to your test to detect leaks
+1. Run ``um t --leak-check -V dm`` (or another suite) to find leaky tests
2. When a leak is detected, add ``malloc_dump_to_file()`` calls before and
after the leaking operation
3. Run the test and compare the dump files to identify leaked allocations
@@ -102,6 +102,7 @@ struct ut_arg {
* @arg_error: Set if ut_str/int/bool() detects a type mismatch
* @keep_record: Preserve console recording when ut_fail() is called
* @emit_result: Emit result line after each test completes
+ * @leak_check: Check for memory leaks around each test
* @video_ctx: Vidconsole context for test message display (allocated on use)
* @video_save: Saved framebuffer region for video tests
* @priv: Private data for tests to use as needed
@@ -139,6 +140,7 @@ struct unit_test_state {
bool arg_error;
bool keep_record;
bool emit_result;
+ bool leak_check;
void *video_ctx;
struct abuf video_save;
char priv[UT_PRIV_SIZE];
@@ -258,6 +258,7 @@ static int do_ut(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
bool force_run = false;
bool keep_record = false;
bool emit_result = false;
+ bool leak_check = false;
int runs_per_text = 1;
int workers = 0, worker_id = 0;
struct suite *ste;
@@ -283,6 +284,9 @@ static int do_ut(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
case 'm':
force_run = true;
break;
+ case 'L':
+ leak_check = true;
+ break;
case 'I':
test_insert = str + 1;
if (!strchr(test_insert, ':'))
@@ -316,6 +320,7 @@ next_arg:
ut_init_state(&uts);
uts.keep_record = keep_record;
uts.emit_result = emit_result;
+ uts.leak_check = leak_check;
uts.workers = workers;
uts.worker_id = worker_id;
name = argv[0];
@@ -363,9 +368,10 @@ next_arg:
}
U_BOOT_LONGHELP(ut,
- "[-Efmrs] [-R] [-I<n>:<one_test>] [-P<n>:<w>] <suite> [<test> [<args>...]]\n"
+ "[-ELfmrs] [-R] [-I<n>:<one_test>] [-P<n>:<w>] <suite> [<test> [<args>...]]\n"
" - run unit tests\n"
" -E Emit result line after each test\n"
+ " -L Check for memory leaks around each test\n"
" -r<runs> Number of times to run each test\n"
" -f/-m Force 'manual' tests to run as well\n"
" -I Test to run after <n> other tests have run\n"
@@ -631,6 +631,7 @@ static int ut_run_test(struct unit_test_state *uts, struct unit_test *test,
{
const char *fname = strrchr(test->file, '/') + 1;
const char *note = "";
+ struct mallinfo leak_before;
int old_fail_count;
int ret;
@@ -638,6 +639,9 @@ static int ut_run_test(struct unit_test_state *uts, struct unit_test *test,
note = " (flat tree)";
printf("Test: %s: %s%s\n", test_name, fname, note);
+ if (uts->leak_check)
+ leak_before = mallinfo();
+
/* Allow access to test state from drivers */
ut_set_state(uts);
@@ -659,6 +663,17 @@ static int ut_run_test(struct unit_test_state *uts, struct unit_test *test,
ut_set_state(NULL);
+ if (uts->leak_check) {
+ struct mallinfo leak_after;
+ int diff;
+
+ leak_after = mallinfo();
+ diff = leak_after.uordblks - leak_before.uordblks;
+ if (diff)
+ printf("Leak: %d bytes: %s%s\n", diff, test_name,
+ note);
+ }
+
if (uts->emit_result) {
bool passed = uts->cur.fail_count == old_fail_count;