diff --git a/cmd/Kconfig b/cmd/Kconfig index d602f430ab6..072ff879cd8 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -3049,6 +3049,16 @@ config CMD_MALLOC about memory allocation, such as total memory allocated and currently in use. +config CMD_MALLOC_LOG + bool "malloc log - Log malloc traffic" + depends on CMD_MALLOC && MCHECK_LOG + default y + help + This adds the 'malloc log' subcommand which records all + malloc/free/realloc calls with their addresses, sizes, and caller + information. Use 'malloc log start' to begin recording and + 'malloc log' to display the recorded entries. + config CMD_MOUSE bool "mouse - Show mouse input" default y if MOUSE diff --git a/cmd/malloc.c b/cmd/malloc.c index 9c7dfbfc0c3..361750c45dd 100644 --- a/cmd/malloc.c +++ b/cmd/malloc.c @@ -38,10 +38,47 @@ static int do_malloc_dump(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int __maybe_unused do_malloc_log(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + if (argc < 2) { + malloc_log_dump(); + return 0; + } + + if (!strcmp(argv[1], "start")) { + malloc_log_start(); + printf("Malloc logging started\n"); + } else if (!strcmp(argv[1], "stop")) { + malloc_log_stop(); + printf("Malloc logging stopped\n"); + } else if (!strcmp(argv[1], "dump")) { + malloc_log_dump(); + } else { + return CMD_RET_USAGE; + } + + return 0; +} + +#if CONFIG_IS_ENABLED(CMD_MALLOC_LOG) +#define MALLOC_LOG_HELP \ + "malloc log [start|stop|dump] - log malloc traffic\n" \ + " start - start recording malloc/free calls\n" \ + " stop - stop recording\n" \ + " dump - print the log (or just 'malloc log')\n" +#define MALLOC_LOG_SUBCMD , U_BOOT_SUBCMD_MKENT(log, 3, 1, do_malloc_log) +#else +#define MALLOC_LOG_HELP +#define MALLOC_LOG_SUBCMD +#endif + U_BOOT_LONGHELP(malloc, "info - display malloc statistics\n" - "malloc dump - dump heap chunks (address, size, status)\n"); + "malloc dump - dump heap chunks (address, size, status)\n" + MALLOC_LOG_HELP); 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(dump, 1, 1, do_malloc_dump)); + U_BOOT_SUBCMD_MKENT(dump, 1, 1, do_malloc_dump) + MALLOC_LOG_SUBCMD); diff --git a/doc/develop/malloc.rst b/doc/develop/malloc.rst index 92180af055a..b9ab884d419 100644 --- a/doc/develop/malloc.rst +++ b/doc/develop/malloc.rst @@ -390,7 +390,8 @@ Malloc-Traffic Log On sandbox, when mcheck is enabled, a malloc-traffic log can record all malloc/free/realloc calls. This is useful for debugging allocation patterns -and finding where allocations without caller info originate. +and finding where allocations without caller info originate. See +:doc:`../usage/cmd/malloc` for usage. The log buffer is allocated from host memory using ``os_malloc()``, so it does not affect U-Boot's heap. The size is controlled by diff --git a/doc/usage/cmd/malloc.rst b/doc/usage/cmd/malloc.rst index 3693034b41e..03f0669658b 100644 --- a/doc/usage/cmd/malloc.rst +++ b/doc/usage/cmd/malloc.rst @@ -13,6 +13,7 @@ Synopsis malloc info malloc dump + malloc log [start|stop] Description ----------- @@ -31,6 +32,13 @@ dump for debugging memory allocation issues. When CONFIG_MCHECK_HEAP_PROTECTION is enabled, the caller string is also shown if available. +log + Controls the malloc traffic log. With no argument, dumps the recorded log + entries. Use ``start`` to begin recording malloc/free/realloc calls, and + ``stop`` to stop recording. Each entry shows the operation type, pointer + address, size, and caller backtrace. This is useful for tracking down + memory leaks or understanding allocation patterns. + The total heap size is set by ``CONFIG_SYS_MALLOC_LEN``. Example @@ -71,11 +79,31 @@ With CONFIG_MCHECK_HEAP_PROTECTION enabled, the caller backtrace is shown:: 18a3b840 90 used of_alias_scan:911 <-board_init_ ... +With CONFIG_CMD_MALLOC_LOG enabled, the log subcommand is available:: + + => malloc log start + Malloc logging started + => ... do some operations ... + => malloc log stop + Malloc logging stopped + => malloc log + Malloc log: 5 entries (max 524288, total 5) + Seq Type Ptr Size Caller + ---- -------- ---------------- -------- ------ + 0 alloc 16a01b90 20 hush_file_init:3277 + <-parse_file_outer:3295 <-run_pipe_real:1986 + 1 alloc 16a01bc0 100 xmalloc:107 <-xzalloc:117 + <-new_pipe:1498 <-run_list_real:1702 + 2 free 16a01bc0 0 free_pipe_list:2001 + <-parse_stream_outer:3208 <-parse_file_outer:3300 + ... + Configuration ------------- The malloc command is enabled by CONFIG_CMD_MALLOC which depends on -CONFIG_MALLOC_DEBUG. +CONFIG_MALLOC_DEBUG. The log subcommand is enabled by CONFIG_CMD_MALLOC_LOG +which additionally requires CONFIG_MCHECK_LOG. Return value ------------ diff --git a/test/cmd/malloc.c b/test/cmd/malloc.c index 3c1a44bcacf..75e8afdec63 100644 --- a/test/cmd/malloc.c +++ b/test/cmd/malloc.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -52,3 +53,50 @@ static int cmd_test_malloc_dump(struct unit_test_state *uts) return 0; } CMD_TEST(cmd_test_malloc_dump, UTF_CONSOLE); + +#if CONFIG_IS_ENABLED(MCHECK_LOG) +/* Test 'malloc log' command */ +static int cmd_test_malloc_log(struct unit_test_state *uts) +{ + struct mlog_info info; + void *ptr, *ptr2; + int seq; + + ut_assertok(run_command("malloc log start", 0)); + ut_assert_nextline("Malloc logging started"); + ut_assert_console_end(); + + /* Get current log position so we know our sequence numbers */ + ut_assertok(malloc_log_info(&info)); + seq = info.total_count; + + /* Do allocations with distinctive sizes we can search for */ + ptr = malloc(12345); + ut_assertnonnull(ptr); + ptr2 = realloc(ptr, 23456); + ut_assertnonnull(ptr2); + free(ptr2); + + ut_assertok(run_command("malloc log stop", 0)); + ut_assert_nextline("Malloc logging stopped"); + ut_assert_console_end(); + + /* Dump the log and find our allocations by sequence number and size */ + ut_assertok(run_command("malloc log", 0)); + ut_assert_nextlinen("Malloc log: "); + ut_assert_nextline("%4s %-8s %10s %8s %s", + "Seq", "Type", "Address", "Size", "Caller"); + ut_assert_nextline("---- -------- ---------- -------- ------"); + /* 12345 = 0x3039, 23456 = 0x5ba0 */ + ut_assert_skip_to_linen("%4d alloc %10lx 3039", seq, + (ulong)map_to_sysmem(ptr)); + ut_assert_skip_to_linen("%4d realloc %10lx 5ba0", seq + 1, + (ulong)map_to_sysmem(ptr2)); + ut_assert_skip_to_linen("%4d free %10lx 0", seq + 2, + (ulong)map_to_sysmem(ptr2)); + console_record_reset_enable(); /* discard remaining output */ + + return 0; +} +CMD_TEST(cmd_test_malloc_log, UTF_CONSOLE); +#endif /* MCHECK_LOG */