From 335576013e9d39b07aec7150886deee7b1040118 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Wed, 12 Dec 2018 20:29:47 +0300 Subject: [PATCH] heap: Separate standalone and common part of tracing module --- components/heap/CMakeLists.txt | 5 +- components/heap/Kconfig | 25 ++- components/heap/component.mk | 8 +- .../{heap_trace.c => heap_trace_standalone.c} | 210 ++---------------- components/heap/include/esp_heap_trace.h | 12 +- components/heap/include/heap_trace.inc | 189 ++++++++++++++++ 6 files changed, 245 insertions(+), 204 deletions(-) rename components/heap/{heap_trace.c => heap_trace_standalone.c} (56%) create mode 100644 components/heap/include/heap_trace.inc diff --git a/components/heap/CMakeLists.txt b/components/heap/CMakeLists.txt index 442a9676f..fbbd10eea 100644 --- a/components/heap/CMakeLists.txt +++ b/components/heap/CMakeLists.txt @@ -1,6 +1,5 @@ set(COMPONENT_SRCS "heap_caps.c" "heap_caps_init.c" - "heap_trace.c" "multi_heap.c") if(NOT CONFIG_HEAP_POISONING_DISABLED) @@ -11,6 +10,10 @@ if(CONFIG_HEAP_TASK_TRACKING) list(APPEND COMPONENT_SRCS "heap_task_info.c") endif() +if(CONFIG_HEAP_TRACING_STANDALONE) + list(APPEND COMPONENT_SRCS "heap_trace_standalone.c") +endif() + set(COMPONENT_ADD_INCLUDEDIRS "include") set(COMPONENT_ADD_LDFRAGMENTS linker.lf) set(COMPONENT_REQUIRES "") diff --git a/components/heap/Kconfig b/components/heap/Kconfig index b2e00ab3a..c0e226bdd 100644 --- a/components/heap/Kconfig +++ b/components/heap/Kconfig @@ -17,14 +17,31 @@ menu "Heap memory debugging" bool "Comprehensive" endchoice - config HEAP_TRACING - bool "Enable heap tracing" + choice HEAP_TRACING_DEST + bool "Heap tracing" + default HEAP_TRACING_OFF help Enables the heap tracing API defined in esp_heap_trace.h. This function causes a moderate increase in IRAM code side and a minor increase in heap function - (malloc/free/realloc) CPU overhead, even when the tracing feature is not used. So it's best to keep it - disabled unless tracing is being used. + (malloc/free/realloc) CPU overhead, even when the tracing feature is not used. + So it's best to keep it disabled unless tracing is being used. + + config HEAP_TRACING_OFF + bool "Disabled" + config HEAP_TRACING_STANDALONE + bool "Standalone" + select HEAP_TRACING + config HEAP_TRACING_TOHOST + bool "Host-based" + select HEAP_TRACING + endchoice + + config HEAP_TRACING + bool + default F + help + Enables/disables heap tracing API. config HEAP_TRACING_STACK_DEPTH int "Heap tracing stack depth" diff --git a/components/heap/component.mk b/components/heap/component.mk index 7d8ef920a..266fbada0 100644 --- a/components/heap/component.mk +++ b/components/heap/component.mk @@ -2,7 +2,7 @@ # Component Makefile # -COMPONENT_OBJS := heap_caps_init.o heap_caps.o multi_heap.o heap_trace.o +COMPONENT_OBJS := heap_caps_init.o heap_caps.o multi_heap.o ifndef CONFIG_HEAP_POISONING_DISABLED COMPONENT_OBJS += multi_heap_poisoning.o @@ -12,6 +12,12 @@ COMPONENT_OBJS += heap_task_info.o endif endif +ifdef CONFIG_HEAP_TRACING_STANDALONE + +COMPONENT_OBJS += heap_trace_standalone.o + +endif + ifdef CONFIG_HEAP_TRACING WRAP_FUNCTIONS = calloc malloc free realloc heap_caps_malloc heap_caps_free heap_caps_realloc heap_caps_malloc_default heap_caps_realloc_default diff --git a/components/heap/heap_trace.c b/components/heap/heap_trace_standalone.c similarity index 56% rename from components/heap/heap_trace.c rename to components/heap/heap_trace_standalone.c index 58babbc19..138a8cd35 100644 --- a/components/heap/heap_trace.c +++ b/components/heap/heap_trace_standalone.c @@ -12,23 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. #include -#include #include #define HEAP_TRACE_SRCFILE /* don't warn on inclusion here */ #include "esp_heap_trace.h" #undef HEAP_TRACE_SRCFILE -#include "esp_heap_caps.h" #include "esp_attr.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "soc/soc_memory_layout.h" -#include "heap_private.h" #define STACK_DEPTH CONFIG_HEAP_TRACING_STACK_DEPTH +#if CONFIG_HEAP_TRACING_STANDALONE + static portMUX_TYPE trace_mux = portMUX_INITIALIZER_UNLOCKED; static bool tracing; static heap_trace_mode_t mode; @@ -55,10 +53,6 @@ static bool has_overflowed = false; esp_err_t heap_trace_init_standalone(heap_trace_record_t *record_buffer, size_t num_records) { -#ifndef CONFIG_HEAP_TRACING - return ESP_ERR_NOT_SUPPORTED; -#endif - if (tracing) { return ESP_ERR_INVALID_STATE; } @@ -70,13 +64,10 @@ esp_err_t heap_trace_init_standalone(heap_trace_record_t *record_buffer, size_t esp_err_t heap_trace_start(heap_trace_mode_t mode_param) { -#ifndef CONFIG_HEAP_TRACING - return ESP_ERR_NOT_SUPPORTED; -#endif - if (buffer == NULL || total_records == 0) { return ESP_ERR_INVALID_STATE; } + portENTER_CRITICAL(&trace_mux); tracing = false; @@ -93,9 +84,6 @@ esp_err_t heap_trace_start(heap_trace_mode_t mode_param) static esp_err_t set_tracing(bool enable) { -#ifndef CONFIG_HEAP_TRACING - return ESP_ERR_NOT_SUPPORTED; -#endif if (tracing == enable) { return ESP_ERR_INVALID_STATE; } @@ -120,9 +108,6 @@ size_t heap_trace_get_count(void) esp_err_t heap_trace_get(size_t index, heap_trace_record_t *record) { -#ifndef CONFIG_HEAP_TRACING - return ESP_ERR_NOT_SUPPORTED; -#endif if (record == NULL) { return ESP_ERR_INVALID_STATE; } @@ -141,10 +126,6 @@ esp_err_t heap_trace_get(size_t index, heap_trace_record_t *record) void heap_trace_dump(void) { -#ifndef CONFIG_HEAP_TRACING - printf("no data, heap tracing is disabled.\n"); - return; -#endif size_t delta_size = 0; size_t delta_allocs = 0; printf("%u allocations trace (%u entry buffer)\n", @@ -192,6 +173,10 @@ void heap_trace_dump(void) /* Add a new allocation to the heap trace records */ static IRAM_ATTR void record_allocation(const heap_trace_record_t *record) { + if (!tracing || record->address == NULL) { + return; + } + portENTER_CRITICAL(&trace_mux); if (tracing) { if (count == total_records) { @@ -224,6 +209,10 @@ static void remove_record(int index); */ static IRAM_ATTR void record_free(void *p, void **callers) { + if (!tracing || p == NULL) { + return; + } + portENTER_CRITICAL(&trace_mux); if (tracing && count > 0) { total_frees++; @@ -261,179 +250,6 @@ static IRAM_ATTR void remove_record(int index) count--; } -/* Encode the CPU ID in the LSB of the ccount value */ -inline static uint32_t get_ccount(void) -{ - uint32_t ccount = xthal_get_ccount() & ~3; -#ifndef CONFIG_FREERTOS_UNICORE - ccount |= xPortGetCoreID(); -#endif - return ccount; -} +#include "heap_trace.inc" -// Caller is 2 stack frames deeper than we care about -#define STACK_OFFSET 2 - -#define TEST_STACK(N) do { \ - if (STACK_DEPTH == N) { \ - return; \ - } \ - callers[N] = __builtin_return_address(N+STACK_OFFSET); \ - if (!esp_ptr_executable(callers[N])) { \ - return; \ - } \ - } while(0) - -/* Static function to read the call stack for a traced heap call. - - Calls to __builtin_return_address are "unrolled" via TEST_STACK macro as gcc requires the - argument to be a compile-time constant. -*/ -static IRAM_ATTR __attribute__((noinline)) void get_call_stack(void **callers) -{ - bzero(callers, sizeof(void *) * STACK_DEPTH); - TEST_STACK(0); - TEST_STACK(1); - TEST_STACK(2); - TEST_STACK(3); - TEST_STACK(4); - TEST_STACK(5); - TEST_STACK(6); - TEST_STACK(7); - TEST_STACK(8); - TEST_STACK(9); -} - -_Static_assert(STACK_DEPTH >= 0 && STACK_DEPTH <= 10, "CONFIG_HEAP_TRACING_STACK_DEPTH must be in range 0-10"); - - -typedef enum { - TRACE_MALLOC_CAPS, - TRACE_MALLOC_DEFAULT -} trace_malloc_mode_t; - - -void *__real_heap_caps_malloc(size_t size, uint32_t caps); -void *__real_heap_caps_malloc_default( size_t size ); -void *__real_heap_caps_realloc_default( void *ptr, size_t size ); - -/* trace any 'malloc' event */ -static IRAM_ATTR __attribute__((noinline)) void *trace_malloc(size_t size, uint32_t caps, trace_malloc_mode_t mode) -{ - uint32_t ccount = get_ccount(); - void *p; - if ( mode == TRACE_MALLOC_CAPS ) { - p = __real_heap_caps_malloc(size, caps); - } else { //TRACE_MALLOC_DEFAULT - p = __real_heap_caps_malloc_default(size); - } - - if (tracing && p != NULL) { - heap_trace_record_t rec = { - .address = p, - .ccount = ccount, - .size = size, - }; - get_call_stack(rec.alloced_by); - record_allocation(&rec); - } - return p; -} - -void __real_heap_caps_free(void *p); - -/* trace any 'free' event */ -static IRAM_ATTR __attribute__((noinline)) void trace_free(void *p) -{ - if (tracing && p != NULL) { - void *callers[STACK_DEPTH]; - get_call_stack(callers); - record_free(p, callers); - } - __real_heap_caps_free(p); -} - -void * __real_heap_caps_realloc(void *p, size_t size, uint32_t caps); - -/* trace any 'realloc' event */ -static IRAM_ATTR __attribute__((noinline)) void *trace_realloc(void *p, size_t size, uint32_t caps, trace_malloc_mode_t mode) -{ - void *callers[STACK_DEPTH]; - uint32_t ccount = get_ccount(); - if (tracing && p != NULL && size == 0) { - get_call_stack(callers); - record_free(p, callers); - } - void *r; - if (mode == TRACE_MALLOC_CAPS ) { - r = __real_heap_caps_realloc(p, size, caps); - } else { //TRACE_MALLOC_DEFAULT - r = __real_heap_caps_realloc_default(p, size); - } - if (tracing && r != NULL) { - get_call_stack(callers); - if (p != NULL) { - /* trace realloc as free-then-alloc */ - record_free(p, callers); - } - heap_trace_record_t rec = { - .address = r, - .ccount = ccount, - .size = size, - }; - memcpy(rec.alloced_by, callers, sizeof(void *) * STACK_DEPTH); - record_allocation(&rec); - } - return r; -} - -/* Note: this changes the behaviour of libc malloc/realloc/free a bit, - as they no longer go via the libc functions in ROM. But more or less - the same in the end. */ - -IRAM_ATTR void *__wrap_malloc(size_t size) -{ - return trace_malloc(size, 0, TRACE_MALLOC_DEFAULT); -} - -IRAM_ATTR void __wrap_free(void *p) -{ - trace_free(p); -} - -IRAM_ATTR void *__wrap_realloc(void *p, size_t size) -{ - return trace_realloc(p, size, 0, TRACE_MALLOC_DEFAULT); -} - -IRAM_ATTR void *__wrap_calloc(size_t nmemb, size_t size) -{ - size = size * nmemb; - void *result = trace_malloc(size, 0, TRACE_MALLOC_DEFAULT); - if (result != NULL) { - memset(result, 0, size); - } - return result; -} - -IRAM_ATTR void *__wrap_heap_caps_malloc(size_t size, uint32_t caps) -{ - return trace_malloc(size, caps, TRACE_MALLOC_CAPS); -} - -void __wrap_heap_caps_free(void *p) __attribute__((alias("__wrap_free"))); - -IRAM_ATTR void *__wrap_heap_caps_realloc(void *p, size_t size, uint32_t caps) -{ - return trace_realloc(p, size, caps, TRACE_MALLOC_CAPS); -} - -IRAM_ATTR void *__wrap_heap_caps_malloc_default( size_t size ) -{ - return trace_malloc(size, 0, TRACE_MALLOC_DEFAULT); -} - -IRAM_ATTR void *__wrap_heap_caps_realloc_default( void *ptr, size_t size ) -{ - return trace_realloc(ptr, size, 0, TRACE_MALLOC_DEFAULT); -} +#endif /*CONFIG_HEAP_TRACING_STANDALONE*/ diff --git a/components/heap/include/esp_heap_trace.h b/components/heap/include/esp_heap_trace.h index 5573d5e5a..a71f96362 100644 --- a/components/heap/include/esp_heap_trace.h +++ b/components/heap/include/esp_heap_trace.h @@ -47,7 +47,6 @@ typedef struct { /** * @brief Initialise heap tracing in standalone mode. - * @note Standalone mode is the only mode currently supported. * * This function must be called before any other heap tracing functions. * @@ -63,6 +62,17 @@ typedef struct { */ esp_err_t heap_trace_init_standalone(heap_trace_record_t *record_buffer, size_t num_records); +/** + * @brief Initialise heap tracing in host-based mode. + * + * This function must be called before any other heap tracing functions. + * + * @return + * - ESP_ERR_INVALID_STATE Heap tracing is currently in progress. + * - ESP_OK Heap tracing initialised successfully. + */ +esp_err_t heap_trace_init_tohost(void); + /** * @brief Start heap tracing. All heap allocations & frees will be traced, until heap_trace_stop() is called. * diff --git a/components/heap/include/heap_trace.inc b/components/heap/include/heap_trace.inc new file mode 100644 index 000000000..a51b07a6a --- /dev/null +++ b/components/heap/include/heap_trace.inc @@ -0,0 +1,189 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include "soc/soc_memory_layout.h" +#include "esp_attr.h" + +/* Encode the CPU ID in the LSB of the ccount value */ +inline static uint32_t get_ccount(void) +{ + uint32_t ccount = xthal_get_ccount() & ~3; +#ifndef CONFIG_FREERTOS_UNICORE + ccount |= xPortGetCoreID(); +#endif + return ccount; +} + +// Caller is 2 stack frames deeper than we care about +#define STACK_OFFSET 2 + +#define TEST_STACK(N) do { \ + if (STACK_DEPTH == N) { \ + return; \ + } \ + callers[N] = __builtin_return_address(N+STACK_OFFSET); \ + if (!esp_ptr_executable(callers[N])) { \ + return; \ + } \ + } while(0) + +/* Static function to read the call stack for a traced heap call. + + Calls to __builtin_return_address are "unrolled" via TEST_STACK macro as gcc requires the + argument to be a compile-time constant. +*/ +static IRAM_ATTR __attribute__((noinline)) void get_call_stack(void **callers) +{ + bzero(callers, sizeof(void *) * STACK_DEPTH); + TEST_STACK(0); + TEST_STACK(1); + TEST_STACK(2); + TEST_STACK(3); + TEST_STACK(4); + TEST_STACK(5); + TEST_STACK(6); + TEST_STACK(7); + TEST_STACK(8); + TEST_STACK(9); +} + +_Static_assert(STACK_DEPTH >= 0 && STACK_DEPTH <= 10, "CONFIG_HEAP_TRACING_STACK_DEPTH must be in range 0-10"); + + +typedef enum { + TRACE_MALLOC_CAPS, + TRACE_MALLOC_DEFAULT +} trace_malloc_mode_t; + + +void *__real_heap_caps_malloc(size_t size, uint32_t caps); +void *__real_heap_caps_malloc_default( size_t size ); +void *__real_heap_caps_realloc_default( void *ptr, size_t size ); + +/* trace any 'malloc' event */ +static IRAM_ATTR __attribute__((noinline)) void *trace_malloc(size_t size, uint32_t caps, trace_malloc_mode_t mode) +{ + uint32_t ccount = get_ccount(); + void *p; + + if ( mode == TRACE_MALLOC_CAPS ) { + p = __real_heap_caps_malloc(size, caps); + } else { //TRACE_MALLOC_DEFAULT + p = __real_heap_caps_malloc_default(size); + } + + heap_trace_record_t rec = { + .address = p, + .ccount = ccount, + .size = size, + }; + get_call_stack(rec.alloced_by); + record_allocation(&rec); + return p; +} + +void __real_heap_caps_free(void *p); + +/* trace any 'free' event */ +static IRAM_ATTR __attribute__((noinline)) void trace_free(void *p) +{ + void *callers[STACK_DEPTH]; + get_call_stack(callers); + record_free(p, callers); + + __real_heap_caps_free(p); +} + +void * __real_heap_caps_realloc(void *p, size_t size, uint32_t caps); + +/* trace any 'realloc' event */ +static IRAM_ATTR __attribute__((noinline)) void *trace_realloc(void *p, size_t size, uint32_t caps, trace_malloc_mode_t mode) +{ + void *callers[STACK_DEPTH]; + uint32_t ccount = get_ccount(); + void *r; + + /* trace realloc as free-then-alloc */ + get_call_stack(callers); + record_free(p, callers); + + if (mode == TRACE_MALLOC_CAPS ) { + r = __real_heap_caps_realloc(p, size, caps); + } else { //TRACE_MALLOC_DEFAULT + r = __real_heap_caps_realloc_default(p, size); + } + /* realloc with zero size is a free */ + if (size != 0) { + heap_trace_record_t rec = { + .address = r, + .ccount = ccount, + .size = size, + }; + memcpy(rec.alloced_by, callers, sizeof(void *) * STACK_DEPTH); + record_allocation(&rec); + } + return r; +} + +/* Note: this changes the behaviour of libc malloc/realloc/free a bit, + as they no longer go via the libc functions in ROM. But more or less + the same in the end. */ + +IRAM_ATTR void *__wrap_malloc(size_t size) +{ + return trace_malloc(size, 0, TRACE_MALLOC_DEFAULT); +} + +IRAM_ATTR void __wrap_free(void *p) +{ + trace_free(p); +} + +IRAM_ATTR void *__wrap_realloc(void *p, size_t size) +{ + return trace_realloc(p, size, 0, TRACE_MALLOC_DEFAULT); +} + +IRAM_ATTR void *__wrap_calloc(size_t nmemb, size_t size) +{ + size = size * nmemb; + void *result = trace_malloc(size, 0, TRACE_MALLOC_DEFAULT); + if (result != NULL) { + memset(result, 0, size); + } + return result; +} + +IRAM_ATTR void *__wrap_heap_caps_malloc(size_t size, uint32_t caps) +{ + return trace_malloc(size, caps, TRACE_MALLOC_CAPS); +} + +void __wrap_heap_caps_free(void *p) __attribute__((alias("__wrap_free"))); + +IRAM_ATTR void *__wrap_heap_caps_realloc(void *p, size_t size, uint32_t caps) +{ + return trace_realloc(p, size, caps, TRACE_MALLOC_CAPS); +} + +IRAM_ATTR void *__wrap_heap_caps_malloc_default( size_t size ) +{ + return trace_malloc(size, 0, TRACE_MALLOC_DEFAULT); +} + +IRAM_ATTR void *__wrap_heap_caps_realloc_default( void *ptr, size_t size ) +{ + return trace_realloc(ptr, size, 0, TRACE_MALLOC_DEFAULT); +}