[Concept,15/24] malloc: Add function to dump the malloc()-traffic log

Message ID 20260103011908.149445-16-sjg@u-boot.org
State New
Headers
Series Malloc debugging and test/py improvements |

Commit Message

Simon Glass Jan. 3, 2026, 1:18 a.m. UTC
  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(+)
  

Patch

diff --git a/common/dlmalloc.c b/common/dlmalloc.c
index bf60e3004d4..6f10a980d6e 100644
--- a/common/dlmalloc.c
+++ b/common/dlmalloc.c
@@ -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;
diff --git a/include/malloc.h b/include/malloc.h
index f8bfe843738..4bd6458b70d 100644
--- a/include/malloc.h
+++ b/include/malloc.h
@@ -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
  */
diff --git a/test/common/malloc.c b/test/common/malloc.c
index 4e5bd68c013..d29e9364b01 100644
--- a/test/common/malloc.c
+++ b/test/common/malloc.c
@@ -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 */