[Concept,19/35] malloc: Add call counters for malloc, free, realloc

Message ID 20251210000737.180797-20-sjg@u-boot.org
State New
Headers
Series malloc: Add heap debugging commands and mcheck caller tracking |

Commit Message

Simon Glass Dec. 10, 2025, 12:07 a.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Add counters to track the number of calls to malloc(), free(), and
realloc(). These are displayed by the 'malloc info' command and
accessible via malloc_get_info().

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 cmd/malloc.c             |  7 +++++--
 common/dlmalloc.c        | 18 ++++++++++++++++++
 doc/usage/cmd/malloc.rst | 12 ++++++++----
 include/malloc.h         |  6 ++++++
 test/cmd/malloc.c        |  4 ++++
 5 files changed, 41 insertions(+), 6 deletions(-)
  

Patch

diff --git a/cmd/malloc.c b/cmd/malloc.c
index cb2fa34155b..3750a16158b 100644
--- a/cmd/malloc.c
+++ b/cmd/malloc.c
@@ -21,8 +21,11 @@  static int do_malloc_info(struct cmd_tbl *cmdtp, int flag, int argc,
 	if (ret)
 		return CMD_RET_FAILURE;
 
-	printf("total bytes  = %s\n", format_size(buf, info.total_bytes));
-	printf("in use bytes = %s\n", format_size(buf, info.in_use_bytes));
+	printf("total bytes   = %s\n", format_size(buf, info.total_bytes));
+	printf("in use bytes  = %s\n", format_size(buf, info.in_use_bytes));
+	printf("malloc count  = %lu\n", info.malloc_count);
+	printf("free count    = %lu\n", info.free_count);
+	printf("realloc count = %lu\n", info.realloc_count);
 
 	return 0;
 }
diff --git a/common/dlmalloc.c b/common/dlmalloc.c
index b0845e6ea7c..b8de42cc47e 100644
--- a/common/dlmalloc.c
+++ b/common/dlmalloc.c
@@ -646,6 +646,12 @@  ulong mem_malloc_start;
 ulong mem_malloc_end;
 ulong mem_malloc_brk;
 
+#ifndef NO_MALLOC_STATS
+static ulong malloc_count;
+static ulong free_count;
+static ulong realloc_count;
+#endif /* !NO_MALLOC_STATS */
+
 #endif /* __UBOOT__ */
 
 #ifndef SMALLCHUNKS_AS_FUNCS
@@ -3699,6 +3705,9 @@  int malloc_get_info(struct malloc_info *info)
 
   info->total_bytes = mem_malloc_end - mem_malloc_start;
   info->in_use_bytes = mi.uordblks;
+  info->malloc_count = malloc_count;
+  info->free_count = free_count;
+  info->realloc_count = realloc_count;
 
   return 0;
 }
@@ -4926,6 +4935,9 @@  void* dlmalloc_impl(size_t bytes) {
   if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT))
     return malloc_simple(bytes);
 #endif
+#if !NO_MALLOC_STATS
+  malloc_count++;
+#endif
 
   /* Return NULL if not initialized yet */
   if (!mem_malloc_start && !mem_malloc_end)
@@ -5092,6 +5104,9 @@  void* dlmalloc_impl(size_t bytes) {
 STATIC_IF_MCHECK
 void dlfree_impl(void* mem) {
 #ifdef __UBOOT__
+#if !NO_MALLOC_STATS
+  free_count++;
+#endif
 #if CONFIG_IS_ENABLED(SYS_MALLOC_F)
   /* free() is a no-op - all the memory will be freed on relocation */
   if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT)) {
@@ -5662,6 +5677,9 @@  static void internal_inspect_all(mstate m,
 STATIC_IF_MCHECK
 void* dlrealloc_impl(void* oldmem, size_t bytes) {
 #ifdef __UBOOT__
+#if !NO_MALLOC_STATS
+  realloc_count++;
+#endif
 #if CONFIG_IS_ENABLED(SYS_MALLOC_F)
   if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT)) {
     /* This is harder to support and should not be needed */
diff --git a/doc/usage/cmd/malloc.rst b/doc/usage/cmd/malloc.rst
index d21aa6c1421..7b08b358258 100644
--- a/doc/usage/cmd/malloc.rst
+++ b/doc/usage/cmd/malloc.rst
@@ -19,8 +19,9 @@  Description
 The malloc command shows information about the malloc heap.
 
 info
-    Shows memory-allocation statistics, including the total heap size and the
-    amount currently in use.
+    Shows memory-allocation statistics, including the total heap size, the
+    amount currently in use, and call counts for malloc(), free(), and
+    realloc().
 
 The total heap size is set by ``CONFIG_SYS_MALLOC_LEN``.
 
@@ -30,8 +31,11 @@  Example
 ::
 
     => malloc info
-    total bytes  = 96 MiB
-    in use bytes = 700.9 KiB
+    total bytes   = 96 MiB
+    in use bytes  = 700.9 KiB
+    malloc count  = 1234
+    free count    = 567
+    realloc count = 89
 
 Configuration
 -------------
diff --git a/include/malloc.h b/include/malloc.h
index 7e276d444ab..0d8a2a43f2a 100644
--- a/include/malloc.h
+++ b/include/malloc.h
@@ -675,10 +675,16 @@  void mspace_inspect_all(mspace msp,
  *
  * @total_bytes: Total bytes available in the heap
  * @in_use_bytes: Current bytes allocated (in use by application)
+ * @malloc_count: Number of calls to malloc()
+ * @free_count: Number of calls to free()
+ * @realloc_count: Number of calls to realloc()
  */
 struct malloc_info {
 	ulong total_bytes;
 	ulong in_use_bytes;
+	ulong malloc_count;
+	ulong free_count;
+	ulong realloc_count;
 };
 
 /* Memory pool boundaries */
diff --git a/test/cmd/malloc.c b/test/cmd/malloc.c
index 6cd52b68900..f9d41a36cad 100644
--- a/test/cmd/malloc.c
+++ b/test/cmd/malloc.c
@@ -19,10 +19,14 @@  static int cmd_test_malloc_info(struct unit_test_state *uts)
 	ut_assertok(malloc_get_info(&info));
 	ut_assert(info.total_bytes >= CONFIG_SYS_MALLOC_LEN);
 	ut_assert(info.in_use_bytes < info.total_bytes);
+	ut_assert(info.malloc_count > 0);
 
 	ut_assertok(run_command("malloc info", 0));
 	ut_assert_nextlinen("total bytes   = ");
 	ut_assert_nextlinen("in use bytes  = ");
+	ut_assert_nextlinen("malloc count  = ");
+	ut_assert_nextlinen("free count    = ");
+	ut_assert_nextlinen("realloc count = ");
 	ut_assert_console_end();
 
 	return 0;