Merge branch 'feat/add-heap-walker-api' into 'master'

feat(heap): Add walker to the heap component

Closes IDF-9189

See merge request espressif/esp-idf!29047
This commit is contained in:
Guillaume Souchere
2024-03-22 15:58:34 +08:00
20 changed files with 436 additions and 26 deletions

View File

@@ -44,11 +44,16 @@ else()
endif() endif()
endif() endif()
if(CONFIG_HEAP_TLSF_USE_ROM_IMPL AND (CONFIG_ESP_ROM_TLSF_CHECK_PATCH OR CONFIG_HEAP_TLSF_CHECK_PATCH)) if(CONFIG_HEAP_TLSF_USE_ROM_IMPL AND CONFIG_ESP_ROM_TLSF_CHECK_PATCH)
# This file shall be included in the build if TLSF in ROM is activated # This file shall be included in the build if TLSF in ROM is activated
list(APPEND sources "patches/esp_rom_tlsf.c") list(APPEND sources "patches/esp_rom_tlsf.c")
endif() endif()
if(CONFIG_HEAP_TLSF_USE_ROM_IMPL AND CONFIG_ESP_ROM_MULTI_HEAP_WALK_PATCH)
# This file shall be included in the build if TLSF in ROM is activated
list(APPEND sources "patches/esp_rom_multi_heap.c")
endif()
list(APPEND private_required_comp soc hal) list(APPEND private_required_comp soc hal)
endif() endif()
@@ -64,6 +69,8 @@ if(CONFIG_HAL_WDT_USE_ROM_IMPL)
list(APPEND sources "patches/esp_rom_wdt.c") list(APPEND sources "patches/esp_rom_wdt.c")
endif() endif()
if(CONFIG_ESP_ROM_HAS_FLASH_COUNT_PAGES_BUG OR CONFIG_ESP_ROM_HAS_CACHE_WRITEBACK_BUG) if(CONFIG_ESP_ROM_HAS_FLASH_COUNT_PAGES_BUG OR CONFIG_ESP_ROM_HAS_CACHE_WRITEBACK_BUG)
list(APPEND sources "patches/esp_rom_cache_esp32s2_esp32s3.c") list(APPEND sources "patches/esp_rom_cache_esp32s2_esp32s3.c")
endif() endif()
@@ -216,6 +223,10 @@ else() # Regular app build
target_link_libraries(${COMPONENT_LIB} PRIVATE "-u tlsf_set_rom_patches") target_link_libraries(${COMPONENT_LIB} PRIVATE "-u tlsf_set_rom_patches")
endif() endif()
if((CONFIG_ESP_ROM_TLSF_CHECK_PATCH OR CONFIG_ESP_ROM_MULTI_HEAP_WALK_PATCH))
target_link_libraries(${COMPONENT_LIB} PRIVATE "-u esp_rom_include_multi_heap_patch")
endif()
rom_linker_script("heap") rom_linker_script("heap")
endif() endif()

View File

@@ -39,6 +39,14 @@ config ESP_ROM_HAS_HEAP_TLSF
bool bool
default y default y
config ESP_ROM_TLSF_CHECK_PATCH
bool
default y
config ESP_ROM_MULTI_HEAP_WALK_PATCH
bool
default y
config ESP_ROM_HAS_LAYOUT_TABLE config ESP_ROM_HAS_LAYOUT_TABLE
bool bool
default y default y

View File

@@ -15,6 +15,8 @@
#define ESP_ROM_HAS_HAL_WDT (1) // ROM has the implementation of Watchdog HAL driver #define ESP_ROM_HAS_HAL_WDT (1) // ROM has the implementation of Watchdog HAL driver
#define ESP_ROM_HAS_HAL_SYSTIMER (1) // ROM has the implementation of Systimer HAL driver #define ESP_ROM_HAS_HAL_SYSTIMER (1) // ROM has the implementation of Systimer HAL driver
#define ESP_ROM_HAS_HEAP_TLSF (1) // ROM has the implementation of the tlsf and multi-heap library #define ESP_ROM_HAS_HEAP_TLSF (1) // ROM has the implementation of the tlsf and multi-heap library
#define ESP_ROM_TLSF_CHECK_PATCH (1) // ROM does not contain the patch of tlsf_check_pool()
#define ESP_ROM_MULTI_HEAP_WALK_PATCH (1) // ROM does not contain the patch of multi_heap_walk()
#define ESP_ROM_HAS_LAYOUT_TABLE (1) // ROM has the layout table #define ESP_ROM_HAS_LAYOUT_TABLE (1) // ROM has the layout table
#define ESP_ROM_HAS_SPI_FLASH (1) // ROM has the implementation of SPI Flash driver #define ESP_ROM_HAS_SPI_FLASH (1) // ROM has the implementation of SPI Flash driver
#define ESP_ROM_HAS_NEWLIB (1) // ROM has newlib (at least parts of it) functions included #define ESP_ROM_HAS_NEWLIB (1) // ROM has newlib (at least parts of it) functions included

