From patchwork Mon Mar 16 18:30:29 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2017 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1773685886; bh=FbodRPITxWBrOtuufPy3Z6qimg7890iTtZ1cXcmoh4Q=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=rKDm+tCO6GHIzXU8q1U2/jK4Tlb1wOO3Hm50lLVrSunOZx2JYy16UuoUKid1NB/sy HZ3OFwRQHhlIrVuDFyB/IuHw+8uhYC36FNmzsBUj0ZsapRq7MPXKEev8bJsCtfwIRe hpt9CmxkjViXppkYxq4fSQHoh67EvcAKmwOLFLOwnnCRY9ejj0mNAgMyohXwZxE3i5 1R76rGNY5gYbCPEzr2lcacE1whqfdkMUUdzaEkYWDVGZVJBvtz67dJXbIuR8h8ZC5X N+vKAcXZ/YnJcJUA91hAlu2XLwhgwXNUGtq6PCe78DdJBKWhl/lYKrRyOBcHin0DAm FKFN/z1exvEFg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 841BA6A0A9 for ; Mon, 16 Mar 2026 12:31:26 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id OqWfY9giIch5 for ; Mon, 16 Mar 2026 12:31:26 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1773685885; bh=FbodRPITxWBrOtuufPy3Z6qimg7890iTtZ1cXcmoh4Q=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=p6UodCOVm5i2E3TESynIxmgztMXz7xMrVdXQd+HSJGsV54rXbKi0/lal/il8Io4EW M7vMHTwzIUEk1gNS8Aorpa++scIV60JQGrV/SrqYsFXtA5Fhy/+YayK9pZ6jkisDFz n3+6YabvTZ81K22A/o6wbRcP1KkX/uTwD0F2qQ7c/IqMm78mlFCti3dNvQpdC8R6kC 0fH8I663hV1yk4rAvGXu5uyL+Eg4gkRJNjpoXhKAQ05ZTC9bNnQkLEi8octFyWqZdp OcZM2fsrHfVBWEui1jf4mAMkV9AeFXUZixzMSMYopEJyNNi8ujnJubLIduEkrzFlPe chi5SsXs+0TsQ== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 229C86A07B for ; Mon, 16 Mar 2026 12:31:25 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1773685880; bh=kmiXXfQiUFfqhJsbEZUptj/+jkY6pVkYCOJckOUkoi8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Gh4AkwDtC7G9MujDwk/vaRPQk7F4MNOiBSWR1pefLqZRTzvTdPTREc88Ev1jr0+p7 uwYOvh7YieAsenp5n1Y3aroZfqN6DjzLyUxbzdNlwb1xfLnCP/XysoPx+b3mxpmGgf 2hms2iEFhaaIt4tgPdGvi7HIatOnFubdPvML+YrcZmHo20ADCIYVm6ZziSSu6wnBLk Ggl2c8S2O36CWe1bkjAxArOUrRq2bnXGDdQaFmHbGXjS9nrVC06cl711rp0BfxTtZO Ny0stTK+nZPobnZjJhRpdiJQDELDcNdJGCuE+aPR9FdXEx4d4KErT13uiywSk5hGK/ 0islaOdaYaMjg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 4E1B96A08D; Mon, 16 Mar 2026 12:31:20 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id mM7efshMiMdS; Mon, 16 Mar 2026 12:31:20 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1773685876; bh=1khvGZ9MgcPeSNJqEsIFlaCGUPHjTiWINDeS0HNe9Bw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HiSpQmSbMQdLC/8dU5dcee1oajtCUifd7Bv+JSf7NxmB+6hUV1joMJ3R0T1bL41Ev n6XVTF4m7I2aVrN772svcjj9oU6ZWmevDcW/3+5yV1c9MAUKgNKB7GxZxqqzvwpffz Dw/YUqAXPNqt15xzC9CBTLXUjoih1cVFbZOT5Tm869cwd0/He4TD3Qfvaqy2McfyQp IfkZVc5m2iuydSVNwr9CDYu5DEbIVrBvt+RVdyQqTdmjR4fTC8/UmCcEdVbsaE+zE3 +JeGDR6A3SDsu0NP81VA4N40BR13eSyDA+6oTFQmTOkkPRi2LY+bLho1Ae2BlW07cD ZtrrHGEUI64zw== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 9D8666A07B; Mon, 16 Mar 2026 12:31:16 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Mon, 16 Mar 2026 12:30:29 -0600 Message-ID: <20260316183050.3855921-7-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260316183050.3855921-1-sjg@u-boot.org> References: <20260316183050.3855921-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 2MLKW45EXAW6NSHCKCRFON35ELOGA4NQ X-Message-ID-Hash: 2MLKW45EXAW6NSHCKCRFON35ELOGA4NQ X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 06/17] test: Add memory leak checking option to ut command List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass 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 --- 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(-) diff --git a/doc/develop/malloc.rst b/doc/develop/malloc.rst index 7e46c05dfde..74acc3220af 100644 --- a/doc/develop/malloc.rst +++ b/doc/develop/malloc.rst @@ -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 diff --git a/include/test/test.h b/include/test/test.h index b81cab4d7a4..7d28948eb5f 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -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]; diff --git a/test/cmd_ut.c b/test/cmd_ut.c index f8ff02bdbc7..7de10b32065 100644 --- a/test/cmd_ut.c +++ b/test/cmd_ut.c @@ -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:] [-P:] [ [...]]\n" + "[-ELfmrs] [-R] [-I:] [-P:] [ [...]]\n" " - run unit tests\n" " -E Emit result line after each test\n" + " -L Check for memory leaks around each test\n" " -r Number of times to run each test\n" " -f/-m Force 'manual' tests to run as well\n" " -I Test to run after other tests have run\n" diff --git a/test/test-main.c b/test/test-main.c index 77223cfbcb7..be13084ed92 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -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;