From: Simon Glass <simon.glass@canonical.com>
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>
---
common/dlmalloc.c | 70 ++++++++++++++++++++++++++++++++++++++++++++
include/malloc.h | 8 +++++
test/common/malloc.c | 41 ++++++++++++++++++++++++++
3 files changed, 119 insertions(+)
@@ -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, ...)
+ __attribute__((format(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;
@@ -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
*/
@@ -692,4 +692,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 */