View File

@@ -51,6 +51,10 @@ config ESP_ROM_TLSF_CHECK_PATCH
bool bool
default y default y
config ESP_ROM_MULTI_HEAP_WALK_PATCH
bool
default y
config ESP_ROM_HAS_LAYOUT_TABLE config ESP_ROM_HAS_LAYOUT_TABLE
bool bool
default y default y

View File

@@ -18,6 +18,7 @@
#define ESP_ROM_HAS_HAL_SYSTIMER (1) // ROM has the implementation of Systimer HAL driver #define ESP_ROM_HAS_HAL_SYSTIMER (1) // ROM has the implementation of Systimer HAL driver
#define ESP_ROM_HAS_HEAP_TLSF (1) // ROM has the implementation of the tlsf and multi-heap library #define ESP_ROM_HAS_HEAP_TLSF (1) // ROM has the implementation of the tlsf and multi-heap library
#define ESP_ROM_TLSF_CHECK_PATCH (1) // ROM does not contain the patch of tlsf_check_pool() #define ESP_ROM_TLSF_CHECK_PATCH (1) // ROM does not contain the patch of tlsf_check_pool()
#define ESP_ROM_MULTI_HEAP_WALK_PATCH (1) // ROM does not contain the patch of multi_heap_walk()
#define ESP_ROM_HAS_LAYOUT_TABLE (1) // ROM has the layout table #define ESP_ROM_HAS_LAYOUT_TABLE (1) // ROM has the layout table
#define ESP_ROM_HAS_SPI_FLASH (1) // ROM has the implementation of SPI Flash driver #define ESP_ROM_HAS_SPI_FLASH (1) // ROM has the implementation of SPI Flash driver
#define ESP_ROM_WITHOUT_REGI2C (1) // ROM has no regi2c APIs #define ESP_ROM_WITHOUT_REGI2C (1) // ROM has no regi2c APIs

View File

@@ -51,6 +51,10 @@ config ESP_ROM_TLSF_CHECK_PATCH
bool bool
default y default y
config ESP_ROM_MULTI_HEAP_WALK_PATCH
bool
default y
config ESP_ROM_HAS_LAYOUT_TABLE config ESP_ROM_HAS_LAYOUT_TABLE
bool bool
default y default y

View File

@@ -18,6 +18,7 @@
#define ESP_ROM_HAS_HAL_SYSTIMER (1) // ROM has the implementation of Systimer HAL driver #define ESP_ROM_HAS_HAL_SYSTIMER (1) // ROM has the implementation of Systimer HAL driver
#define ESP_ROM_HAS_HEAP_TLSF (1) // ROM has the implementation of the tlsf and multi-heap library #define ESP_ROM_HAS_HEAP_TLSF (1) // ROM has the implementation of the tlsf and multi-heap library
#define ESP_ROM_TLSF_CHECK_PATCH (1) // ROM does not contain the patch of tlsf_check_pool() #define ESP_ROM_TLSF_CHECK_PATCH (1) // ROM does not contain the patch of tlsf_check_pool()
#define ESP_ROM_MULTI_HEAP_WALK_PATCH (1) // ROM does not contain the patch of multi_heap_walk()
#define ESP_ROM_HAS_LAYOUT_TABLE (1) // ROM has the layout table #define ESP_ROM_HAS_LAYOUT_TABLE (1) // ROM has the layout table
#define ESP_ROM_HAS_SPI_FLASH (1) // ROM has the implementation of SPI Flash driver #define ESP_ROM_HAS_SPI_FLASH (1) // ROM has the implementation of SPI Flash driver
#define ESP_ROM_HAS_REGI2C_BUG (1) // ROM has the regi2c bug #define ESP_ROM_HAS_REGI2C_BUG (1) // ROM has the regi2c bug

