@@ -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
@@ -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);
@@ -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
@@ -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
------------
@@ -7,6 +7,7 @@
*/
#include <malloc.h>
+#include <mapmem.h>
#include <dm/test.h>
#include <test/cmd.h>
#include <test/ut.h>
@@ -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 */