From patchwork Wed Dec 10 00:07:20 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 886 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=1765325411; bh=MdVDqXRYt/cgw8gj3viR1p20zpbN1diSjQsE1MYIaxg=; 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=u5Qoh32YikryQIT5cDBeV6X0IrfIwHvNG9ed4dIF8ac69HunwXj2I+L0+AIbLJjsv VAr2C/XqCBAFg+QUeejKxlh1Ha76R7W16RB3cwhf7+Zc1l+OJkPxTyktYAGT8vLPK8 6jhjitpsTnJeZ6gD1bfQNHBFn+RsFDhWlXUQuiPhWQmu+BmlwSeHIm3+Gxxst2oNEf DfcYi7hItrX/CbkNVQ9G4gjSopkMnYYlUpxAw2Z5+/YYF8fKSL9QDRyFiS3Af6gbrq anJ51sZLkm3IvbdabyHYou/5Aj+rqRA11BCJzlgrc0aNZ/KKODDSRyNSxsmpG+HdUQ ARo5Pfvgp/3KA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 9B3C16896E for ; Tue, 9 Dec 2025 17:10:11 -0700 (MST) 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 NKwYfnymqZTK for ; Tue, 9 Dec 2025 17:10:11 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765325411; bh=MdVDqXRYt/cgw8gj3viR1p20zpbN1diSjQsE1MYIaxg=; 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=u5Qoh32YikryQIT5cDBeV6X0IrfIwHvNG9ed4dIF8ac69HunwXj2I+L0+AIbLJjsv VAr2C/XqCBAFg+QUeejKxlh1Ha76R7W16RB3cwhf7+Zc1l+OJkPxTyktYAGT8vLPK8 6jhjitpsTnJeZ6gD1bfQNHBFn+RsFDhWlXUQuiPhWQmu+BmlwSeHIm3+Gxxst2oNEf DfcYi7hItrX/CbkNVQ9G4gjSopkMnYYlUpxAw2Z5+/YYF8fKSL9QDRyFiS3Af6gbrq anJ51sZLkm3IvbdabyHYou/5Aj+rqRA11BCJzlgrc0aNZ/KKODDSRyNSxsmpG+HdUQ ARo5Pfvgp/3KA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 86CFE689EA for ; Tue, 9 Dec 2025 17:10:11 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765325409; bh=GVCvsWRiuMcY4ZJPVV5YjAWb73VJPiGWAWoQcbfGSgw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fm1OA04UylMRUkWu8aVU/NvhixLMoyQg+aKzWvD4hC1qDmP0/ZJoxLRc7EJO9FZwe 37IRAOyDqRb3Uf/ii9Hys/fxzErzxZateJA75sde94778MQ0vV7ziL08eMSWCsqiE9 8xQgU+gDD9WUqQjMLMoW0cSIWfB93i0gjP36VDsa5RdnKDJRd51940pQ/4gmCPVb1Y tf4Ikzs2Vcm+fB/H51ffAwKsesjySodw9OR9Pc8/WayPHkxaAmmL5+LMphO42muFyl mNqPsBcjqO/DrxLLyxR8e3OGuadfPQXAW+T3C5lMxEx/K0Rk2UwGoDPSjnm7gqdUtw MDXRmNGyeOGrw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 5876468A47; Tue, 9 Dec 2025 17:10:09 -0700 (MST) 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 PjFTnA5m2rSZ; Tue, 9 Dec 2025 17:10:09 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765325403; bh=kNpf+c0MT1Ul/5niTGMYMx02jIXRaHVJyUZGfq5Wx9U=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=X9gxIAD8qa6dU9+HwBR+WcFPy1vF7+umAlOeSzUjHceYU+GV7XVjQFLw1O5HkA0LH /eJo3e1cZDQMKyyQpknmpvsWhHg4XD1InDRNSYoB2yabWcKN5a+Th8/a3MN9v6t6Gh uysvbt/TVpaFchao7HKCgsk9qdF/gDwoKAeK/mwaWhrHF5vZtJP6c/C6RCe7F4q8f1 RsPSRCeB10Isi7KkMCl+aW8j5DjYcYhfXQ9R4xzBZ7VuMcfRdNm2OqMDfrTROY7mM0 WR1btrk6olMcBttgPxFCTAJWuVPDIUfkg2lMKM6WRFrZiSpv1HpYb8vYzqSVcWy261 vX7qu96dnn5wQ== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 768B4689EA; Tue, 9 Dec 2025 17:10:03 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Tue, 9 Dec 2025 17:07:20 -0700 Message-ID: <20251210000737.180797-30-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251210000737.180797-1-sjg@u-boot.org> References: <20251210000737.180797-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 2N6YV7C3OAOM3ZV2RZF5KZ4PYD6FHHB3 X-Message-ID-Hash: 2N6YV7C3OAOM3ZV2RZF5KZ4PYD6FHHB3 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: Heinrich Schuchardt , Simon Glass , Claude X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 29/35] malloc: Add malloc dump command to walk the heap 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 new 'malloc dump' command that walks the dlmalloc heap from start to end, printing each chunk's address, size (in hex), and status (used/free/top). This is useful for debugging memory allocation issues. When CONFIG_MCHECK_HEAP_PROTECTION is enabled, the caller string is also shown if available. Example output: Heap dump: 18a1d000 - 1ea1f000 Address Size Status ---------------------------------- 18a1d000 10 (chunk header) 18a1d010 90 used 18adfc30 60 18adff90 5f3f030 top 1ea1f000 end ---------------------------------- Used: c2ef0 bytes in 931 chunks Free: 5f3f0c0 bytes in 2 chunks + top Expand the console-record size to handle this command. Co-developed-by: Claude Signed-off-by: Simon Glass --- cmd/malloc.c | 14 ++++++-- common/Kconfig | 1 + common/dlmalloc.c | 71 +++++++++++++++++++++++++++++++++++++++- doc/usage/cmd/malloc.rst | 22 +++++++++++++ include/malloc.h | 8 +++++ test/cmd/malloc.c | 20 +++++++++++ 6 files changed, 133 insertions(+), 3 deletions(-) diff --git a/cmd/malloc.c b/cmd/malloc.c index 3750a16158b..9c7dfbfc0c3 100644 --- a/cmd/malloc.c +++ b/cmd/malloc.c @@ -30,8 +30,18 @@ static int do_malloc_info(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_malloc_dump(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + malloc_dump(); + + return 0; +} + U_BOOT_LONGHELP(malloc, - "info - display malloc statistics\n"); + "info - display malloc statistics\n" + "malloc dump - dump heap chunks (address, size, status)\n"); U_BOOT_CMD_WITH_SUBCMDS(malloc, "malloc information", malloc_help_text, - U_BOOT_SUBCMD_MKENT(info, 1, 1, do_malloc_info)); + U_BOOT_SUBCMD_MKENT(info, 1, 1, do_malloc_info), + U_BOOT_SUBCMD_MKENT(dump, 1, 1, do_malloc_dump)); diff --git a/common/Kconfig b/common/Kconfig index 3bd11f44c51..0aa32145710 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -26,6 +26,7 @@ config CONSOLE_RECORD_INIT_F config CONSOLE_RECORD_OUT_SIZE hex "Output buffer size" depends on CONSOLE_RECORD + default 0x2000 if CMD_MALLOC && UNIT_TEST default 0x1000 if UNIT_TEST default 0x400 help diff --git a/common/dlmalloc.c b/common/dlmalloc.c index 420bed1c480..b40963604e4 100644 --- a/common/dlmalloc.c +++ b/common/dlmalloc.c @@ -635,7 +635,7 @@ DECLARE_GLOBAL_DATA_PTR; #if CONFIG_IS_ENABLED(MCHECK_HEAP_PROTECTION) || CONFIG_IS_ENABLED(MALLOC_DEBUG) #define STATIC_IF_MCHECK static -#ifdef CONFIG_MCHECK_HEAP_PROTECTION +#if CONFIG_IS_ENABLED(MCHECK_HEAP_PROTECTION) #undef MALLOC_COPY #undef MALLOC_ZERO static inline void MALLOC_ZERO(void *p, size_t sz) { memset(p, 0, sz); } @@ -7021,6 +7021,75 @@ void malloc_disable_testing(void) malloc_testing = false; } +void malloc_dump(void) +{ + mchunkptr q; + msegmentptr s; + size_t used = 0, free_space = 0; + int used_count = 0, free_count = 0; + + if (!is_initialized(gm)) { + printf("dlmalloc not initialized\n"); + return; + } + + printf("Heap dump: %lx - %lx\n", mem_malloc_start, mem_malloc_end); + printf("%12s %10s %s\n", "Address", "Size", "Status"); + printf("----------------------------------\n"); + + s = &gm->seg; + while (s != 0) { + q = align_as_chunk(s->base); + + /* Show chunk header before first allocation */ + printf("%12lx %10zx (chunk header)\n", (ulong)s->base, + (size_t)((char *)chunk2mem(q) - (char *)s->base)); + + while (segment_holds(s, q) && + q != gm->top && q->head != FENCEPOST_HEAD) { + size_t sz = chunksize(q); + void *mem = chunk2mem(q); + + if (is_inuse(q)) { +#if CONFIG_IS_ENABLED(MCHECK_HEAP_PROTECTION) + struct mcheck_hdr *hdr = (struct mcheck_hdr *)mem; + + if (hdr->caller[0]) + printf("%12lx %10zx %s\n", + (ulong)mem, sz, hdr->caller); + else + printf("%12lx %10zx\n", + (ulong)mem, sz); +#else + printf("%12lx %10zx\n", + (ulong)mem, sz); +#endif + used += sz; + used_count++; + } else { + printf("%12lx %10zx \n", + (ulong)mem, sz); + free_space += sz; + free_count++; + } + q = next_chunk(q); + } + s = s->next; + } + + /* Print top chunk (wilderness) */ + if (gm->top && gm->topsize > 0) { + printf("%12lx %10zx top\n", + (ulong)chunk2mem(gm->top), gm->topsize); + free_space += gm->topsize; + } + + printf("%12lx %10s end\n", mem_malloc_end, ""); + printf("----------------------------------\n"); + printf("Used: %zx bytes in %d chunks\n", used, used_count); + printf("Free: %zx bytes in %d chunks + top\n", free_space, free_count); +} + int initf_malloc(void) { #if CONFIG_IS_ENABLED(SYS_MALLOC_F) diff --git a/doc/usage/cmd/malloc.rst b/doc/usage/cmd/malloc.rst index 7b08b358258..fac9bb29aac 100644 --- a/doc/usage/cmd/malloc.rst +++ b/doc/usage/cmd/malloc.rst @@ -12,6 +12,7 @@ Synopsis :: malloc info + malloc dump Description ----------- @@ -23,6 +24,13 @@ info amount currently in use, and call counts for malloc(), free(), and realloc(). +dump + Walks the heap and prints each chunk's address, size (in hex), and status. + In-use chunks show no status label, while free chunks show ````. + Special entries show ``(chunk header)``, ``top``, or ``end``. This is useful + for debugging memory allocation issues. When CONFIG_MCHECK_HEAP_PROTECTION + is enabled, the caller string is also shown if available. + The total heap size is set by ``CONFIG_SYS_MALLOC_LEN``. Example @@ -37,6 +45,20 @@ Example free count = 567 realloc count = 89 + => malloc dump + Heap dump: 19a0e000 - 1fa10000 + Address Size Status + ---------------------------------- + 19a0e000 10 (chunk header) + 19a0e010 a0 + 19a0e0b0 6070 + 19adfc30 60 + 19adff90 5f3f030 top + 1fa10000 end + ---------------------------------- + Used: c2ef0 bytes in 931 chunks + Free: 5f3f0c0 bytes in 2 chunks + top + Configuration ------------- diff --git a/include/malloc.h b/include/malloc.h index 0d8a2a43f2a..a4d588936ec 100644 --- a/include/malloc.h +++ b/include/malloc.h @@ -704,6 +704,14 @@ void malloc_enable_testing(int max_allocs); */ void malloc_disable_testing(void); +/** + * malloc_dump() - Print a dump of all heap chunks + * + * Walks the dlmalloc heap from start to end, printing each chunk's + * address, size, and status (used/free/top). + */ +void malloc_dump(void); + /** * mem_malloc_init() - Initialize the malloc() heap * diff --git a/test/cmd/malloc.c b/test/cmd/malloc.c index f9d41a36cad..3c1a44bcacf 100644 --- a/test/cmd/malloc.c +++ b/test/cmd/malloc.c @@ -32,3 +32,23 @@ static int cmd_test_malloc_info(struct unit_test_state *uts) return 0; } CMD_TEST(cmd_test_malloc_info, UTF_CONSOLE); + +/* Test 'malloc dump' command */ +static int cmd_test_malloc_dump(struct unit_test_state *uts) +{ + /* this takes a long time to dump, with truetype enabled, so skip it */ + return -EAGAIN; + + ut_assertok(run_command("malloc dump", 0)); + ut_assert_nextlinen("Heap dump: "); + ut_assert_nextline("%12s %10s %s", "Address", "Size", "Status"); + ut_assert_nextline("----------------------------------"); + ut_assert_nextline("%12lx %10x (chunk header)", mem_malloc_start, 0x10); + ut_assert_skip_to_line("----------------------------------"); + ut_assert_nextlinen("Used: "); + ut_assert_nextlinen("Free: "); + ut_assert_console_end(); + + return 0; +} +CMD_TEST(cmd_test_malloc_dump, UTF_CONSOLE);