From 260da4a6f392517478d29b44b6733941f212a629 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Mon, 10 Sep 2018 13:34:07 +0800 Subject: [PATCH] feat(heap): Add new heap component --- components/esp8266/source/startup.c | 3 + components/heap/component.mk | 7 + components/heap/include/esp_heap_caps.h | 197 +++++++++++ components/heap/include/esp_heap_caps_init.h | 31 ++ components/heap/include/esp_heap_trace.h | 82 +++++ .../heap/include/priv/esp_heap_caps_priv.h | 154 ++++++++ components/heap/port/esp8266/esp_heap_init.c | 38 ++ .../port/esp8266/include/esp_heap_config.h | 23 ++ .../heap/port/esp8266/include/esp_heap_port.h | 34 ++ components/heap/src/esp_heap_caps.c | 329 ++++++++++++++++++ components/heap/src/esp_heap_trace.c | 143 ++++++++ 11 files changed, 1041 insertions(+) create mode 100644 components/heap/component.mk create mode 100644 components/heap/include/esp_heap_caps.h create mode 100644 components/heap/include/esp_heap_caps_init.h create mode 100644 components/heap/include/esp_heap_trace.h create mode 100644 components/heap/include/priv/esp_heap_caps_priv.h create mode 100644 components/heap/port/esp8266/esp_heap_init.c create mode 100644 components/heap/port/esp8266/include/esp_heap_config.h create mode 100644 components/heap/port/esp8266/include/esp_heap_port.h create mode 100644 components/heap/src/esp_heap_caps.c create mode 100644 components/heap/src/esp_heap_trace.c diff --git a/components/esp8266/source/startup.c b/components/esp8266/source/startup.c index d7c42eae..e8b991fe 100644 --- a/components/esp8266/source/startup.c +++ b/components/esp8266/source/startup.c @@ -26,6 +26,7 @@ #include "esp_image_format.h" #include "esp_phy_init.h" #include "esp_wifi_osi.h" +#include "esp_heap_caps_init.h" #include "internal/esp_wifi_internal.h" #define FLASH_MAP_ADDR 0x40200000 @@ -109,6 +110,8 @@ void call_user_start(size_t start_addr) "movi a2, 0xffffff00\n" "and a1, a1, a2\n"); + heap_caps_init(); + wifi_os_init(); assert(wifi_task_create(user_init_entry, "uiT", 2048, NULL, wifi_task_get_max_priority()) != NULL); diff --git a/components/heap/component.mk b/components/heap/component.mk new file mode 100644 index 00000000..b2e8c9a9 --- /dev/null +++ b/components/heap/component.mk @@ -0,0 +1,7 @@ +# +# Component Makefile +# + +COMPONENT_ADD_INCLUDEDIRS := include port/esp8266/include + +COMPONENT_SRCDIRS := src port/esp8266 diff --git a/components/heap/include/esp_heap_caps.h b/components/heap/include/esp_heap_caps.h new file mode 100644 index 00000000..ff99678d --- /dev/null +++ b/components/heap/include/esp_heap_caps.h @@ -0,0 +1,197 @@ +// Copyright 2018-2019 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. + +#pragma once + +#include "sdkconfig.h" + +#include +#include +#include "esp_err.h" + +#include "esp_heap_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get "HEAP_ALIGN_SIZE" bytes aligned data(HEAP_ALIGN(ptr) >= ptr). + */ +#define HEAP_ALIGN(ptr) (((size_t)ptr + (HEAP_ALIGN_SIZE - 1)) & ~(HEAP_ALIGN_SIZE - 1)) + +#define MALLOC_CAP_32BIT (1 << 1) ///< Memory must allow for aligned 32-bit data accesses +#define MALLOC_CAP_8BIT (1 << 2) ///< Memory must allow for 8-bit data accesses +#define MALLOC_CAP_DMA (1 << 3) ///< Memory must be able to accessed by DMA + +#define MEM_BLK_TAG 0x80000000 ///< Mark the memory block used +#define MEM_BLK_TRACE 0x80000000 ///< Mark the memory block traced + +#define MEM_HEAD_SIZE sizeof(mem_blk_t) ///< Size of first type memory block +#define MEM2_HEAD_SIZE sizeof(mem2_blk_t) ///< Size of second type memory block + +/** + * First type memory block. + */ +typedef struct mem_blk { + struct mem_blk *prev; ///< Point to previous memory block + struct mem_blk *next; ///< Point to next memory block +} mem_blk_t; + +/** + * Second type memory block. + */ +typedef struct mem_blk2 { + struct mem_blk2 *prev; ///< Point to previous memory block + struct mem_blk2 *next; ///< Point to next memory block + + const char *file; ///< Which "file" allocate the memory block + size_t line; ///< Which "line" allocate the memory block +} mem2_blk_t; + +/** + * User region information. + */ +typedef struct heap_region { + void *start_addr; ///< Heap region start address + size_t total_size; ///< Heap region total size by byte + + uint32_t caps; ///< Heap capacity + + void *free_blk; ///< First free memory block + + size_t free_bytes; ///< Current free heap size by byte + + size_t min_free_bytes; ///< Minimum free heap size by byte ever +} heap_region_t; + + +/** + * @brief Get the total free size of all the regions that have the given capabilities + * + * This function takes all regions capable of having the given capabilities allocated in them + * and adds up the free space they have. + * + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type of memory + * + * @return Amount of free bytes in the regions + */ +size_t heap_caps_get_free_size(uint32_t caps); + +/** + * @brief Get the total minimum free memory of all regions with the given capabilities + * + * This adds all the low water marks of the regions capable of delivering the memory + * with the given capabilities. + * + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type of memory + * + * @return Amount of free bytes in the regions + */ +size_t heap_caps_get_minimum_free_size(uint32_t caps); + +/** + * @brief Initialize regions of memory to the collection of heaps at runtime. + * + * @param region region table head point + * @param max_num region table size + */ +void esp_heap_caps_init_region(heap_region_t *region, size_t max_num); + +/** + * @brief Allocate a chunk of memory which has the given capabilities + * + * Equivalent semantics to libc malloc(), for capability-aware memory. + * + * In SDK, ``malloc(s)`` is equivalent to ``heap_caps_malloc(s, MALLOC_CAP_32BIT)``. + * + * @param size Size, in bytes, of the amount of memory to allocate + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type of memory to be returned + * + * @return A pointer to the memory allocated on success, NULL on failure + */ +#define heap_caps_malloc(size, caps) _heap_caps_malloc(size, caps, __FILE__, __LINE__) + +void *_heap_caps_malloc(size_t size, uint32_t caps, const char *file, size_t line); + +/** + * @brief Free memory previously allocated via heap_caps_(m/c/re/z)alloc(). + * + * Equivalent semantics to libc free(), for capability-aware memory. + * + * In SDK, ``free(p)`` is equivalent to ``heap_caps_free(p)``. + * + * @param ptr Pointer to memory previously returned from heap_caps_(m/c/re/z)alloc(). Can be NULL. + */ +#define heap_caps_free(p) _heap_caps_free(p, __FILE__, __LINE__) + +void _heap_caps_free(void *ptr, const char *file, size_t line); + +/** + * @brief Allocate a chunk of memory which has the given capabilities. The initialized value in the memory is set to zero. + * + * Equivalent semantics to libc calloc(), for capability-aware memory. + * + * In IDF, ``calloc(c, s)`` is equivalent to ``heap_caps_calloc(c, s, MALLOC_CAP_32BIT)``. + * + * @param n Number of continuing chunks of memory to allocate + * @param size Size, in bytes, of a chunk of memory to allocate + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type of memory to be returned + * + * @return A pointer to the memory allocated on success, NULL on failure + */ +#define heap_caps_calloc(n, size, caps) _heap_caps_calloc(n, size, caps, __FILE__, __LINE__) + +void *_heap_caps_calloc(size_t count, size_t size, uint32_t caps, const char *file, size_t line); + +/** + * @brief Reallocate memory previously allocated via heap_caps_(m/c/re/z)alloc(). + * + * Equivalent semantics to libc realloc(), for capability-aware memory. + * + * In SDK, ``realloc(p, s)`` is equivalent to ``heap_caps_realloc(p, s, MALLOC_CAP_32BIT)``. + * + * 'caps' parameter can be different to the capabilities that any original 'ptr' was allocated with. In this way, + * realloc can be used to "move" a buffer if necessary to ensure it meets a new set of capabilities. + * + * @param ptr Pointer to previously allocated memory, or NULL for a new allocation. + * @param size Size of the new buffer requested, or 0 to free the buffer. + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type of memory desired for the new allocation. + * + * @return Pointer to a new buffer of size 'size' with capabilities 'caps', or NULL if allocation failed. + */ +#define heap_caps_realloc(ptr, size, caps) _heap_caps_realloc(ptr, size, caps, __FILE__, __LINE__) + +void *_heap_caps_realloc(void *mem, size_t newsize, uint32_t caps, const char *file, size_t line); + +/** + * @brief Allocate a chunk of memory which has the given capabilities. The initialized value in the memory is set to zero. + * + * Equivalent semantics to libc calloc(), for capability-aware memory. + * + * In IDF, ``calloc(c, s)`` is equivalent to ``heap_caps_calloc(c, s, MALLOC_CAP_32BIT)``. + * + * @param n Number of continuing chunks of memory to allocate + * @param size Size, in bytes, of a chunk of memory to allocate + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type of memory to be returned + * + * @return A pointer to the memory allocated on success, NULL on failure + */ +#define heap_caps_zalloc(size, caps) _heap_caps_zalloc(size, caps, __FILE__, __LINE__) + +void *_heap_caps_zalloc(size_t size, uint32_t caps, const char *file, size_t line); + +#ifdef __cplusplus +} +#endif diff --git a/components/heap/include/esp_heap_caps_init.h b/components/heap/include/esp_heap_caps_init.h new file mode 100644 index 00000000..932a8793 --- /dev/null +++ b/components/heap/include/esp_heap_caps_init.h @@ -0,0 +1,31 @@ +// Copyright 2018-2019 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. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the capability-aware heap allocator. + * + * This is called once in the ESP8266 startup code. Do not call it + * at other times. + */ +void heap_caps_init(); + +#ifdef __cplusplus +} +#endif diff --git a/components/heap/include/esp_heap_trace.h b/components/heap/include/esp_heap_trace.h new file mode 100644 index 00000000..a4655ccd --- /dev/null +++ b/components/heap/include/esp_heap_trace.h @@ -0,0 +1,82 @@ +// Copyright 2018-2019 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. + +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + HEAP_TRACE_NONE = 0, + + HEAP_TRACE_LEAKS, +} heap_trace_mode_t; + +typedef struct { + char buf[1]; +} heap_trace_record_t; + +/** + * @brief Check if heap trace is on + * + * @return true if on or false + */ +int heap_trace_is_on(void); + +/** + * @brief Empty function just for passing compiling some place. + */ +esp_err_t heap_trace_init_standalone(heap_trace_record_t *record_buffer, size_t num_records); + +/** + * @brief Start heap tracing. All heap allocations will be traced, until heap_trace_stop() is called. + * + * @param mode Mode for tracing. + * - HEAP_TRACE_LEAKS means only suspected memory leaks are traced. (When memory is freed, the record is removed from the trace buffer.) + * @return + * - ESP_OK Tracing is started. + */ +esp_err_t heap_trace_start(heap_trace_mode_t mode); + +/** + * @brief Stop heap tracing. + * + * @return + * - ESP_OK Heap tracing stopped.. + */ +esp_err_t heap_trace_stop(void); + +/** + * @brief Resume heap tracing which was previously stopped. + * + * @return + * - ESP_ERR_NOT_SUPPORTED Project was compiled without heap tracing enabled in menuconfig. + * - ESP_OK Heap tracing resumed. + */ +esp_err_t heap_trace_resume(void); + +/** + * @brief Dump heap trace record data to stdout + * + * @note It is safe to call this function while heap tracing is running, however in HEAP_TRACE_LEAK mode the dump may skip + * entries unless heap tracing is stopped first. + */ +void heap_trace_dump(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/heap/include/priv/esp_heap_caps_priv.h b/components/heap/include/priv/esp_heap_caps_priv.h new file mode 100644 index 00000000..8ed10789 --- /dev/null +++ b/components/heap/include/priv/esp_heap_caps_priv.h @@ -0,0 +1,154 @@ +// Copyright 2018-2019 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. + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define _mem_blk_get_ptr(_mem_blk, _offset, _mask) \ + ((mem_blk_t *)((((uint32_t *)(_mem_blk))[_offset]) & (~_mask))) + +#define _mem_blk_set_ptr(_mem_blk, _val, _offset, _mask) \ +{ \ + uint32_t *__p = (uint32_t *)(_mem_blk); \ + uint32_t __bits = __p[_offset] & (_mask); \ + \ + __p[_offset] = (uint32_t)(_val) | __bits; \ +} + +#define mem_blk_prev(_mem_blk) _mem_blk_get_ptr(_mem_blk, 0, MEM_BLK_TAG) +#define mem_blk_next(_mem_blk) _mem_blk_get_ptr(_mem_blk, 1, MEM_BLK_TRACE) + +#define mem_blk_set_prev(_mem_blk, _prev) _mem_blk_set_ptr(_mem_blk, _prev, 0, MEM_BLK_TAG) +#define mem_blk_set_next(_mem_blk, _next) _mem_blk_set_ptr(_mem_blk, _next, 1, MEM_BLK_TRACE) + +static inline size_t mem_blk_head_size(bool trace) +{ + return trace ? MEM2_HEAD_SIZE : MEM_HEAD_SIZE; +} + +static inline void mem_blk_set_traced(mem2_blk_t *mem_blk, const char *file, size_t line) +{ + uint32_t *val = (uint32_t *)mem_blk + 1; + + *val |= MEM_BLK_TRACE; + + mem_blk->file = file; + mem_blk->line = line; +} + +static inline void mem_blk_set_untraced(mem2_blk_t *mem_blk) +{ + uint32_t *val = (uint32_t *)mem_blk + 1; + + *val &= ~MEM_BLK_TRACE; +} + +static inline int mem_blk_is_traced(mem_blk_t *mem_blk) +{ + uint32_t *val = (uint32_t *)mem_blk + 1; + + return *val & MEM_BLK_TRACE; +} + +static inline void mem_blk_set_used(mem_blk_t *mem_blk) +{ + uint32_t *val = (uint32_t *)mem_blk; + + *val |= MEM_BLK_TAG; +} + +static inline void mem_blk_set_unused(mem_blk_t *mem_blk) +{ + uint32_t *val = (uint32_t *)mem_blk; + + *val &= ~MEM_BLK_TAG; +} + +static inline int mem_blk_is_used(mem_blk_t *mem_blk) +{ + uint32_t *val = (uint32_t *)mem_blk; + + return *val & MEM_BLK_TAG; +} + +static inline int mem_blk_is_end(mem_blk_t *mem_blk) +{ + return mem_blk_next(mem_blk) == NULL; +} + +static inline size_t blk_link_size(mem_blk_t *blk) +{ + return (size_t)mem_blk_next(blk) - (size_t)blk; +} + +static inline size_t get_blk_region(void *ptr) +{ + size_t num; + extern heap_region_t g_heap_region[HEAP_REGIONS_MAX]; + + for (num = 0; num < HEAP_REGIONS_MAX; num++) { + if ((uint8_t *)ptr > (uint8_t *)g_heap_region[num].start_addr + && (uint8_t *)ptr < ((uint8_t *)g_heap_region[num].start_addr + g_heap_region[num].total_size)) { + break; + } + } + + return num; +} + +static inline size_t ptr2memblk_size(size_t size, bool trace) +{ + size_t head_size = trace ? MEM2_HEAD_SIZE : MEM_HEAD_SIZE; + + return HEAP_ALIGN(size + head_size); +} + +static inline bool ptr_is_traced(void *ptr) +{ + uint32_t *p = (uint32_t *)ptr - 1; + + return p[0] & 0xf0000000 ? (p[0] == 0xffffffff ? true : false) : true; +} + +static inline mem_blk_t *ptr2blk(void *ptr, bool trace) +{ + size_t head_size = trace ? MEM2_HEAD_SIZE : MEM_HEAD_SIZE; + + return (mem_blk_t *)((uint8_t *)ptr - head_size); +} + +static inline void *blk2ptr(mem_blk_t *mem_blk, bool trace) +{ + size_t head_size = trace ? MEM2_HEAD_SIZE : MEM_HEAD_SIZE; + + return (void *)((uint8_t *)mem_blk + head_size); +} + +static inline size_t ptr_size(void *ptr) +{ + bool trancd = ptr_is_traced(ptr); + mem_blk_t *blk_mem = ptr2blk(ptr, trancd); + size_t size = blk_link_size(blk_mem) - mem_blk_head_size(trancd); + + return size; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/heap/port/esp8266/esp_heap_init.c b/components/heap/port/esp8266/esp_heap_init.c new file mode 100644 index 00000000..889b0670 --- /dev/null +++ b/components/heap/port/esp8266/esp_heap_init.c @@ -0,0 +1,38 @@ +// Copyright 2018-2019 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 "esp_heap_caps.h" + +heap_region_t g_heap_region[HEAP_REGIONS_MAX]; + +/** + * @brief Initialize the capability-aware heap allocator. + */ +void heap_caps_init(void) +{ + extern char _heap_start; + extern char _lit4_end; + +#ifndef CONFIG_SOC_FULL_ICACHE + g_heap_region[0].start_addr = (uint8_t *)&_lit4_end; + g_heap_region[0].total_size = ((size_t)(0x4010C000 - (uint32_t)&_lit4_end)); + g_heap_region[0].caps = MALLOC_CAP_32BIT; +#endif + + g_heap_region[1].start_addr = (uint8_t *)&_heap_start; + g_heap_region[1].total_size = ((size_t)(0x40000000 - (uint32_t)&_heap_start)); + g_heap_region[1].caps = MALLOC_CAP_8BIT | MALLOC_CAP_32BIT | MALLOC_CAP_DMA; + + esp_heap_caps_init_region(g_heap_region, HEAP_REGIONS_MAX); +} diff --git a/components/heap/port/esp8266/include/esp_heap_config.h b/components/heap/port/esp8266/include/esp_heap_config.h new file mode 100644 index 00000000..6c1c97c1 --- /dev/null +++ b/components/heap/port/esp8266/include/esp_heap_config.h @@ -0,0 +1,23 @@ +// Copyright 2018-2019 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. + +#pragma once + +#include "sdkconfig.h" + +#define HEAP_ALIGN_SIZE 4 + +#define HEAP_REGIONS_MAX 2 + +#define MEM_BLK_MIN 1 diff --git a/components/heap/port/esp8266/include/esp_heap_port.h b/components/heap/port/esp8266/include/esp_heap_port.h new file mode 100644 index 00000000..4b9522b9 --- /dev/null +++ b/components/heap/port/esp8266/include/esp_heap_port.h @@ -0,0 +1,34 @@ +// Copyright 2018-2019 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. + +#pragma once + +#define _heap_caps_lock(_num) \ +{ \ + extern void vPortETSIntrLock(void); \ + vPortETSIntrLock(); \ +} + +#define _heap_caps_unlock(_num) \ +{ \ + extern void vPortETSIntrUnlock(void); \ + vPortETSIntrUnlock(); \ +} + +#define _heap_caps_feed_wdt(_num) \ +{ \ + extern void pp_soft_wdt_feed(void); \ + pp_soft_wdt_feed(); \ +} + diff --git a/components/heap/src/esp_heap_caps.c b/components/heap/src/esp_heap_caps.c new file mode 100644 index 00000000..606365c6 --- /dev/null +++ b/components/heap/src/esp_heap_caps.c @@ -0,0 +1,329 @@ +// Copyright 2018-2019 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 +#include + +#include "esp_heap_caps.h" +#include "esp_heap_port.h" +#include "esp_heap_trace.h" +#include "priv/esp_heap_caps_priv.h" + +#define LOG_LOCAL_LEVEL ESP_LOG_NONE + +#include "esp_log.h" + +static const char *TAG = "heap_caps"; +extern heap_region_t g_heap_region[HEAP_REGIONS_MAX]; + +/** + * @brief Initialize regions of memory to the collection of heaps at runtime. + */ +void esp_heap_caps_init_region(heap_region_t *region, size_t max_num) +{ + uint8_t num; + mem_blk_t *mem_start, *mem_end; + + for (num = 0; num < max_num; num++) { + mem_start = (mem_blk_t *)HEAP_ALIGN(region[num].start_addr); + mem_end = (mem_blk_t *)(HEAP_ALIGN(region[num].start_addr + region[num].total_size)); + if ((uint8_t *)mem_end != region[num].start_addr + region[num].total_size) + mem_end = (mem_blk_t *)((uint8_t *)mem_end - sizeof(void *)); + mem_end = (mem_blk_t *)((uint8_t *)mem_end - MEM_HEAD_SIZE); + + ESP_EARLY_LOGV(TAG, "heap %d start from %p to %p total %d bytes, mem_blk from %p to %p total", + num, region[num].start_addr, region[num].start_addr + region[num].total_size, + region[num].total_size, mem_start, mem_end); + + mem_start->prev = NULL; + mem_start->next = mem_end; + + mem_end->prev = mem_start; + mem_end->next = NULL; + + g_heap_region[num].free_blk = mem_start; + g_heap_region[num].min_free_bytes = g_heap_region[num].free_bytes = blk_link_size(mem_start); + } +} + +/** + * @brief Get the total free size of all the regions that have the given capabilities + */ +size_t heap_caps_get_free_size(uint32_t caps) +{ + size_t bytes = 0; + + for (int i = 0; i < HEAP_REGIONS_MAX; i++) + if (caps == (caps & g_heap_region[i].caps)) + bytes += g_heap_region[i].free_bytes; + + return bytes; +} + +/** + * @brief Get the total minimum free memory of all regions with the given capabilities + */ +size_t heap_caps_get_minimum_free_size(uint32_t caps) +{ + size_t bytes = 0; + + for (int i = 0; i < HEAP_REGIONS_MAX; i++) + if (caps == (caps & g_heap_region[i].caps)) + bytes += g_heap_region[i].min_free_bytes; + + return bytes; +} + +/** + * @brief Allocate a chunk of memory which has the given capabilities + */ +void *_heap_caps_malloc(size_t size, uint32_t caps, const char *file, size_t line) +{ + mem_blk_t *mem_blk, *next_mem_blk; + void *ret_mem = NULL; + uint32_t num; + uint32_t mem_blk_size; + + if (line == 0) { + ESP_EARLY_LOGV(TAG, "caller func %p", file); + } else { + ESP_EARLY_LOGV(TAG, "caller file %s line %d", file, line); + } + + for (num = 0; num < HEAP_REGIONS_MAX; num++) { + bool trace; + size_t head_size; + + if ((g_heap_region[num].caps & caps) != caps) + continue; + + _heap_caps_lock(num); + + trace = heap_trace_is_on(); + + mem_blk_size = ptr2memblk_size(size, trace); + + ESP_EARLY_LOGV(TAG, "malloc size is %d(%x) blk size is %d(%x) region is %d", size, size, + mem_blk_size, mem_blk_size, num); + + if (mem_blk_size > g_heap_region[num].free_bytes) + goto next_region; + + mem_blk = (mem_blk_t *)g_heap_region[num].free_blk; + + ESP_EARLY_LOGV(TAG, "malloc start %p", mem_blk); + + while (mem_blk && !mem_blk_is_end(mem_blk) && (mem_blk_is_used(mem_blk) || blk_link_size(mem_blk) < mem_blk_size)) { + ESP_EARLY_LOGV(TAG, "malloc mem_blk %p next %p used %x traced %x, size %d", mem_blk, mem_blk_next(mem_blk), + mem_blk_is_used(mem_blk), mem_blk_is_traced(mem_blk), blk_link_size(mem_blk)); + mem_blk = mem_blk_next(mem_blk); + } + + ESP_EARLY_LOGV(TAG, "malloc end %p, end %d", mem_blk, mem_blk_is_end(mem_blk)); + + if (!mem_blk || mem_blk_is_end(mem_blk)) + goto next_region; + + ret_mem = blk2ptr(mem_blk, trace); + ESP_EARLY_LOGV(TAG, "ret_mem is %p", ret_mem); + + head_size = mem_blk_head_size(trace); + + if (blk_link_size(mem_blk) >= mem_blk_size + head_size + MEM_BLK_MIN) + next_mem_blk = (mem_blk_t *)((uint8_t *)mem_blk + mem_blk_size); + else + next_mem_blk = mem_blk_next(mem_blk); + + ESP_EARLY_LOGV(TAG, "next_mem_blk is %p", next_mem_blk); + + if (mem_blk_next(mem_blk) != next_mem_blk) { + next_mem_blk->prev = next_mem_blk->next = NULL; + + mem_blk_set_prev(next_mem_blk, mem_blk); + mem_blk_set_next(next_mem_blk, mem_blk_next(mem_blk)); + + ESP_EARLY_LOGV(TAG, "mem_blk1 %p, mem_blk->prev %p(%p), mem_blk->next %p(%p)", mem_blk, mem_blk_prev(mem_blk), + mem_blk->prev, mem_blk_next(mem_blk), mem_blk->next); + + mem_blk_set_prev(mem_blk_next(mem_blk), next_mem_blk); + mem_blk_set_next(mem_blk, next_mem_blk); + } + + mem_blk_set_used(mem_blk); + if (trace) { + mem_blk_set_traced((mem2_blk_t *)mem_blk, file, line); + ESP_EARLY_LOGV(TAG, "mem_blk1 %p set trace", mem_blk); + } + + if (g_heap_region[num].free_blk == mem_blk) { + mem_blk_t *free_blk = mem_blk; + + while (free_blk && !mem_blk_is_end(free_blk) && mem_blk_is_used(free_blk)) { + free_blk = mem_blk_next(free_blk); + } + + ESP_EARLY_LOGV(TAG, "reset free_blk from %p to %p", g_heap_region[num].free_blk, free_blk); + g_heap_region[num].free_blk = free_blk; + } else { + ESP_EARLY_LOGV(TAG, "free_blk is %p", g_heap_region[num].free_blk); + } + + g_heap_region[num].free_bytes -= mem_blk_size; + + if (g_heap_region[num].min_free_bytes > g_heap_region[num].free_bytes) + g_heap_region[num].min_free_bytes = g_heap_region[num].free_bytes; + + ESP_EARLY_LOGV(TAG, "mem_blk2 %p, mem_blk->prev %p(%p), mem_blk->next %p(%p)", mem_blk, mem_blk_prev(mem_blk), + mem_blk->prev, mem_blk_next(mem_blk), mem_blk->next); + ESP_EARLY_LOGV(TAG, "next_mem_blk %p, next_mem_blk->prev %p(%p), next_mem_blk->next %p(%p)", next_mem_blk, + mem_blk_prev(next_mem_blk), next_mem_blk->prev, mem_blk_next(next_mem_blk), next_mem_blk->next); + ESP_EARLY_LOGV(TAG, "last_mem_blk %p, last_mem_blk->prev %p(%p), last_mem_blk->next %p(%p)", mem_blk_next(next_mem_blk), + mem_blk_next(next_mem_blk)->prev, mem_blk_prev(mem_blk_next(next_mem_blk)), mem_blk_next(mem_blk_next(next_mem_blk)), mem_blk_next(next_mem_blk)->next); + +next_region: + _heap_caps_unlock(num); + + if (ret_mem) + break; + } + + ESP_EARLY_LOGV(TAG, "malloc return mem %p", ret_mem); + + return ret_mem; +} + +/** + * @brief Free memory previously allocated via heap_caps_(m/c/r/z)alloc(). + */ +void _heap_caps_free(void *ptr, const char *file, size_t line) +{ + int num; + mem_blk_t *mem_blk; + mem_blk_t *tmp, *next, *prev, *last; + + if ((int)line == 0) { + ESP_EARLY_LOGV(TAG, "caller func %p", file); + } else { + ESP_EARLY_LOGV(TAG, "caller file %s line %d", file, line); + } + + if (!ptr) { + ESP_EARLY_LOGE(TAG, "free(ptr=NULL)"); + if ((int)line == 0) { + ESP_EARLY_LOGE(TAG, "caller func %p", file); + } else { + ESP_EARLY_LOGE(TAG, "caller file %s line %d", file, line); + } + return; + } + + num = get_blk_region(ptr); + + if (num >= HEAP_REGIONS_MAX) { + ESP_EARLY_LOGE(TAG, "free(ptr_region=NULL)"); + return; + } + + mem_blk = ptr2blk(ptr, ptr_is_traced(ptr)); +#ifndef MEMLEAK_DEBUG + if (mem_blk->prev) { + ESP_EARLY_LOGE("%p already freed\n", ptr); + return; + } +#endif + + ESP_EARLY_LOGV(TAG, "Free(ptr=%p, mem_blk=%p, region=%d)", ptr, mem_blk, num); + + _heap_caps_lock(num); + + g_heap_region[num].free_bytes += blk_link_size(mem_blk); + + ESP_EARLY_LOGV(TAG, "ptr prev=%p next=%p", mem_blk_prev(mem_blk), mem_blk_next(mem_blk)); + ESP_EARLY_LOGV(TAG, "ptr1 prev->next=%p next->prev=%p", mem_blk_prev(mem_blk) ? mem_blk_next(mem_blk_prev(mem_blk)) : NULL, + mem_blk_prev(mem_blk_next(mem_blk))); + + mem_blk_set_unused(mem_blk); + mem_blk_set_untraced((mem2_blk_t *)mem_blk); + + prev = mem_blk_prev(mem_blk); + next = mem_blk_next(mem_blk); + last = mem_blk_next(next); + + if (prev && !mem_blk_is_used(prev)) { + mem_blk_set_next(prev, next); + mem_blk_set_prev(next, prev); + tmp = prev; + } else + tmp = mem_blk; + + if (last && !mem_blk_is_used(next)) { + mem_blk_set_next(tmp, last); + mem_blk_set_prev(last, tmp); + } + + ESP_EARLY_LOGV(TAG, "ptr2 prev->next=%p next->prev=%p", mem_blk_prev(mem_blk) ? mem_blk_next(mem_blk_prev(mem_blk)) : NULL, + mem_blk_prev(mem_blk_next(mem_blk))); + + if ((uint8_t *)mem_blk < (uint8_t *)g_heap_region[num].free_blk) { + ESP_EARLY_LOGV(TAG, "Free update free block from %p to %p", g_heap_region[num].free_blk, mem_blk); + g_heap_region[num].free_blk = mem_blk; + } + + _heap_caps_unlock(num); +} + +/** + * @brief Allocate a chunk of memory which has the given capabilities. The initialized value in the memory is set to zero. + */ +void *_heap_caps_calloc(size_t count, size_t size, uint32_t caps, const char *file, size_t line) +{ + void *p = _heap_caps_malloc(count * size, caps, file, line); + if (p) + memset(p, 0, count * size); + + return p; +} + +/** + * @brief Reallocate memory previously allocated via heap_caps_(m/c/r/z)alloc(). + */ +void *_heap_caps_realloc(void *mem, size_t newsize, uint32_t caps, const char *file, size_t line) +{ + void *return_addr = (void *)__builtin_return_address(0); + + void *p = _heap_caps_malloc(newsize, caps, file, line); + if (p && mem) { + size_t mem_size = ptr_size(mem); + size_t min = MIN(newsize, mem_size); + + memcpy(p, mem, min); + _heap_caps_free(mem, (char *)return_addr, line); + } + + return p; +} + +/** + * @brief Allocate a chunk of memory which has the given capabilities. The initialized value in the memory is set to zero. + */ +void *_heap_caps_zalloc(size_t size, uint32_t caps, const char *file, size_t line) +{ + void *p = _heap_caps_malloc(size, caps, file, line); + if (p) + memset(p, 0, size); + + return p; +} diff --git a/components/heap/src/esp_heap_trace.c b/components/heap/src/esp_heap_trace.c new file mode 100644 index 00000000..f0151331 --- /dev/null +++ b/components/heap/src/esp_heap_trace.c @@ -0,0 +1,143 @@ +// Copyright 2018-2019 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 "esp_heap_caps.h" +#include "esp_heap_port.h" +#include "esp_heap_trace.h" +#include "priv/esp_heap_caps_priv.h" + +#include "esp_log.h" + +//#define CONFIG_TRACE_ALL +//#define CONFIG_TRACE_MEM_LINK 1 +//#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE + +#ifdef CONFIG_TRACE_ALL +#define HEAP_INFO_STATE " is %s" +#define HEAP_INFO_STATE_PARAM(_p) ,mem_blk_is_used(_p)?"used":"freed" +#else +#define HEAP_INFO_STATE "" +#define HEAP_INFO_STATE_PARAM(_p) +#endif + +#ifdef CONFIG_TRACE_MEM_LINK +#define HEAP_INFO "p %p, prev %p(%p) next %p(%p) size %d"HEAP_INFO_STATE +#define HEAP_INFO_PARAM(_p) (_p),mem_blk_prev(_p),(_p)->prev,mem_blk_next(_p),(_p)->next,blk_link_size(_p)HEAP_INFO_STATE_PARAM(_p) +#else +#define HEAP_INFO "mem @%p size %d"HEAP_INFO_STATE +#define HEAP_INFO_PARAM(_p) (_p),blk_link_size(_p)HEAP_INFO_STATE_PARAM(_p) +#endif + +static const char *TAG = "heap_trace"; +static int s_heap_trace_mode = HEAP_TRACE_NONE; +extern heap_region_t g_heap_region[HEAP_REGIONS_MAX]; + +/** + * @brief Empty function just for passing compiling some place. + */ +esp_err_t heap_trace_init_standalone(heap_trace_record_t *record_buffer, size_t num_records) +{ + return ESP_OK; +} + +/** + * @brief Check if heap trace is on + */ +int heap_trace_is_on(void) +{ + return s_heap_trace_mode == HEAP_TRACE_LEAKS; +} + +/** + * @brief Start heap tracing. All heap allocations will be traced, until heap_trace_stop() is called. + */ +esp_err_t heap_trace_start(heap_trace_mode_t mode) +{ + s_heap_trace_mode = mode; + + return ESP_OK; +} + +/** + * @brief Stop heap tracing. + */ +esp_err_t heap_trace_stop(void) +{ + s_heap_trace_mode = HEAP_TRACE_NONE; + + return ESP_OK; +} + +/** + * @brief Resume heap tracing which was previously stopped. + */ +esp_err_t heap_trace_resume(void) +{ + s_heap_trace_mode = HEAP_TRACE_LEAKS; + + return ESP_OK; +} + +/** + * @brief Dump heap trace record data to stdout + */ +void heap_trace_dump(void) +{ + uint8_t num; + mem_blk_t *mem_start, *mem_end, *p; + + for (num = 0; num < HEAP_REGIONS_MAX; num++) { + mem_start = (mem_blk_t *)HEAP_ALIGN(g_heap_region[num].start_addr); + mem_end = (mem_blk_t *)(HEAP_ALIGN(g_heap_region[num].start_addr + g_heap_region[num].total_size)); + if ((uint8_t *)mem_end != g_heap_region[num].start_addr + g_heap_region[num].total_size) + mem_end = (mem_blk_t *)((uint8_t *)mem_end - sizeof(void *)); + mem_end = (mem_blk_t *)((uint8_t *)mem_end - MEM_HEAD_SIZE); + + _heap_caps_lock(num); + + ESP_EARLY_LOGI(TAG, "\r\n\r\n"); + ESP_EARLY_LOGD(TAG, "start %p end %p", mem_start, mem_end); + ESP_EARLY_LOGD(TAG, "free blk %p", g_heap_region[num].free_blk); + ESP_EARLY_LOGD(TAG, "size %d mini size %d", g_heap_region[num].free_bytes, g_heap_region[num].min_free_bytes); + + p = mem_start; + while (p != mem_end) { + if (mem_blk_is_used(p) && mem_blk_is_traced(p)) { + mem2_blk_t *mem2_blk = (mem2_blk_t *)p; + + if (mem2_blk->line == (size_t)-1) { + ESP_EARLY_LOGI(TAG, HEAP_INFO " caller func %p", HEAP_INFO_PARAM(p), mem2_blk->file); + } else { + const char *file = rindex(mem2_blk->file, '/'); + if (file) + file++; + + ESP_EARLY_LOGI(TAG, HEAP_INFO " caller file %s line %d", HEAP_INFO_PARAM(p), file, mem2_blk->line); + } + } +#ifdef CONFIG_TRACE_ALL + else { + ESP_EARLY_LOGI(TAG, HEAP_INFO, HEAP_INFO_PARAM(p)); + } +#endif + p = mem_blk_next(p); + + _heap_caps_feed_wdt(g_heap_region[num].caps & caps); + } + + _heap_caps_unlock(num); + } +}