forked from espressif/esp-idf
Merge branch 'feat/heap-get-size-from-any-pointer' into 'master'
feat(heap): add API to get allocated size from any pointer Closes IDF-12764 See merge request espressif/esp-idf!34531
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -38,6 +38,20 @@ typedef bool (*multi_heap_walker_cb_t)(void *block_ptr, size_t block_size, int b
|
||||
*/
|
||||
void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_func, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Function walking through a given heap and returning the pointer to the
|
||||
* allocated block containing the pointer passed as parameter.
|
||||
*
|
||||
* @note The heap parameter must be valid and the pointer parameter must
|
||||
* belong to a block of allocated memory. The app will crash with an
|
||||
* assertion failure if at least one of the parameter is invalid.
|
||||
*
|
||||
* @param heap The heap to walk through
|
||||
* @param ptr The pointer to find the allocated block of
|
||||
* @return Pointer to the allocated block containing the pointer ptr
|
||||
*/
|
||||
void *multi_heap_find_containing_block_impl(multi_heap_handle_t heap, void *ptr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_rom_multi_heap.h"
|
||||
|
||||
// Hook to force the linker to include this file
|
||||
@@ -49,3 +50,101 @@ void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_fun
|
||||
tlsf_walk_pool(tlsf_get_pool(heap->heap_data), walker_func, user_data);
|
||||
multi_heap_internal_unlock(heap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Structure used in multi_heap_find_containing_block to retain
|
||||
* information while walking a given heap to find the allocated block
|
||||
* containing the pointer ptr.
|
||||
*
|
||||
* @note The block_ptr gets filled with the pointer to the allocated block
|
||||
* containing the ptr.
|
||||
*/
|
||||
typedef struct containing_block_data {
|
||||
void *ptr; ///< Pointer to find the containing block of
|
||||
void *block_ptr; ///< Pointer to the containing block
|
||||
} containing_block_data_t;
|
||||
|
||||
|
||||
#if CONFIG_HEAP_POISONING_DISABLED
|
||||
void *multi_heap_find_containing_block(multi_heap_handle_t heap, void *ptr)
|
||||
__attribute__((alias("multi_heap_find_containing_block_impl")));
|
||||
#endif
|
||||
|
||||
typedef struct block_header_t
|
||||
{
|
||||
/* Points to the previous physical block. */
|
||||
struct block_header_t* prev_phys_block;
|
||||
|
||||
/* The size of this block, excluding the block header. */
|
||||
size_t size;
|
||||
|
||||
/* Next and previous free blocks. */
|
||||
struct block_header_t* next_free;
|
||||
struct block_header_t* prev_free;
|
||||
} block_header_t;
|
||||
|
||||
#define tlsf_cast(t, exp) ((t) (exp))
|
||||
#define block_header_free_bit (1UL << 0)
|
||||
#define block_header_prev_free_bit (1UL << 1)
|
||||
#define block_header_overhead (sizeof(size_t))
|
||||
#define block_start_offset (offsetof(block_header_t, size) + sizeof(size_t))
|
||||
|
||||
#if !defined (tlsf_assert)
|
||||
#define tlsf_assert assert
|
||||
#endif
|
||||
|
||||
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 size_t block_size(const block_header_t* block)
|
||||
{
|
||||
return block->size & ~(block_header_free_bit | block_header_prev_free_bit);
|
||||
}
|
||||
static block_header_t* offset_to_block(const void* ptr, size_t size)
|
||||
{
|
||||
return tlsf_cast(block_header_t*, tlsf_cast(ptrdiff_t, ptr) + size);
|
||||
}
|
||||
static int block_is_free(const block_header_t* block)
|
||||
{
|
||||
return tlsf_cast(int, block->size & block_header_free_bit);
|
||||
}
|
||||
static int block_is_last(const block_header_t* block)
|
||||
{
|
||||
return block_size(block) == 0;
|
||||
}
|
||||
static 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;
|
||||
}
|
||||
|
||||
void* tlsf_find_containing_block(pool_t pool, void *ptr)
|
||||
{
|
||||
block_header_t* block = offset_to_block(pool, -(int)block_header_overhead);
|
||||
|
||||
while (block && !block_is_last(block))
|
||||
{
|
||||
if (!block_is_free(block)) {
|
||||
void *block_end = block_to_ptr(block) + block_size(block);
|
||||
if (block_to_ptr(block) <= ptr && block_end > ptr) {
|
||||
// we found the containing block, return
|
||||
return block_to_ptr(block);
|
||||
}
|
||||
}
|
||||
|
||||
block = block_next(block);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *multi_heap_find_containing_block_impl(multi_heap_handle_t heap, void *ptr)
|
||||
{
|
||||
void *block_ptr = tlsf_find_containing_block(tlsf_get_pool(heap->heap_data), ptr);
|
||||
assert(block_ptr);
|
||||
return block_ptr;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -468,6 +468,15 @@ size_t heap_caps_get_allocated_size( void *ptr )
|
||||
return MULTI_HEAP_REMOVE_BLOCK_OWNER_SIZE(size);
|
||||
}
|
||||
|
||||
size_t heap_caps_get_containing_block_size(void *ptr)
|
||||
{
|
||||
heap_t *heap = find_containing_heap(ptr);
|
||||
assert(heap);
|
||||
|
||||
void *block_ptr = multi_heap_find_containing_block(heap->heap, ptr);
|
||||
return multi_heap_get_allocated_size(heap->heap, block_ptr);
|
||||
}
|
||||
|
||||
static HEAP_IRAM_ATTR esp_err_t heap_caps_aligned_check_args(size_t alignment, size_t size, uint32_t caps, const char *funcname)
|
||||
{
|
||||
if (!alignment) {
|
||||
@@ -567,7 +576,7 @@ typedef struct walker_data {
|
||||
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)
|
||||
__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;
|
||||
|
||||
|
@@ -195,11 +195,15 @@ void heap_caps_dump_all(void)
|
||||
heap_caps_dump(MALLOC_CAP_INVALID);
|
||||
}
|
||||
|
||||
size_t heap_caps_get_allocated_size( void *ptr )
|
||||
size_t heap_caps_get_allocated_size(void *ptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t heap_caps_get_containing_block_size(void *ptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *heap_caps_aligned_alloc(size_t alignment, size_t size, uint32_t caps)
|
||||
{
|
||||
void *ptr = aligned_alloc(alignment, size);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -446,7 +446,20 @@ void heap_caps_dump_all(void);
|
||||
* @return Size of the memory allocated at this block.
|
||||
*
|
||||
*/
|
||||
size_t heap_caps_get_allocated_size( void *ptr );
|
||||
size_t heap_caps_get_allocated_size(void *ptr);
|
||||
|
||||
/**
|
||||
* @brief Return the size of the block containing the pointer passed as parameter.
|
||||
*
|
||||
* @param ptr Pointer to currently allocated heap memory. The pointer value
|
||||
* must be within the allocated memory and the memory must not be freed.
|
||||
*
|
||||
* @note The app will crash with an assertion failure if the pointer is invalid.
|
||||
*
|
||||
* @return Size of the containing block allocated.
|
||||
*
|
||||
*/
|
||||
size_t heap_caps_get_containing_block_size(void *ptr);
|
||||
|
||||
/**
|
||||
* @brief Structure used to store heap related data passed to
|
||||
|
@@ -239,6 +239,20 @@ void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_fun
|
||||
*/
|
||||
size_t multi_heap_get_full_block_size(multi_heap_handle_t heap, void *p);
|
||||
|
||||
/**
|
||||
* @brief Function walking through a given heap and returning the pointer to the
|
||||
* allocated block containing the pointer passed as parameter.
|
||||
*
|
||||
* @note The heap parameter must be valid and the pointer parameter must
|
||||
* belong to a block of allocated memory. The app will crash with an
|
||||
* assertion failure if at least one of the parameter is invalid.
|
||||
*
|
||||
* @param heap The heap to walk through
|
||||
* @param ptr The pointer to find the allocated block of
|
||||
* @return Pointer to the allocated block containing the pointer ptr
|
||||
*/
|
||||
void *multi_heap_find_containing_block(multi_heap_handle_t heap, void *ptr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -72,6 +72,9 @@ size_t multi_heap_minimum_free_size(multi_heap_handle_t heap)
|
||||
void *multi_heap_get_block_address(multi_heap_block_handle_t block)
|
||||
__attribute__((alias("multi_heap_get_block_address_impl")));
|
||||
|
||||
void *multi_heap_find_containing_block(multi_heap_handle_t heap, void *ptr)
|
||||
__attribute__((alias("multi_heap_find_containing_block_impl")));
|
||||
|
||||
#endif // !CONFIG_HEAP_TLSF_USE_ROM_IMPL
|
||||
#endif // !MULTI_HEAP_POISONING
|
||||
|
||||
@@ -441,6 +444,26 @@ void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_fun
|
||||
multi_heap_internal_unlock(heap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Structure used in multi_heap_find_containing_block to retain
|
||||
* information while walking a given heap to find the allocated block
|
||||
* containing the pointer ptr.
|
||||
*
|
||||
* @note The block_ptr gets filled with the pointer to the allocated block
|
||||
* containing the ptr.
|
||||
*/
|
||||
typedef struct containing_block_data {
|
||||
void *ptr; ///< Pointer to find the containing block of
|
||||
void *block_ptr; ///< Pointer to the containing block
|
||||
} containing_block_data_t;
|
||||
|
||||
void *multi_heap_find_containing_block_impl(multi_heap_handle_t heap, void *ptr)
|
||||
{
|
||||
void *block_ptr = tlsf_find_containing_block(tlsf_get_pool(heap->heap_data), ptr);
|
||||
assert(block_ptr);
|
||||
return block_ptr;
|
||||
}
|
||||
|
||||
#endif // CONFIG_HEAP_TLSF_USE_ROM_IMPL
|
||||
|
||||
size_t multi_heap_reset_minimum_free_bytes(multi_heap_handle_t heap)
|
||||
|
@@ -56,6 +56,7 @@ size_t multi_heap_free_size_impl(multi_heap_handle_t heap);
|
||||
size_t multi_heap_minimum_free_size_impl(multi_heap_handle_t heap);
|
||||
size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p);
|
||||
void *multi_heap_get_block_address_impl(multi_heap_block_handle_t block);
|
||||
void *multi_heap_find_containing_block_impl(multi_heap_handle_t heap, void *ptr);
|
||||
|
||||
/* Some internal functions for heap poisoning use */
|
||||
|
||||
|
@@ -450,6 +450,15 @@ void multi_heap_internal_poison_fill_region(void *start, size_t size, bool is_fr
|
||||
memset(start, is_free ? FREE_FILL_PATTERN : MALLOC_FILL_PATTERN, size);
|
||||
}
|
||||
|
||||
void *multi_heap_find_containing_block(multi_heap_handle_t heap, void *ptr)
|
||||
{
|
||||
void * block_ptr = multi_heap_find_containing_block_impl(heap, ptr);
|
||||
// add the poison_head_t size to the pointer returned since all other
|
||||
// functions expect the pointer to point to the first usable byte and not
|
||||
// to the first allocated byte.
|
||||
return block_ptr + sizeof(poison_head_t);
|
||||
}
|
||||
|
||||
#else // !MULTI_HEAP_POISONING
|
||||
|
||||
#ifdef MULTI_HEAP_POISONING_SLOW
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -210,9 +210,15 @@ TEST_CASE("test get allocated size", "[heap]")
|
||||
// since the heap component aligns to 4 bytes)
|
||||
const size_t aligned_size = (alloc_sizes[i] + 3) & ~3;
|
||||
const size_t real_size = heap_caps_get_allocated_size(ptr_array[i]);
|
||||
printf("initial size: %d, requested size : %d, allocated size: %d\n", alloc_sizes[i], aligned_size, real_size);
|
||||
TEST_ASSERT(aligned_size <= real_size);
|
||||
|
||||
// test the heap_caps_get_containing_block_size() returns the right number of bytes
|
||||
// when the pointer to the first, last (calculated from the requested size) and to a byte
|
||||
// in the middle of the chunk is passed as parameter
|
||||
TEST_ASSERT(aligned_size <= heap_caps_get_containing_block_size(ptr_array[i]));
|
||||
TEST_ASSERT(aligned_size <= heap_caps_get_containing_block_size(ptr_array[i] + alloc_sizes[i]));
|
||||
TEST_ASSERT(aligned_size <= heap_caps_get_containing_block_size(ptr_array[i] + (alloc_sizes[i] / 2)));
|
||||
|
||||
heap_caps_free(ptr_array[i]);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user