View File

@@ -43,6 +43,10 @@ config ESP_ROM_TLSF_CHECK_PATCH
bool bool
default y default y
config ESP_ROM_MULTI_HEAP_WALK_PATCH
bool
default y
config ESP_ROM_HAS_LAYOUT_TABLE config ESP_ROM_HAS_LAYOUT_TABLE
bool bool
default y default y

View File

@@ -16,6 +16,7 @@
#define ESP_ROM_HAS_HAL_SYSTIMER (1) // ROM has the implementation of Systimer HAL driver #define ESP_ROM_HAS_HAL_SYSTIMER (1) // ROM has the implementation of Systimer HAL driver
#define ESP_ROM_HAS_HEAP_TLSF (1) // ROM has the implementation of the tlsf and multi-heap library #define ESP_ROM_HAS_HEAP_TLSF (1) // ROM has the implementation of the tlsf and multi-heap library
#define ESP_ROM_TLSF_CHECK_PATCH (1) // ROM does not contain the patch of tlsf_check_pool() #define ESP_ROM_TLSF_CHECK_PATCH (1) // ROM does not contain the patch of tlsf_check_pool()
#define ESP_ROM_MULTI_HEAP_WALK_PATCH (1) // ROM does not contain the patch of multi_heap_walk()
#define ESP_ROM_HAS_LAYOUT_TABLE (1) // ROM has the layout table #define ESP_ROM_HAS_LAYOUT_TABLE (1) // ROM has the layout table
#define ESP_ROM_HAS_SPI_FLASH (1) // ROM has the implementation of SPI Flash driver #define ESP_ROM_HAS_SPI_FLASH (1) // ROM has the implementation of SPI Flash driver
#define ESP_ROM_WITHOUT_REGI2C (1) // ROM has no regi2c APIs #define ESP_ROM_WITHOUT_REGI2C (1) // ROM has no regi2c APIs

View File

