mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-14 01:58:36 -07:00
Furi: heap walker
This commit is contained in:
@@ -429,12 +429,54 @@ void cli_command_free(Cli* cli, FuriString* args, void* context) {
|
|||||||
printf("Maximum pool block: %zu\r\n", memmgr_pool_get_max_block());
|
printf("Maximum pool block: %zu\r\n", memmgr_pool_get_max_block());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void* addr;
|
||||||
|
size_t size;
|
||||||
|
} FreeBlockInfo;
|
||||||
|
|
||||||
|
#define FREE_BLOCK_INFO_MAX 128
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FreeBlockInfo free_blocks[FREE_BLOCK_INFO_MAX];
|
||||||
|
size_t free_blocks_count;
|
||||||
|
} FreeBlockContext;
|
||||||
|
|
||||||
|
static bool free_block_walker(void* pointer, size_t size, bool used, void* context) {
|
||||||
|
FreeBlockContext* free_blocks = (FreeBlockContext*)context;
|
||||||
|
if(!used) {
|
||||||
|
if(free_blocks->free_blocks_count < FREE_BLOCK_INFO_MAX) {
|
||||||
|
free_blocks->free_blocks[free_blocks->free_blocks_count].addr = pointer;
|
||||||
|
free_blocks->free_blocks[free_blocks->free_blocks_count].size = size;
|
||||||
|
free_blocks->free_blocks_count++;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) {
|
void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) {
|
||||||
UNUSED(cli);
|
UNUSED(cli);
|
||||||
UNUSED(args);
|
UNUSED(args);
|
||||||
UNUSED(context);
|
UNUSED(context);
|
||||||
|
|
||||||
memmgr_heap_printf_free_blocks();
|
FreeBlockContext* free_blocks = malloc(sizeof(FreeBlockContext));
|
||||||
|
free_blocks->free_blocks_count = 0;
|
||||||
|
|
||||||
|
memmgr_heap_walk_blocks(free_block_walker, free_blocks);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < free_blocks->free_blocks_count; i++) {
|
||||||
|
printf(
|
||||||
|
"A %p S %zu\r\n",
|
||||||
|
(void*)free_blocks->free_blocks[i].addr,
|
||||||
|
free_blocks->free_blocks[i].size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(free_blocks->free_blocks_count == FREE_BLOCK_INFO_MAX) {
|
||||||
|
printf("... and more\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(free_blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_command_i2c(Cli* cli, FuriString* args, void* context) {
|
void cli_command_i2c(Cli* cli, FuriString* args, void* context) {
|
||||||
|
|||||||
@@ -11,6 +11,23 @@ static tlsf_t tlsf = NULL;
|
|||||||
static size_t heap_used = 0;
|
static size_t heap_used = 0;
|
||||||
static size_t heap_max_used = 0;
|
static size_t heap_max_used = 0;
|
||||||
|
|
||||||
|
// Furi heap extension
|
||||||
|
#include <m-dict.h>
|
||||||
|
|
||||||
|
// Allocation tracking types
|
||||||
|
DICT_DEF2(MemmgrHeapAllocDict, uint32_t, uint32_t) //-V1048
|
||||||
|
|
||||||
|
DICT_DEF2( //-V1048
|
||||||
|
MemmgrHeapThreadDict,
|
||||||
|
uint32_t,
|
||||||
|
M_DEFAULT_OPLIST,
|
||||||
|
MemmgrHeapAllocDict_t,
|
||||||
|
DICT_OPLIST(MemmgrHeapAllocDict))
|
||||||
|
|
||||||
|
// Thread allocation tracing storage
|
||||||
|
static MemmgrHeapThreadDict_t memmgr_heap_thread_dict = {0};
|
||||||
|
static volatile uint32_t memmgr_heap_thread_trace_depth = 0;
|
||||||
|
|
||||||
static inline void memmgr_lock(void) {
|
static inline void memmgr_lock(void) {
|
||||||
vTaskSuspendAll();
|
vTaskSuspendAll();
|
||||||
}
|
}
|
||||||
@@ -23,24 +40,70 @@ static inline size_t memmgr_get_heap_size(void) {
|
|||||||
return (size_t)&__heap_end__ - (size_t)&__heap_start__;
|
return (size_t)&__heap_end__ - (size_t)&__heap_start__;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize tracing storage on start
|
||||||
|
void memmgr_heap_init(void) {
|
||||||
|
MemmgrHeapThreadDict_init(memmgr_heap_thread_dict);
|
||||||
|
}
|
||||||
|
|
||||||
void memmgr_heap_enable_thread_trace(FuriThreadId thread_id) {
|
void memmgr_heap_enable_thread_trace(FuriThreadId thread_id) {
|
||||||
UNUSED(thread_id);
|
memmgr_lock();
|
||||||
|
{
|
||||||
|
memmgr_heap_thread_trace_depth++;
|
||||||
|
furi_check(MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id) == NULL);
|
||||||
|
MemmgrHeapAllocDict_t alloc_dict;
|
||||||
|
MemmgrHeapAllocDict_init(alloc_dict);
|
||||||
|
MemmgrHeapThreadDict_set_at(memmgr_heap_thread_dict, (uint32_t)thread_id, alloc_dict);
|
||||||
|
MemmgrHeapAllocDict_clear(alloc_dict);
|
||||||
|
memmgr_heap_thread_trace_depth--;
|
||||||
|
}
|
||||||
|
memmgr_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void memmgr_heap_disable_thread_trace(FuriThreadId thread_id) {
|
void memmgr_heap_disable_thread_trace(FuriThreadId thread_id) {
|
||||||
UNUSED(thread_id);
|
memmgr_lock();
|
||||||
|
{
|
||||||
|
memmgr_heap_thread_trace_depth++;
|
||||||
|
furi_check(MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id));
|
||||||
|
memmgr_heap_thread_trace_depth--;
|
||||||
|
}
|
||||||
|
memmgr_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id) {
|
size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id) {
|
||||||
UNUSED(thread_id);
|
size_t leftovers = MEMMGR_HEAP_UNKNOWN;
|
||||||
return 0;
|
vTaskSuspendAll();
|
||||||
|
{
|
||||||
|
memmgr_heap_thread_trace_depth++;
|
||||||
|
MemmgrHeapAllocDict_t* alloc_dict =
|
||||||
|
MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id);
|
||||||
|
if(alloc_dict) {
|
||||||
|
leftovers = 0;
|
||||||
|
MemmgrHeapAllocDict_it_t alloc_dict_it;
|
||||||
|
for(MemmgrHeapAllocDict_it(alloc_dict_it, *alloc_dict);
|
||||||
|
!MemmgrHeapAllocDict_end_p(alloc_dict_it);
|
||||||
|
MemmgrHeapAllocDict_next(alloc_dict_it)) {
|
||||||
|
MemmgrHeapAllocDict_itref_t* data = MemmgrHeapAllocDict_ref(alloc_dict_it);
|
||||||
|
if(data->key != 0) {
|
||||||
|
block_header_t* block = block_from_ptr((uint8_t*)data->key);
|
||||||
|
if(!block_is_free(block)) {
|
||||||
|
// TODO: with tlsf we know the size of the block, so we don't need to store it on the dict
|
||||||
|
leftovers += data->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memmgr_heap_thread_trace_depth--;
|
||||||
|
}
|
||||||
|
(void)xTaskResumeAll();
|
||||||
|
return leftovers;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool tlsf_walker_max_free(void* ptr, size_t size, int used, void* user) {
|
static bool tlsf_walker_max_free(void* ptr, size_t size, int used, void* user) {
|
||||||
UNUSED(ptr);
|
UNUSED(ptr);
|
||||||
|
|
||||||
|
bool free = !used;
|
||||||
size_t* max_free_block_size = (size_t*)user;
|
size_t* max_free_block_size = (size_t*)user;
|
||||||
if(!used && size > *max_free_block_size) {
|
if(free && size > *max_free_block_size) {
|
||||||
*max_free_block_size = size;
|
*max_free_block_size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +123,52 @@ size_t memmgr_heap_get_max_free_block(void) {
|
|||||||
return max_free_block_size;
|
return max_free_block_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void memmgr_heap_printf_free_blocks(void) {
|
typedef struct {
|
||||||
|
BlockWalker walker;
|
||||||
|
void* context;
|
||||||
|
} BlockWalkerWrapper;
|
||||||
|
|
||||||
|
static bool tlsf_walker_wrapper(void* ptr, size_t size, int used, void* user) {
|
||||||
|
BlockWalkerWrapper* wrapper = (BlockWalkerWrapper*)user;
|
||||||
|
return wrapper->walker(ptr, size, used, wrapper->context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void memmgr_heap_walk_blocks(BlockWalker walker, void* context) {
|
||||||
|
memmgr_lock();
|
||||||
|
|
||||||
|
BlockWalkerWrapper wrapper = {walker, context};
|
||||||
|
pool_t pool = tlsf_get_pool(tlsf);
|
||||||
|
tlsf_walk_pool(pool, tlsf_walker_wrapper, &wrapper);
|
||||||
|
|
||||||
|
memmgr_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void memmgr_heap_trace_malloc(void* pointer, size_t size) {
|
||||||
|
FuriThreadId thread_id = furi_thread_get_current_id();
|
||||||
|
if(thread_id && memmgr_heap_thread_trace_depth == 0) {
|
||||||
|
memmgr_heap_thread_trace_depth++;
|
||||||
|
MemmgrHeapAllocDict_t* alloc_dict =
|
||||||
|
MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id);
|
||||||
|
if(alloc_dict) {
|
||||||
|
MemmgrHeapAllocDict_set_at(*alloc_dict, (uint32_t)pointer, (uint32_t)size);
|
||||||
|
}
|
||||||
|
memmgr_heap_thread_trace_depth--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void memmgr_heap_trace_free(void* pointer) {
|
||||||
|
FuriThreadId thread_id = furi_thread_get_current_id();
|
||||||
|
if(thread_id && memmgr_heap_thread_trace_depth == 0) {
|
||||||
|
memmgr_heap_thread_trace_depth++;
|
||||||
|
MemmgrHeapAllocDict_t* alloc_dict =
|
||||||
|
MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id);
|
||||||
|
if(alloc_dict) {
|
||||||
|
// In some cases thread may want to release memory that was not allocated by it
|
||||||
|
const bool res = MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer);
|
||||||
|
UNUSED(res);
|
||||||
|
}
|
||||||
|
memmgr_heap_thread_trace_depth--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* pvPortMalloc(size_t xSize) {
|
void* pvPortMalloc(size_t xSize) {
|
||||||
@@ -75,6 +183,7 @@ void* pvPortMalloc(size_t xSize) {
|
|||||||
if(tlsf == NULL) {
|
if(tlsf == NULL) {
|
||||||
size_t pool_size = (size_t)&__heap_end__ - (size_t)&__heap_start__;
|
size_t pool_size = (size_t)&__heap_end__ - (size_t)&__heap_start__;
|
||||||
tlsf = tlsf_create_with_pool((void*)&__heap_start__, pool_size, pool_size);
|
tlsf = tlsf_create_with_pool((void*)&__heap_start__, pool_size, pool_size);
|
||||||
|
memmgr_heap_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocate block
|
// allocate block
|
||||||
@@ -93,6 +202,9 @@ void* pvPortMalloc(size_t xSize) {
|
|||||||
// clear block content
|
// clear block content
|
||||||
memset(data, 0, xSize);
|
memset(data, 0, xSize);
|
||||||
|
|
||||||
|
// trace allocation
|
||||||
|
memmgr_heap_trace_malloc(data, xSize);
|
||||||
|
|
||||||
memmgr_unlock();
|
memmgr_unlock();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
@@ -119,12 +231,15 @@ void vPortFree(void* pv) {
|
|||||||
// free
|
// free
|
||||||
tlsf_free(tlsf, pv);
|
tlsf_free(tlsf, pv);
|
||||||
|
|
||||||
|
// trace free
|
||||||
|
memmgr_heap_trace_free(pv);
|
||||||
|
|
||||||
memmgr_unlock();
|
memmgr_unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t xPortGetFreeHeapSize(void) {
|
size_t xPortGetFreeHeapSize(void) {
|
||||||
return memmgr_get_heap_size() - heap_used;
|
return memmgr_get_heap_size() - heap_used - tlsf_size(tlsf);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t xPortGetTotalHeapSize(void) {
|
size_t xPortGetTotalHeapSize(void) {
|
||||||
@@ -132,5 +247,5 @@ size_t xPortGetTotalHeapSize(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t xPortGetMinimumEverFreeHeapSize(void) {
|
size_t xPortGetMinimumEverFreeHeapSize(void) {
|
||||||
return memmgr_get_heap_size() - heap_max_used;
|
return memmgr_get_heap_size() - heap_max_used - tlsf_size(tlsf);
|
||||||
}
|
}
|
||||||
@@ -40,9 +40,17 @@ size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id);
|
|||||||
*/
|
*/
|
||||||
size_t memmgr_heap_get_max_free_block(void);
|
size_t memmgr_heap_get_max_free_block(void);
|
||||||
|
|
||||||
/** Print the address and size of all free blocks to stdout
|
typedef bool (*BlockWalker)(void* pointer, size_t size, bool used, void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Walk through all heap blocks
|
||||||
|
* @warning This function will lock memory manager and may cause deadlocks if any malloc/free is called inside the callback.
|
||||||
|
* Also, printf and furi_log contains malloc calls, so do not use them.
|
||||||
|
*
|
||||||
|
* @param walker
|
||||||
|
* @param context
|
||||||
*/
|
*/
|
||||||
void memmgr_heap_printf_free_blocks(void);
|
void memmgr_heap_walk_blocks(BlockWalker walker, void* context);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,60.4,,
|
Version,+,61.0,,
|
||||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
@@ -2360,7 +2360,7 @@ Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId
|
|||||||
Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId
|
Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId
|
||||||
Function,+,memmgr_heap_get_max_free_block,size_t,
|
Function,+,memmgr_heap_get_max_free_block,size_t,
|
||||||
Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId
|
Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId
|
||||||
Function,+,memmgr_heap_printf_free_blocks,void,
|
Function,+,memmgr_heap_walk_blocks,void,"BlockWalker, void*"
|
||||||
Function,-,memmgr_pool_get_free,size_t,
|
Function,-,memmgr_pool_get_free,size_t,
|
||||||
Function,-,memmgr_pool_get_max_block,size_t,
|
Function,-,memmgr_pool_get_max_block,size_t,
|
||||||
Function,+,memmove,void*,"void*, const void*, size_t"
|
Function,+,memmove,void*,"void*, const void*, size_t"
|
||||||
|
|||||||
|
Reference in New Issue
Block a user