mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
FreeRTOS: temporary solution for memory canaries and memory debug
1. This is just a temporary solution, it will be removed when umm_malloc is ready 2. Support memory canaries mechanism 2. Add debug code to show allocated memory info
This commit is contained in:
parent
1a6e63feef
commit
b21d2dfa6b
@ -158,6 +158,12 @@ config FREERTOS_BREAK_ON_SCHEDULER_START_JTAG
|
||||
If JTAG/OCD is connected, stop execution when the scheduler is started and the first
|
||||
task is executed.
|
||||
|
||||
menuconfig ENABLE_MEMORY_DEBUG
|
||||
bool "Enable heap memory debug"
|
||||
default n
|
||||
help
|
||||
Enable this option to show malloc heap block and memory crash detect
|
||||
|
||||
menuconfig FREERTOS_DEBUG_INTERNALS
|
||||
bool "Debug FreeRTOS internals"
|
||||
default n
|
||||
|
@ -132,6 +132,7 @@ task.h is included from an application file. */
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "heap_regions_debug.h"
|
||||
|
||||
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||
|
||||
@ -171,7 +172,7 @@ static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert );
|
||||
|
||||
/* The size of the structure placed at the beginning of each allocated memory
|
||||
block must by correctly byte aligned. */
|
||||
static const uint32_t uxHeapStructSize = ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK );
|
||||
static const uint32_t uxHeapStructSize = ( ( sizeof ( BlockLink_t ) + BLOCK_HEAD_LEN + BLOCK_TAIL_LEN + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK );
|
||||
|
||||
/* Create a couple of list links to mark the start and end of the list. */
|
||||
static BlockLink_t xStart, *pxEnd = NULL;
|
||||
@ -238,6 +239,13 @@ void *pvReturn = NULL;
|
||||
while( ( ( pxBlock->xTag != tag ) || ( pxBlock->xBlockSize < xWantedSize ) ) && ( pxBlock->pxNextFreeBlock != NULL ) )
|
||||
{
|
||||
// ets_printf("Block %x -> %x\n", (uint32_t)pxBlock, (uint32_t)pxBlock->pxNextFreeBlock);
|
||||
|
||||
#if (configENABLE_MEMORY_DEBUG == 1)
|
||||
{
|
||||
mem_check_block(pxBlock);
|
||||
}
|
||||
#endif
|
||||
|
||||
pxPreviousBlock = pxBlock;
|
||||
pxBlock = pxBlock->pxNextFreeBlock;
|
||||
}
|
||||
@ -248,7 +256,7 @@ void *pvReturn = NULL;
|
||||
{
|
||||
/* Return the memory space pointed to - jumping over the
|
||||
BlockLink_t structure at its start. */
|
||||
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + uxHeapStructSize );
|
||||
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + uxHeapStructSize - BLOCK_TAIL_LEN - BLOCK_HEAD_LEN);
|
||||
|
||||
/* This block is being returned for use so must be taken out
|
||||
of the list of free blocks. */
|
||||
@ -256,13 +264,14 @@ void *pvReturn = NULL;
|
||||
|
||||
/* If the block is larger than required it can be split into
|
||||
two. */
|
||||
|
||||
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
|
||||
{
|
||||
/* This block is to be split into two. Create a new
|
||||
block following the number of bytes requested. The void
|
||||
cast is used to prevent byte alignment warnings from the
|
||||
compiler. */
|
||||
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
|
||||
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize);
|
||||
|
||||
/* Calculate the sizes of two blocks split from the
|
||||
single block. */
|
||||
@ -270,6 +279,13 @@ void *pvReturn = NULL;
|
||||
pxNewBlockLink->xTag = tag;
|
||||
pxBlock->xBlockSize = xWantedSize;
|
||||
|
||||
#if (configENABLE_MEMORY_DEBUG == 1)
|
||||
{
|
||||
mem_init_dog(pxNewBlockLink);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Insert the new block into the list of free blocks. */
|
||||
prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );
|
||||
}
|
||||
@ -293,6 +309,13 @@ void *pvReturn = NULL;
|
||||
by the application and has no "next" block. */
|
||||
pxBlock->xBlockSize |= xBlockAllocatedBit;
|
||||
pxBlock->pxNextFreeBlock = NULL;
|
||||
|
||||
#if (configENABLE_MEMORY_DEBUG == 1)
|
||||
{
|
||||
mem_init_dog(pxBlock);
|
||||
mem_malloc_block(pxBlock);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -340,11 +363,20 @@ BlockLink_t *pxLink;
|
||||
{
|
||||
/* The memory being freed will have an BlockLink_t structure immediately
|
||||
before it. */
|
||||
puc -= uxHeapStructSize;
|
||||
puc -= (uxHeapStructSize - BLOCK_TAIL_LEN - BLOCK_HEAD_LEN) ;
|
||||
|
||||
/* This casting is to keep the compiler from issuing warnings. */
|
||||
pxLink = ( void * ) puc;
|
||||
|
||||
#if (configENABLE_MEMORY_DEBUG == 1)
|
||||
{
|
||||
taskENTER_CRITICAL(&xMallocMutex);
|
||||
mem_check_block(pxLink);
|
||||
mem_free_block(pxLink);
|
||||
taskEXIT_CRITICAL(&xMallocMutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check the block is actually allocated. */
|
||||
configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
|
||||
configASSERT( pxLink->pxNextFreeBlock == NULL );
|
||||
@ -497,7 +529,7 @@ const HeapRegionTagged_t *pxHeapRegion;
|
||||
{
|
||||
/* xStart is used to hold a pointer to the first item in the list of
|
||||
free blocks. The void cast is used to prevent compiler warnings. */
|
||||
xStart.pxNextFreeBlock = ( BlockLink_t * ) pucAlignedHeap;
|
||||
xStart.pxNextFreeBlock = ( BlockLink_t * ) (pucAlignedHeap + BLOCK_HEAD_LEN);
|
||||
xStart.xBlockSize = ( size_t ) 0;
|
||||
}
|
||||
else
|
||||
@ -519,7 +551,7 @@ const HeapRegionTagged_t *pxHeapRegion;
|
||||
ulAddress = ( ( uint32_t ) pucAlignedHeap ) + xTotalRegionSize;
|
||||
ulAddress -= uxHeapStructSize;
|
||||
ulAddress &= ~portBYTE_ALIGNMENT_MASK;
|
||||
pxEnd = ( BlockLink_t * ) ulAddress;
|
||||
pxEnd = ( BlockLink_t * ) (ulAddress + BLOCK_HEAD_LEN);
|
||||
pxEnd->xBlockSize = 0;
|
||||
pxEnd->pxNextFreeBlock = NULL;
|
||||
pxEnd->xTag = -1;
|
||||
@ -527,8 +559,8 @@ const HeapRegionTagged_t *pxHeapRegion;
|
||||
/* To start with there is a single free block in this region that is
|
||||
sized to take up the entire heap region minus the space taken by the
|
||||
free block structure. */
|
||||
pxFirstFreeBlockInRegion = ( BlockLink_t * ) pucAlignedHeap;
|
||||
pxFirstFreeBlockInRegion->xBlockSize = ulAddress - ( uint32_t ) pxFirstFreeBlockInRegion;
|
||||
pxFirstFreeBlockInRegion = ( BlockLink_t * ) (pucAlignedHeap + BLOCK_HEAD_LEN);
|
||||
pxFirstFreeBlockInRegion->xBlockSize = ulAddress - ( uint32_t ) pxFirstFreeBlockInRegion + BLOCK_HEAD_LEN;
|
||||
pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd;
|
||||
pxFirstFreeBlockInRegion->xTag=pxHeapRegion->xTag;
|
||||
|
||||
@ -545,6 +577,13 @@ const HeapRegionTagged_t *pxHeapRegion;
|
||||
xDefinedRegions++;
|
||||
xRegIdx++;
|
||||
pxHeapRegion = &( pxHeapRegions[ xRegIdx ] );
|
||||
|
||||
#if (configENABLE_MEMORY_DEBUG == 1)
|
||||
{
|
||||
mem_init_dog(pxFirstFreeBlockInRegion);
|
||||
mem_init_dog(pxEnd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
xMinimumEverFreeBytesRemaining = xTotalHeapSize;
|
||||
@ -555,5 +594,12 @@ const HeapRegionTagged_t *pxHeapRegion;
|
||||
|
||||
/* Work out the position of the top bit in a size_t variable. */
|
||||
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
|
||||
|
||||
#if (configENABLE_MEMORY_DEBUG == 1)
|
||||
{
|
||||
mem_debug_init(uxHeapStructSize, &xStart, pxEnd, &xMallocMutex, xBlockAllocatedBit);
|
||||
mem_check_all(0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
193
components/freertos/heap_regions_debug.c
Normal file
193
components/freertos/heap_regions_debug.c
Normal file
@ -0,0 +1,193 @@
|
||||
#include "heap_regions_debug.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if (configENABLE_MEMORY_DEBUG == 1)
|
||||
|
||||
static os_block_t g_malloc_list, *g_free_list=NULL, *g_end;
|
||||
static size_t g_heap_struct_size;
|
||||
static mem_dbg_ctl_t g_mem_dbg;
|
||||
char g_mem_print = 0;
|
||||
static portMUX_TYPE *g_malloc_mutex = NULL;
|
||||
static unsigned int g_alloc_bit;
|
||||
#define MEM_DEBUG(...)
|
||||
|
||||
void mem_debug_init(size_t size, void *start, void *end, portMUX_TYPE *mutex, unsigned int alloc_bit)
|
||||
{
|
||||
MEM_DEBUG("size=%d start=%p end=%p mutex=%p alloc_bit=0x%x\n", size, start, end, mutex, alloc_bit);
|
||||
memset(&g_mem_dbg, 0, sizeof(g_mem_dbg));
|
||||
memset(&g_malloc_list, 0, sizeof(g_malloc_list));
|
||||
g_malloc_mutex = mutex;
|
||||
g_heap_struct_size = size;
|
||||
g_free_list = start;
|
||||
g_end = end;
|
||||
g_alloc_bit = alloc_bit;
|
||||
}
|
||||
|
||||
void mem_debug_push(char type, void *addr)
|
||||
{
|
||||
os_block_t *b = (os_block_t*)addr;
|
||||
debug_block_t *debug_b = DEBUG_BLOCK(b);
|
||||
|
||||
MEM_DEBUG("push type=%d addr=%p\n", type, addr);
|
||||
if (g_mem_print){
|
||||
if (type == DEBUG_TYPE_MALLOC){
|
||||
ets_printf("task=%s t=%s s=%u a=%p\n", debug_b->head.task?debug_b->head.task:"", type==DEBUG_TYPE_MALLOC?"m":"f", b->size&(~g_alloc_bit), addr);
|
||||
} else {
|
||||
ets_printf("task=%s t=%s s=%u a=%p\n", debug_b->head.task?debug_b->head.task:"", type==DEBUG_TYPE_MALLOC?"m":"f", b->size&(~g_alloc_bit), addr);
|
||||
}
|
||||
} else {
|
||||
mem_dbg_info_t *info = &g_mem_dbg.info[g_mem_dbg.cnt%DEBUG_MAX_INFO_NUM];
|
||||
|
||||
info->addr = addr;
|
||||
info->type = type;
|
||||
info->time = g_mem_dbg.cnt;
|
||||
g_mem_dbg.cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
void mem_debug_malloc_show(void)
|
||||
{
|
||||
os_block_t *b = g_malloc_list.next;
|
||||
debug_block_t *d;
|
||||
|
||||
taskENTER_CRITICAL(g_malloc_mutex);
|
||||
while (b){
|
||||
d = DEBUG_BLOCK(b);
|
||||
d->head.task[3] = '\0';
|
||||
ets_printf("t=%s s=%u a=%p\n", d->head.task?d->head.task:"", b->size&(~g_alloc_bit), b);
|
||||
b = b->next;
|
||||
}
|
||||
taskEXIT_CRITICAL(g_malloc_mutex);
|
||||
}
|
||||
|
||||
void mem_debug_show(void)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (!g_mem_print) return;
|
||||
|
||||
for (i=0; i<DEBUG_MAX_INFO_NUM; i++){
|
||||
ets_printf("%u %s %p\n", g_mem_dbg.info[i].time, g_mem_dbg.info[i].type == DEBUG_TYPE_FREE?"f":"m", g_mem_dbg.info[i].addr);
|
||||
}
|
||||
}
|
||||
|
||||
void mem_check_block(void* data)
|
||||
{
|
||||
debug_block_t *b = DEBUG_BLOCK(data);
|
||||
|
||||
MEM_DEBUG("check block data=%p\n", data);
|
||||
if (data && (HEAD_DOG(b) == DEBUG_DOG_VALUE)){
|
||||
if (TAIL_DOG(b) != DEBUG_DOG_VALUE){
|
||||
ets_printf("f task=%s a=%p h=%08x t=%08x\n", b->head.task?b->head.task:"", b, HEAD_DOG(b), TAIL_DOG(b));
|
||||
DOG_ASSERT();
|
||||
}
|
||||
} else {
|
||||
ets_printf("f task=%s a=%p h=%08x\n", b->head.task?b->head.task:"", b, HEAD_DOG(b));\
|
||||
DOG_ASSERT();
|
||||
}
|
||||
}
|
||||
|
||||
void mem_init_dog(void *data)
|
||||
{
|
||||
debug_block_t *b = DEBUG_BLOCK(data);
|
||||
xTaskHandle task;
|
||||
|
||||
MEM_DEBUG("init dog, data=%p debug_block=%p block_size=%x\n", data, b, b->os_block.size);
|
||||
if (!data) return;
|
||||
#if (INCLUDE_pcTaskGetTaskName == 1)
|
||||
task = xTaskGetCurrentTaskHandle();
|
||||
if (task){
|
||||
strncpy(b->head.task, pcTaskGetTaskName(task), 3);
|
||||
b->head.task[3] = '\0';
|
||||
}
|
||||
#else
|
||||
b->head.task = '\0';
|
||||
#endif
|
||||
HEAD_DOG(b) = DEBUG_DOG_VALUE;
|
||||
TAIL_DOG(b) = DEBUG_DOG_VALUE;
|
||||
}
|
||||
|
||||
void mem_check_all(void* pv)
|
||||
{
|
||||
os_block_t *b;
|
||||
|
||||
if (pv){
|
||||
char *puc = (char*)(pv);
|
||||
os_block_t *b;
|
||||
puc -= (g_heap_struct_size - BLOCK_TAIL_LEN - BLOCK_HEAD_LEN);
|
||||
b = (os_block_t*)puc;
|
||||
mem_check_block(b);
|
||||
}
|
||||
|
||||
taskENTER_CRITICAL(g_malloc_mutex);
|
||||
b = g_free_list->next;
|
||||
while(b && b != g_end){
|
||||
mem_check_block(b);
|
||||
ets_printf("check b=%p size=%d ok\n", b, b->size);
|
||||
b = b->next;
|
||||
}
|
||||
taskEXIT_CRITICAL(g_malloc_mutex);
|
||||
}
|
||||
|
||||
void mem_malloc_show(void)
|
||||
{
|
||||
os_block_t *b = g_malloc_list.next;
|
||||
debug_block_t *debug_b;
|
||||
|
||||
while (b){
|
||||
debug_b = DEBUG_BLOCK(b);
|
||||
ets_printf("%s %p %p %u\n", debug_b->head.task, debug_b, b, b->size&(~g_alloc_bit));
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
|
||||
void mem_malloc_block(void *data)
|
||||
{
|
||||
os_block_t *b = (os_block_t*)data;
|
||||
|
||||
MEM_DEBUG("mem malloc block data=%p, size=%u\n", data, b->size&(~g_alloc_bit));
|
||||
mem_debug_push(DEBUG_TYPE_MALLOC, data);
|
||||
|
||||
if (b){
|
||||
b->next = g_malloc_list.next;
|
||||
g_malloc_list.next = b;
|
||||
}
|
||||
}
|
||||
|
||||
void mem_free_block(void *data)
|
||||
{
|
||||
os_block_t *del = (os_block_t*)data;
|
||||
os_block_t *b = g_malloc_list.next;
|
||||
os_block_t *pre = &g_malloc_list;
|
||||
debug_block_t *debug_b;
|
||||
|
||||
MEM_DEBUG("mem free block data=%p, size=%d\n", data, del->size&(~g_alloc_bit));
|
||||
mem_debug_push(DEBUG_TYPE_FREE, data);
|
||||
|
||||
if (!del) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (b){
|
||||
if ( (del == b) ){
|
||||
pre->next = b->next;
|
||||
b->next = NULL;
|
||||
return;
|
||||
}
|
||||
pre = b;
|
||||
b = b->next;
|
||||
}
|
||||
|
||||
debug_b = DEBUG_BLOCK(del);
|
||||
ets_printf("%s %p %p %u already free\n", debug_b->head.task, debug_b, del, del->size&(~g_alloc_bit));
|
||||
mem_malloc_show();
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -188,8 +188,12 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef INCLUDE_pcTaskGetTaskName
|
||||
#if ( configENABLE_MEMORY_DEBUG == 1)
|
||||
#define INCLUDE_pcTaskGetTaskName 1
|
||||
#else
|
||||
#define INCLUDE_pcTaskGetTaskName 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef configUSE_APPLICATION_TASK_TAG
|
||||
#define configUSE_APPLICATION_TASK_TAG 0
|
||||
|
@ -222,6 +222,10 @@
|
||||
#define INCLUDE_vTaskDelay 1
|
||||
#define INCLUDE_uxTaskGetStackHighWaterMark 1
|
||||
|
||||
#ifndef configENABLE_MEMORY_DEBUG
|
||||
#define configENABLE_MEMORY_DEBUG 0
|
||||
#endif
|
||||
|
||||
#define INCLUDE_xSemaphoreGetMutexHolder 1
|
||||
|
||||
/* The priority at which the tick interrupt runs. This should probably be
|
||||
|
78
components/freertos/include/freertos/heap_regions_debug.h
Normal file
78
components/freertos/include/freertos/heap_regions_debug.h
Normal file
@ -0,0 +1,78 @@
|
||||
#ifndef _HEAP_REGION_DEBUG_H
|
||||
#define _HEAP_REGION_DEBUG_H
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
#if (configENABLE_MEMORY_DEBUG == 1)
|
||||
|
||||
#define DEBUG_DOG_VALUE 0x1a2b3c4d
|
||||
#define DEBUG_MAX_INFO_NUM 20
|
||||
#define DEBUG_TYPE_MALLOC 1
|
||||
#define DEBUG_TYPE_FREE 2
|
||||
|
||||
typedef struct {
|
||||
unsigned int dog;
|
||||
char task[4];
|
||||
unsigned int pc;
|
||||
}block_head_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned int dog;
|
||||
}block_tail_t;
|
||||
|
||||
/* Please keep this definition same as BlockLink_t */
|
||||
typedef struct _os_block_t {
|
||||
struct _os_block_t *next;
|
||||
size_t size;
|
||||
unsigned int xtag;
|
||||
}os_block_t;
|
||||
|
||||
typedef struct {
|
||||
block_head_t head;
|
||||
os_block_t os_block;
|
||||
}debug_block_t;
|
||||
|
||||
typedef struct _mem_dbg_info{
|
||||
void *addr;
|
||||
char *task;
|
||||
uint32_t pc;
|
||||
uint32_t time;
|
||||
uint8_t type;
|
||||
}mem_dbg_info_t;
|
||||
|
||||
typedef struct _mem_dbg_ctl{
|
||||
mem_dbg_info_t info[DEBUG_MAX_INFO_NUM];
|
||||
uint32_t cnt;
|
||||
}mem_dbg_ctl_t;
|
||||
|
||||
#define BLOCK_HEAD_LEN sizeof(block_head_t)
|
||||
#define BLOCK_TAIL_LEN sizeof(block_tail_t)
|
||||
#define OS_BLOCK(_b) ((os_block_t*)((debug_block_t*)((char*)(_b) + BLOCK_HEAD_LEN)))
|
||||
#define DEBUG_BLOCK(_b) ((debug_block_t*)((char*)(_b) - BLOCK_HEAD_LEN))
|
||||
#define HEAD_DOG(_b) ((_b)->head.dog)
|
||||
#define TAIL_DOG(_b) (*(unsigned int*)((char*)(_b) + (((_b)->os_block.size & (~g_alloc_bit) ) - BLOCK_TAIL_LEN)))
|
||||
|
||||
#define DOG_ASSERT()\
|
||||
{\
|
||||
mem_debug_show();\
|
||||
abort();\
|
||||
}
|
||||
|
||||
extern void mem_check_block(void * data);
|
||||
extern void mem_init_dog(void *data);
|
||||
extern void mem_debug_init(size_t size, void *start, void *end, portMUX_TYPE *mutex, unsigned int alloc_bit);
|
||||
extern void mem_malloc_block(void *data);
|
||||
extern void mem_free_block(void *data);
|
||||
extern void mem_check_all(void* pv);
|
||||
|
||||
#else
|
||||
|
||||
#define mem_check_block(...)
|
||||
#define mem_init_dog(...)
|
||||
|
||||
#define BLOCK_HEAD_LEN 0
|
||||
#define BLOCK_TAIL_LEN 0
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user