@@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Opaque handle to a registered heap */
typedef struct multi_heap_info *multi_heap_handle_t;
/**
* @brief Callback called when walking the given heap blocks of memory
*
* @param block_ptr Pointer to the block data
* @param block_size The size of the block
* @param block_used Block status. 0: free, 1: allocated
* @param user_data Opaque pointer to user defined data
*
* @return True if the walker is expected to continue the heap traversal
* False if the walker is expected to stop the traversal of the heap
*/
typedef bool (*multi_heap_walker_cb_t)(void *block_ptr, size_t block_size, int block_used, void *user_data);
/**
* @brief Call the tlsf_walk_pool function of the heap given as parameter with
* the walker function passed as parameter
*
* @param heap The heap to traverse
* @param walker_func The walker to trigger on each block of the heap
* @param user_data Opaque pointer to user defined data
*/
void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_func, void *user_data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,50 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* This file is a patch for the multi_heap.c file stored in ROM
* - added function multi_heap_walk
*/
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include "esp_rom_multi_heap.h"
// Hook to force the linker to include this file
void esp_rom_include_multi_heap_patch(void)
{
}
/*!
* @brief Opaque types for TLSF implementation
*/
typedef void* tlsf_t;
typedef void* pool_t;
typedef void* tlsf_walker;
typedef struct multi_heap_info {
void *lock;
size_t free_bytes;
size_t minimum_free_bytes;
size_t pool_size;
void* heap_data;
} heap_t;
extern void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user);
extern pool_t tlsf_get_pool(tlsf_t tlsf);
extern void multi_heap_internal_lock(multi_heap_handle_t heap);
extern void multi_heap_internal_unlock(multi_heap_handle_t heap);
void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_func, void *user_data)
{
assert(heap != NULL);
multi_heap_internal_lock(heap);
tlsf_walk_pool(tlsf_get_pool(heap->heap_data), walker_func, user_data);
multi_heap_internal_unlock(heap);
}

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -15,6 +15,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <stdio.h>
#include "esp_rom_caps.h" #include "esp_rom_caps.h"
#include "esp_rom_tlsf.h" #include "esp_rom_tlsf.h"
@@ -24,13 +25,17 @@
*/ */
typedef void* tlsf_t; typedef void* tlsf_t;
typedef void* pool_t; typedef void* pool_t;
typedef void* tlsf_walker; typedef ptrdiff_t tlsfptr_t;
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* Bring certain inline functions, macro and structures from the * Bring certain inline functions, macro and structures from the
* tlsf ROM implementation to be able to compile the patch. * tlsf ROM implementation to be able to compile the patch.
* ---------------------------------------------------------------- */ * ---------------------------------------------------------------- */
#if !defined (tlsf_assert)
#define tlsf_assert assert
#endif
#define tlsf_cast(t, exp) ((t) (exp)) #define tlsf_cast(t, exp) ((t) (exp))
#define block_header_free_bit (1 << 0) #define block_header_free_bit (1 << 0)
@@ -72,6 +77,30 @@ static inline __attribute__((always_inline)) block_header_t* block_from_ptr(cons
tlsf_cast(unsigned char*, ptr) - block_start_offset); tlsf_cast(unsigned char*, ptr) - block_start_offset);
} }
static inline __attribute__((always_inline)) block_header_t* offset_to_block(const void* ptr, size_t size)
{
return tlsf_cast(block_header_t*, tlsf_cast(tlsfptr_t, ptr) + size);
}
static inline __attribute__((always_inline)) int block_is_last(const block_header_t* block)
{
return block_size(block) == 0;
}
static inline __attribute__((always_inline)) void* block_to_ptr(const block_header_t* block)
{
return tlsf_cast(void*,
tlsf_cast(unsigned char*, block) + block_start_offset);
}
static inline __attribute__((always_inline)) block_header_t* block_next(const block_header_t* block)
{
block_header_t* next = offset_to_block(block_to_ptr(block),
block_size(block) - block_header_overhead);
tlsf_assert(!block_is_last(block));
return next;
}
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* End of the environment necessary to compile and link the patch * End of the environment necessary to compile and link the patch
* defined below * defined below
@@ -92,7 +121,9 @@ typedef struct integrity_t
int status; int status;
} integrity_t; } integrity_t;
static void integrity_walker(void* ptr, size_t size, int used, void* user) typedef bool (*tlsf_walker)(void* ptr, size_t size, int used, void* user);
static bool integrity_walker(void* ptr, size_t size, int used, void* user)
{ {
block_header_t* block = block_from_ptr(ptr); block_header_t* block = block_from_ptr(ptr);
integrity_t* integ = tlsf_cast(integrity_t*, user); integrity_t* integ = tlsf_cast(integrity_t*, user);
@@ -124,9 +155,38 @@ static void integrity_walker(void* ptr, size_t size, int used, void* user)
integ->prev_status = this_status; integ->prev_status = this_status;
integ->status += status; integ->status += status;
return true;
}
static bool default_walker(void* ptr, size_t size, int used, void* user)
{
(void)user;
printf("\t%p %s size: %x (%p)\n", ptr, used ? "used" : "free", (unsigned int)size, block_from_ptr(ptr));
return true;
}
void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user)
{
tlsf_walker pool_walker = walker ? walker : default_walker;
block_header_t* block =
offset_to_block(pool, -(int)block_header_overhead);
bool ret_val = true;
while (block && !block_is_last(block) && ret_val == true)
{
ret_val = pool_walker(
block_to_ptr(block),
block_size(block),
!block_is_free(block),
user);
if (ret_val == true) {
block = block_next(block);
}
}
} }
extern void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user);
int tlsf_check_pool(pool_t pool) int tlsf_check_pool(pool_t pool)
{ {
/* Check that the blocks are physically correct. */ /* Check that the blocks are physically correct. */

View File

@@ -119,15 +119,6 @@ menu "Heap memory debugging"
features will be added and bugs will be fixed in the IDF source features will be added and bugs will be fixed in the IDF source
but cannot be synced to ROM. but cannot be synced to ROM.
config HEAP_TLSF_CHECK_PATCH
bool "Patch the tlsf_check_pool() for ROM HEAP TLSF implementation"
depends on HEAP_TLSF_USE_ROM_IMPL && IDF_TARGET_ESP32C2 && ESP32C2_REV_MIN_FULL < 200
default y
help
ROM does not contain the patch of tlsf_check_pool() allowing perform
the integrity checking on used blocks. The patch to allow such check
needs to be applied.
config HEAP_PLACE_FUNCTION_INTO_FLASH config HEAP_PLACE_FUNCTION_INTO_FLASH
bool "Force the entire heap component to be placed in flash memory" bool "Force the entire heap component to be placed in flash memory"
depends on !HEAP_TLSF_USE_ROM_IMPL depends on !HEAP_TLSF_USE_ROM_IMPL

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -15,6 +15,10 @@
#include "heap_private.h" #include "heap_private.h"
#include "esp_system.h" #include "esp_system.h"
#if CONFIG_HEAP_TLSF_USE_ROM_IMPL
#include "esp_rom_multi_heap.h"
#endif
#ifdef CONFIG_HEAP_USE_HOOKS #ifdef CONFIG_HEAP_USE_HOOKS
#define CALL_HOOK(hook, ...) { \ #define CALL_HOOK(hook, ...) { \
if (hook != NULL) { \ if (hook != NULL) { \
@@ -860,3 +864,46 @@ void *heap_caps_aligned_calloc(size_t alignment, size_t n, size_t size, uint32_t
return ptr; return ptr;
} }
typedef struct walker_data {
void *opaque_ptr;
heap_caps_walker_cb_t cb_func;
heap_t *heap;
} walker_data_t;
__attribute__((noinline)) static bool heap_caps_walker(void* block_ptr, size_t block_size, int block_used, void *user_data)
{
walker_data_t *walker_data = (walker_data_t*)user_data;
walker_heap_into_t heap_info = {
(intptr_t)walker_data->heap->start,
(intptr_t)walker_data->heap->end
};
walker_block_info_t block_info = {
block_ptr,
block_size,
(bool)block_used
};
return walker_data->cb_func(heap_info, block_info, walker_data->opaque_ptr);
}
void heap_caps_walk(uint32_t caps, heap_caps_walker_cb_t walker_func, void *user_data)
{
assert(walker_func != NULL);
bool all_heaps = caps & MALLOC_CAP_INVALID;
heap_t *heap;
SLIST_FOREACH(heap, &registered_heaps, next) {
if (heap->heap != NULL
&& (all_heaps || (get_all_caps(heap) & caps) == caps)) {
walker_data_t walker_data = {user_data, walker_func, heap};
multi_heap_walk(heap->heap, heap_caps_walker, &walker_data);
}
}
}
void heap_caps_walk_all(heap_caps_walker_cb_t walker_func, void *user_data)
{
heap_caps_walk(MALLOC_CAP_INVALID, walker_func, user_data);
}

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -444,6 +444,56 @@ void heap_caps_dump_all(void);
*/ */
size_t heap_caps_get_allocated_size( void *ptr ); size_t heap_caps_get_allocated_size( void *ptr );
/**
* @brief Structure used to store heap related data passed to
* the walker callback function
*/
typedef struct walker_heap_info {
intptr_t start; ///< Start address of the heap in which the block is located
intptr_t end; ///< End address of the heap in which the block is located
} walker_heap_into_t;
/**
* @brief Structure used to store block related data passed to
* the walker callback function
*/
typedef struct walker_block_info {
void *ptr; ///< Pointer to the block data
size_t size; ///< The size of the block
bool used; ///< Block status. True: used, False: free
} walker_block_info_t;
/**
* @brief Function callback used to get information of memory block
* during calls to heap_caps_walk or heap_caps_walk_all
*
* @param heap_info See walker_heap_into_t
* @param block_info See walker_block_info_t
* @param user_data Opaque pointer to user defined data
*
* @return True to proceed with the heap traversal
* False to stop the traversal of the current heap and continue
* with the traversal of the next heap (if any)
*/
typedef bool (*heap_caps_walker_cb_t)(walker_heap_into_t heap_info, walker_block_info_t block_info, void *user_data);
/**
* @brief Function called to walk through the heaps with the given set of capabilities
*
* @param caps The set of capabilities assigned to the heaps to walk through
* @param walker_func Callback called for each block of the heaps being traversed
* @param user_data Opaque pointer to user defined data
*/
void heap_caps_walk(uint32_t caps, heap_caps_walker_cb_t walker_func, void *user_data);
/**
* @brief Function called to walk through all heaps defined by the heap component
*
* @param walker_func Callback called for each block of the heaps being traversed
* @param user_data Opaque pointer to user defined data
*/
void heap_caps_walk_all(heap_caps_walker_cb_t walker_func, void *user_data);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -207,6 +207,29 @@ size_t multi_heap_reset_minimum_free_bytes(multi_heap_handle_t heap);
*/ */
void multi_heap_restore_minimum_free_bytes(multi_heap_handle_t heap, const size_t new_minimum_free_bytes_value); void multi_heap_restore_minimum_free_bytes(multi_heap_handle_t heap, const size_t new_minimum_free_bytes_value);
/**
* @brief Callback called when walking the given heap blocks of memory
*
* @param block_ptr Pointer to the block data
* @param block_size The size of the block
* @param block_used Block status. 0: free, 1: allocated
* @param user_data Opaque pointer to user defined data
*
* @return True if the walker is expected to continue the heap traversal
* False if the walker is expected to stop the traversal of the heap
*/
typedef bool (*multi_heap_walker_cb_t)(void *block_ptr, size_t block_size, int block_used, void *user_data);
/**
* @brief Call the tlsf_walk_pool function of the heap given as parameter with
* the walker function passed as parameter
*
* @param heap The heap to traverse
* @param walker_func The walker to trigger on each block of the heap
* @param user_data Opaque pointer to user defined data
*/
void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_func, void *user_data);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -355,13 +355,11 @@ bool multi_heap_check(multi_heap_handle_t heap, bool print_errors)
return valid; return valid;
} }
__attribute__((noinline)) static void multi_heap_dump_tlsf(void* ptr, size_t size, int used, void* user) __attribute__((noinline)) static bool multi_heap_dump_tlsf(void *ptr, size_t size, int used, void *user)
{ {
(void)user; (void)user;
MULTI_HEAP_STDERR_PRINTF("Block %p data, size: %d bytes, Free: %s \n", MULTI_HEAP_STDERR_PRINTF("Block %p data, size: %d bytes, Free: %s \n", (void *)ptr, size, used ? "No" : "Yes");
(void *)ptr, return true;
size,
used ? "No" : "Yes");
} }
void multi_heap_dump(multi_heap_handle_t heap) void multi_heap_dump(multi_heap_handle_t heap)
@@ -392,7 +390,7 @@ size_t multi_heap_minimum_free_size_impl(multi_heap_handle_t heap)
return heap->minimum_free_bytes; return heap->minimum_free_bytes;
} }
__attribute__((noinline)) static void multi_heap_get_info_tlsf(void* ptr, size_t size, int used, void* user) __attribute__((noinline)) static bool multi_heap_get_info_tlsf(void* ptr, size_t size, int used, void* user)
{ {
multi_heap_info_t *info = user; multi_heap_info_t *info = user;
@@ -407,6 +405,7 @@ __attribute__((noinline)) static void multi_heap_get_info_tlsf(void* ptr, size_t
} }
info->total_blocks++; info->total_blocks++;
return true;
} }
void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info) void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info)
@@ -431,6 +430,15 @@ void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info)
multi_heap_internal_unlock(heap); multi_heap_internal_unlock(heap);
} }
void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_func, void *user_data)
{
assert(heap != NULL);
multi_heap_internal_lock(heap);
tlsf_walk_pool(tlsf_get_pool(heap->heap_data), walker_func, user_data);
multi_heap_internal_unlock(heap);
}
#endif // CONFIG_HEAP_TLSF_USE_ROM_IMPL #endif // CONFIG_HEAP_TLSF_USE_ROM_IMPL
size_t multi_heap_reset_minimum_free_bytes(multi_heap_handle_t heap) size_t multi_heap_reset_minimum_free_bytes(multi_heap_handle_t heap)

