malloc: Add function to dump the malloc()-traffic log

Add malloc_log_dump() to print all recorded malloc/free/realloc calls
with their addresses, sizes, and caller information. This provides a
way to inspect the log after recording.

The dump shows a summary line with entry counts, followed by a table
with sequence number, operation type, pointer, size, and caller for
each recorded operation.

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 11:07:06 -07:00
parent 1d0df266dc
commit 1390d243bc
3 changed files with 119 additions and 0 deletions

View File

@@ -6070,6 +6070,72 @@ void malloc_log_stop(void)
mlog.enabled = false;
}
/* Output function type for malloc_log_impl */
typedef void (*log_out_fn)(void *ctx, const char *fmt, ...)
__printf(2, 3);
/* Console output function for log */
static void log_to_console(void *ctx, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
static void malloc_log_impl(log_out_fn out, void *ctx)
{
static const char * const mlog_type_names[] = {
"alloc", "free", "realloc", "memalign"
};
uint i, start, count;
if (!mlog.buf) {
out(ctx, "Malloc log not started\n");
return;
}
if (mlog.enabled) {
out(ctx, "Warning: log still active, results may be incomplete\n");
malloc_log_stop();
}
if (mlog.count > mlog.size) {
out(ctx, "Warning: log wrapped, %u entries lost\n",
mlog.count - mlog.size);
count = mlog.size;
start = mlog.head; /* oldest entry is at current head */
} else {
count = mlog.count;
start = 0;
}
out(ctx, "Malloc log: %u entries (max %u, total %u)\n", count, mlog.size,
mlog.count);
out(ctx, "%4s %-8s %10s %8s %s\n", "Seq", "Type", "Address", "Size",
"Caller");
out(ctx, "---- -------- ---------- -------- ------\n");
for (i = 0; i < count; i++) {
uint idx = (start + i) % mlog.size;
struct mlog_entry *ent = &mlog.buf[idx];
out(ctx, "%4u %-8s %10lx %8lx", i, mlog_type_names[ent->type],
(ulong)map_to_sysmem(ent->ptr), (ulong)ent->size);
if (ent->type == MLOG_REALLOC)
out(ctx, " (was %lx)", (ulong)ent->old_size);
if (ent->caller[0])
out(ctx, " %s", ent->caller);
out(ctx, "\n");
}
}
void malloc_log_dump(void)
{
malloc_log_impl(log_to_console, NULL);
}
int malloc_log_info(struct mlog_info *info)
{
if (!mlog.buf)
@@ -6123,6 +6189,10 @@ void malloc_log_stop(void)
{
}
void malloc_log_dump(void)
{
}
int malloc_log_info(struct mlog_info *info)
{
return -ENOENT;

View File

@@ -725,6 +725,14 @@ void malloc_log_start(void);
*/
void malloc_log_stop(void);
/**
* malloc_log_dump() - Dump the malloc traffic log
*
* Prints all recorded malloc/free/realloc calls with their addresses,
* sizes, and caller information.
*/
void malloc_log_dump(void);
/**
* enum mlog_type - Type of malloc log entry
*/

View File

@@ -688,4 +688,45 @@ static int common_test_malloc_log_info(struct unit_test_state *uts)
return 0;
}
COMMON_TEST(common_test_malloc_log_info, 0);
/* Test malloc_log_dump() */
static int common_test_malloc_log_dump(struct unit_test_state *uts)
{
struct mlog_info info;
void *ptr, *ptr2;
malloc_log_start();
/* Do an allocation, realloc, and free */
ptr = malloc(0x100);
ut_assertnonnull(ptr);
ptr2 = realloc(ptr, 0x200);
ut_assertnonnull(ptr2);
free(ptr2);
malloc_log_stop();
ut_assertok(malloc_log_info(&info));
console_record_reset_enable();
malloc_log_dump();
ut_assert_nextline("Malloc log: 3 entries (max %u, total 3)",
info.max_entries);
ut_assert_nextline("%4s %-8s %10s %8s %s", "Seq", "Type", "Address",
"Size", "Caller");
ut_assert_nextline("---- -------- ---------- -------- ------");
ut_assert_nextlinen(" 0 alloc %10lx 100",
(ulong)map_to_sysmem(ptr));
ut_assert_nextlinen(" 1 realloc %10lx 200 (was 100)",
(ulong)map_to_sysmem(ptr2));
ut_assert_nextlinen(" 2 free %10lx 0",
(ulong)map_to_sysmem(ptr2));
ut_assert_console_end();
return 0;
}
COMMON_TEST(common_test_malloc_log_dump, 0);
#endif /* MCHECK_LOG */