@@ -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));
@@ -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
@@ -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 <free>\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)
@@ -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 ``<free>``.
+ 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 <free>
+ 19adff90 5f3f030 top
+ 1fa10000 end
+ ----------------------------------
+ Used: c2ef0 bytes in 931 chunks
+ Free: 5f3f0c0 bytes in 2 chunks + top
+
Configuration
-------------
@@ -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
*
@@ -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);