diff --git a/components/heap/CMakeLists.txt b/components/heap/CMakeLists.txt index 23b8563df27..1bd98a55073 100644 --- a/components/heap/CMakeLists.txt +++ b/components/heap/CMakeLists.txt @@ -92,6 +92,7 @@ if(CONFIG_HEAP_TRACING) set(WRAP_FUNCTIONS heap_caps_realloc_base heap_caps_malloc_base + heap_caps_calloc_base heap_caps_aligned_alloc_base heap_caps_free) diff --git a/components/heap/include/heap_trace.inc b/components/heap/include/heap_trace.inc index d409ee83299..b4dd1a5b2c7 100644 --- a/components/heap/include/heap_trace.inc +++ b/components/heap/include/heap_trace.inc @@ -112,6 +112,7 @@ typedef enum { } trace_malloc_mode_t; void *__real_heap_caps_malloc_base( size_t size, uint32_t caps); +void *__real_heap_caps_calloc_base(size_t n, size_t size, uint32_t caps); void *__real_heap_caps_realloc_base( void *ptr, size_t size, uint32_t caps); void *__real_heap_caps_aligned_alloc_base(size_t alignment, size_t size, uint32_t caps); void __real_heap_caps_free(void *p); @@ -139,6 +140,28 @@ static HEAP_IRAM_ATTR __attribute__((noinline)) void *trace_malloc(size_t alignm return p; } +/* trace any 'calloc' event */ +static HEAP_IRAM_ATTR __attribute__((noinline)) void *trace_calloc(size_t n, size_t size, uint32_t caps) +{ + uint32_t ccount = get_ccount(); + size_t size_bytes; + void *p = __real_heap_caps_calloc_base(n, size, caps); + + if (__builtin_mul_overflow(n, size, &size_bytes)) { + size_bytes = 0; + } + + heap_trace_record_t rec = { + .address = p, + .ccount = ccount, + .size = size_bytes, + .freed = false, + }; + get_call_stack(rec.alloced_by); + record_allocation(&rec); + return p; +} + /* trace any 'realloc' event */ static HEAP_IRAM_ATTR __attribute__((noinline)) void *trace_realloc(void *p, size_t size, uint32_t caps) { @@ -189,6 +212,11 @@ HEAP_IRAM_ATTR void *__wrap_heap_caps_malloc_base(size_t size, uint32_t caps) return trace_malloc(0, size, caps, TRACE_MALLOC_DEFAULT); } +HEAP_IRAM_ATTR void *__wrap_heap_caps_calloc_base(size_t n, size_t size, uint32_t caps) +{ + return trace_calloc(n, size, caps); +} + HEAP_IRAM_ATTR void *__wrap_heap_caps_aligned_alloc_base(size_t alignment, size_t size, uint32_t caps) { (void)alignment; diff --git a/components/heap/test_apps/heap_tests/main/test_heap_trace.c b/components/heap/test_apps/heap_tests/main/test_heap_trace.c index 9df51df2888..69b80a45a81 100644 --- a/components/heap/test_apps/heap_tests/main/test_heap_trace.c +++ b/components/heap/test_apps/heap_tests/main/test_heap_trace.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -71,6 +71,39 @@ TEST_CASE("heap trace leak check", "[heap-trace]") heap_trace_stop(); } +TEST_CASE("heap trace records heap_caps_calloc", "[heap-trace]") +{ + heap_trace_record_t recs[8]; + heap_trace_init_standalone(recs, 8); + + printf("calloc trace test\n"); // Print something before trace starts, or stdout allocations skew total counts + fflush(stdout); + + heap_trace_start(HEAP_TRACE_LEAKS); + + // heap_caps_calloc() reaches heap_caps_malloc_base() through a same-TU + // call inside heap_caps_base.c that the linker --wrap does not redirect. + // This allocation must still be recorded via the heap_caps_calloc_base wrap. + const size_t n = 4; + const size_t size = 16; + void *a = heap_caps_calloc(n, size, MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(a); + + heap_trace_dump(); + TEST_ASSERT_EQUAL(1, heap_trace_get_count()); + + heap_trace_record_t trace_a; + heap_trace_get(0, &trace_a); + TEST_ASSERT_EQUAL_PTR(a, trace_a.address); + TEST_ASSERT_EQUAL(n * size, trace_a.size); + + free(a); + + TEST_ASSERT_EQUAL(0, heap_trace_get_count()); + + heap_trace_stop(); +} + TEST_CASE("heap trace wrapped buffer check", "[heap-trace]") { const size_t N = 8;