diff --git a/components/heap/Kconfig b/components/heap/Kconfig index f19574a691..5f10676bc9 100644 --- a/components/heap/Kconfig +++ b/components/heap/Kconfig @@ -56,6 +56,11 @@ menu "Heap memory debugging" More stack frames uses more memory in the heap trace buffer (and slows down allocation), but can provide useful information. + config HEAP_USE_HOOKS + bool "Use allocation and free hooks" + help + Enable the user to implement function hooks triggered for each successful allocation and free. + config HEAP_TASK_TRACKING bool "Enable heap task tracking" depends on !HEAP_POISONING_DISABLED diff --git a/components/heap/heap_caps.c b/components/heap/heap_caps.c index 595d684d6d..70848f7b0a 100644 --- a/components/heap/heap_caps.c +++ b/components/heap/heap_caps.c @@ -15,11 +15,15 @@ #include "heap_private.h" #include "esp_system.h" +#ifdef CONFIG_HEAP_USE_HOOKS #define CALL_HOOK(hook, ...) { \ if (hook != NULL) { \ hook(__VA_ARGS__); \ } \ } +#else +#define CALL_HOOK(hook, ...) {} +#endif /* Forward declaration for base function, put in IRAM. * These functions don't check for errors after trying to allocate memory. */ diff --git a/components/heap/include/esp_heap_caps.h b/components/heap/include/esp_heap_caps.h index 4a4b99e81e..f3d1026c8b 100644 --- a/components/heap/include/esp_heap_caps.h +++ b/components/heap/include/esp_heap_caps.h @@ -54,6 +54,7 @@ typedef void (*esp_alloc_failed_hook_t) (size_t size, uint32_t caps, const char */ esp_err_t heap_caps_register_failed_alloc_callback(esp_alloc_failed_hook_t callback); +#ifdef CONFIG_HEAP_USE_HOOKS /** * @brief callback called after every allocation * @param ptr the allocated memory @@ -71,6 +72,7 @@ __attribute__((weak)) IRAM_ATTR void esp_heap_trace_alloc_hook(void* ptr, size_t * You should refrain from doing heavy work, logging, flash writes, or any locking. */ __attribute__((weak)) IRAM_ATTR void esp_heap_trace_free_hook(void* ptr); +#endif /** * @brief Allocate a chunk of memory which has the given capabilities diff --git a/components/heap/test_apps/main/test_malloc.c b/components/heap/test_apps/main/test_malloc.c index a1c8994f1e..38b4ea18a0 100644 --- a/components/heap/test_apps/main/test_malloc.c +++ b/components/heap/test_apps/main/test_malloc.c @@ -182,6 +182,7 @@ TEST_CASE("test get allocated size", "[heap]") } } +#ifdef CONFIG_HEAP_USE_HOOKS // provide the definition of alloc and free hooks static const size_t alloc_size = 1234; // make this size atypical to be able to rely on it in the hook static const size_t expected_calls = 2; // one call for malloc/calloc and one call for realloc @@ -247,3 +248,4 @@ TEST_CASE("test allocation and free function hooks", "[heap]") TEST_ASSERT_TRUE(test_success); } +#endif diff --git a/components/heap/test_apps/pytest_heap.py b/components/heap/test_apps/pytest_heap.py index 32f48b0ac8..56a495021e 100644 --- a/components/heap/test_apps/pytest_heap.py +++ b/components/heap/test_apps/pytest_heap.py @@ -102,3 +102,17 @@ def test_memory_protection(dut: Dut) -> None: dut.expect_exact('Press ENTER to see the list of tests') dut.write('[heap][mem_prot]') dut.expect_unity_test_output(timeout=300) + + +@pytest.mark.generic +@pytest.mark.esp32 +@pytest.mark.parametrize( + 'config', + [ + 'func_hooks' + ] +) +def test_heap_func_hooks(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('"test allocation and free function hooks"') + dut.expect_unity_test_output(timeout=300) diff --git a/components/heap/test_apps/sdkconfig.ci.func_hooks b/components/heap/test_apps/sdkconfig.ci.func_hooks new file mode 100644 index 0000000000..9a440b80de --- /dev/null +++ b/components/heap/test_apps/sdkconfig.ci.func_hooks @@ -0,0 +1 @@ +CONFIG_HEAP_USE_HOOKS=y diff --git a/docs/en/api-reference/system/heap_debug.rst b/docs/en/api-reference/system/heap_debug.rst index 6cc9970b86..673872137c 100644 --- a/docs/en/api-reference/system/heap_debug.rst +++ b/docs/en/api-reference/system/heap_debug.rst @@ -24,6 +24,19 @@ To obtain information about the state of the heap: - :cpp:func:`heap_caps_dump` and :cpp:func:`heap_caps_dump_all` will output detailed information about the structure of each block in the heap. Note that this can be large amount of output. +.. _heap-allocation-free: + +Heap allocation and free function hooks +--------------------------------------- + +Heap allocation and free detection hooks allows you to be notified of every successful allocation and free operations: +- Providing a definition of :cpp:func:`esp_heap_trace_alloc_hook` will allow you to be notified of every successful memory allocation operations +- Providing a definition of :cpp:func:`esp_heap_trace_free_hook` will allow you to be notified of every memory free operations + +To activate the feature, navigate to ``Component config`` -> ``Heap Memory Debugging`` in the configuration menu and select ``Use allocation and free hooks`` option (see :ref:`CONFIG_HEAP_USE_HOOKS`). +:cpp:func:`esp_heap_trace_alloc_hook` and :cpp:func:`esp_heap_trace_free_hook` have weak declarations, it is not necessary to provide a declarations for both hooks. +Since allocating and freeing memory is allowed even though strongly recommended against, :cpp:func:`esp_heap_trace_alloc_hook` and :cpp:func:`esp_heap_trace_free_hook` can potentially be called from ISR. + .. _heap-corruption: Heap Corruption Detection diff --git a/docs/en/api-reference/system/mem_alloc.rst b/docs/en/api-reference/system/mem_alloc.rst index 8796961a77..6cb9f07fba 100644 --- a/docs/en/api-reference/system/mem_alloc.rst +++ b/docs/en/api-reference/system/mem_alloc.rst @@ -158,6 +158,7 @@ Heap Tracing & Debugging The following features are documented on the :doc:`Heap Memory Debugging ` page: - :ref:`Heap Information ` (free space, etc.) +- :ref:`Heap allocation and free function hooks ` - :ref:`Heap Corruption Detection ` - :ref:`Heap Tracing ` (memory leak detection, monitoring, etc.)