cmd: malloc: Add a command to show the malloc log

Add a command interface for the malloc-traffic log:
- malloc log start: Start recording allocations
- malloc log stop: Stop recording
- malloc log: Dump the recorded entries

Example output:

  => malloc log
  Malloc log: 29 entries (max 524288, total 29)
   Seq  Type                   Ptr      Size  Caller
  ----  --------  ----------------  --------  ------
     0  free              16a016e0         0  free_pipe_list:2001
                <-parse_stream_outer:3208 <-parse_file_outer:3300
     1  alloc             16a01b90        20  hush_file_init:3277
                <-parse_file_outer:3295 <-run_pipe_real:1986

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
Simon Glass
2026-01-02 10:29:22 -07:00
parent 1390d243bc
commit 7856369dc2
5 changed files with 128 additions and 4 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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
------------

View File

@@ -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 */