View File

@@ -8,7 +8,8 @@ set(src_test "test_heap_main.c"
"test_malloc.c" "test_malloc.c"
"test_realloc.c" "test_realloc.c"
"test_runtime_heap_reg.c" "test_runtime_heap_reg.c"
"test_task_tracking.c") "test_task_tracking.c"
"test_walker.c")
idf_component_register(SRCS ${src_test} idf_component_register(SRCS ${src_test}
INCLUDE_DIRS "." INCLUDE_DIRS "."

View File

@@ -0,0 +1,101 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "unity.h"
#include "stdio.h"
#include "esp_heap_caps.h"
#include "esp_rom_sys.h"
#define PASS_CODE 0x9876BAAB
#define ALLOC_SIZE 16
static void calculate_block_metadata_size(size_t *header, size_t *footer)
{
*header = 0;
*footer = 0;
#if !CONFIG_HEAP_POISONING_DISABLED
*header += 8; // sizeof(poison_head_t)
*footer += 4; // sizeof(poison_tail_t)
#endif
#if CONFIG_HEAP_TASK_TRACKING
*header += 4; // sizeof(TaskHandle_t)
#endif
}
static bool block_found = false;
static bool heap_corrupted = false;
static bool heap_walker(walker_heap_into_t heap_info, walker_block_info_t block_info, void* user_data)
{
if ((intptr_t)heap_info.end - (intptr_t)block_info.ptr < block_info.size)
{
heap_corrupted = true;
return false;
}
TEST_ASSERT(*(uint32_t*)user_data == PASS_CODE);
TEST_ASSERT_NOT_NULL(block_info.ptr);
size_t metadata_size_head = 0;
size_t metadata_size_tail = 0;
calculate_block_metadata_size(&metadata_size_head, &metadata_size_tail);
/* look for the first 4 bytes pass code to identify the memory allocated in the test */
const uint32_t pass_code = *((uint32_t*)block_info.ptr + (metadata_size_head / sizeof(uint32_t)));
if (pass_code == PASS_CODE) {
TEST_ASSERT(block_info.size == ALLOC_SIZE + metadata_size_head + metadata_size_tail);
TEST_ASSERT_TRUE(block_info.used);
block_found = true;
}
return true;
}
/* This test assures the proper functioning of heap_caps_walk and heap_caps_walk_all
* when a corruption occurs in a random heap. The callback which detects the corruption
* returns false to the walker which should result in the interruption of the heap traversal
* by the walker instead of a crash.
*/
TEST_CASE("heap walker", "[heap]")
{
/* Allocate memory using the MALLOC_CAP_DEFAULT capability */
void *default_ptr = heap_caps_malloc(ALLOC_SIZE, MALLOC_CAP_DEFAULT);
TEST_ASSERT_NOT_NULL(default_ptr);
/* Write the pass code in the first word of the allocated memory */
*((uint32_t*)default_ptr) = (uint32_t)PASS_CODE;
/* call the heap_caps_walker to make sure the hook function is triggered
* and check that the allocated memory is found while walking the heap */
uint32_t user_code = PASS_CODE;
heap_caps_walk_all(heap_walker, &user_code);
TEST_ASSERT_TRUE(block_found);
}
/* This test assures the proper functioning of heap_caps_walk and heap_caps_walk_all
*/
TEST_CASE("heap walker corrupted heap detection", "[heap]")
{
/* Allocate memory using the MALLOC_CAP_DEFAULT capability */
void *default_ptr = heap_caps_malloc(ALLOC_SIZE, MALLOC_CAP_DEFAULT);
TEST_ASSERT_NOT_NULL(default_ptr);
size_t metadata_size_head = 0;
size_t metadata_size_tail = 0;
calculate_block_metadata_size(&metadata_size_head, &metadata_size_tail);
(void)metadata_size_tail;
/* corrupting the size field of the block metadata with a size bigger
* than the heap itself */
*((uint32_t*)default_ptr - (metadata_size_head / 4) - 1) = 0xFF000000;
/* Write the pass code in the first word of the allocated memory */
*((uint32_t*)default_ptr) = (uint32_t)PASS_CODE;
/* call the heap_caps_walker to make sure the hook function is triggered
* and check that the allocated memory is found while walking the heap */
uint32_t user_code = PASS_CODE;
heap_caps_walk_all(heap_walker, &user_code);
TEST_ASSERT_TRUE(heap_corrupted);
}