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:
liuzhifu 2016-09-07 21:52:24 +08:00
parent 1a6e63feef
commit b21d2dfa6b
6 changed files with 339 additions and 8 deletions

View File

@ -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 If JTAG/OCD is connected, stop execution when the scheduler is started and the first
task is executed. 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 menuconfig FREERTOS_DEBUG_INTERNALS
bool "Debug FreeRTOS internals" bool "Debug FreeRTOS internals"
default n default n

View File

@ -132,6 +132,7 @@ task.h is included from an application file. */
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "task.h" #include "task.h"
#include "heap_regions_debug.h"
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE #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 /* The size of the structure placed at the beginning of each allocated memory
block must by correctly byte aligned. */ 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. */ /* Create a couple of list links to mark the start and end of the list. */
static BlockLink_t xStart, *pxEnd = NULL; static BlockLink_t xStart, *pxEnd = NULL;
@ -238,6 +239,13 @@ void *pvReturn = NULL;
while( ( ( pxBlock->xTag != tag ) || ( pxBlock->xBlockSize < xWantedSize ) ) && ( pxBlock->pxNextFreeBlock != NULL ) ) while( ( ( pxBlock->xTag != tag ) || ( pxBlock->xBlockSize < xWantedSize ) ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{ {
// ets_printf("Block %x -> %x\n", (uint32_t)pxBlock, (uint32_t)pxBlock->pxNextFreeBlock); // 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; pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock; pxBlock = pxBlock->pxNextFreeBlock;
} }
@ -248,7 +256,7 @@ void *pvReturn = NULL;
{ {
/* Return the memory space pointed to - jumping over the /* Return the memory space pointed to - jumping over the
BlockLink_t structure at its start. */ 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 /* This block is being returned for use so must be taken out
of the list of free blocks. */ 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 /* If the block is larger than required it can be split into
two. */ two. */
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
{ {
/* This block is to be split into two. Create a new /* This block is to be split into two. Create a new
block following the number of bytes requested. The void block following the number of bytes requested. The void
cast is used to prevent byte alignment warnings from the cast is used to prevent byte alignment warnings from the
compiler. */ compiler. */
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize);
/* Calculate the sizes of two blocks split from the /* Calculate the sizes of two blocks split from the
single block. */ single block. */
@ -270,6 +279,13 @@ void *pvReturn = NULL;
pxNewBlockLink->xTag = tag; pxNewBlockLink->xTag = tag;
pxBlock->xBlockSize = xWantedSize; pxBlock->xBlockSize = xWantedSize;
#if (configENABLE_MEMORY_DEBUG == 1)
{
mem_init_dog(pxNewBlockLink);
}
#endif
/* Insert the new block into the list of free blocks. */ /* Insert the new block into the list of free blocks. */
prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );
} }
@ -293,6 +309,13 @@ void *pvReturn = NULL;
by the application and has no "next" block. */ by the application and has no "next" block. */
pxBlock->xBlockSize |= xBlockAllocatedBit; pxBlock->xBlockSize |= xBlockAllocatedBit;
pxBlock->pxNextFreeBlock = NULL; pxBlock->pxNextFreeBlock = NULL;
#if (configENABLE_MEMORY_DEBUG == 1)
{
mem_init_dog(pxBlock);
mem_malloc_block(pxBlock);
}
#endif
} }
else else
{ {
@ -340,11 +363,20 @@ BlockLink_t *pxLink;
{ {
/* The memory being freed will have an BlockLink_t structure immediately /* The memory being freed will have an BlockLink_t structure immediately
before it. */ before it. */
puc -= uxHeapStructSize; puc -= (uxHeapStructSize - BLOCK_TAIL_LEN - BLOCK_HEAD_LEN) ;
/* This casting is to keep the compiler from issuing warnings. */ /* This casting is to keep the compiler from issuing warnings. */
pxLink = ( void * ) puc; 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. */ /* Check the block is actually allocated. */
configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ); configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
configASSERT( pxLink->pxNextFreeBlock == NULL ); 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 /* 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. */ 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; xStart.xBlockSize = ( size_t ) 0;
} }
else else
@ -519,7 +551,7 @@ const HeapRegionTagged_t *pxHeapRegion;
ulAddress = ( ( uint32_t ) pucAlignedHeap ) + xTotalRegionSize; ulAddress = ( ( uint32_t ) pucAlignedHeap ) + xTotalRegionSize;
ulAddress -= uxHeapStructSize; ulAddress -= uxHeapStructSize;
ulAddress &= ~portBYTE_ALIGNMENT_MASK; ulAddress &= ~portBYTE_ALIGNMENT_MASK;
pxEnd = ( BlockLink_t * ) ulAddress; pxEnd = ( BlockLink_t * ) (ulAddress + BLOCK_HEAD_LEN);
pxEnd->xBlockSize = 0; pxEnd->xBlockSize = 0;
pxEnd->pxNextFreeBlock = NULL; pxEnd->pxNextFreeBlock = NULL;
pxEnd->xTag = -1; 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 /* 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 sized to take up the entire heap region minus the space taken by the
free block structure. */ free block structure. */
pxFirstFreeBlockInRegion = ( BlockLink_t * ) pucAlignedHeap; pxFirstFreeBlockInRegion = ( BlockLink_t * ) (pucAlignedHeap + BLOCK_HEAD_LEN);
pxFirstFreeBlockInRegion->xBlockSize = ulAddress - ( uint32_t ) pxFirstFreeBlockInRegion; pxFirstFreeBlockInRegion->xBlockSize = ulAddress - ( uint32_t ) pxFirstFreeBlockInRegion + BLOCK_HEAD_LEN;
pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd; pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd;
pxFirstFreeBlockInRegion->xTag=pxHeapRegion->xTag; pxFirstFreeBlockInRegion->xTag=pxHeapRegion->xTag;
@ -545,6 +577,13 @@ const HeapRegionTagged_t *pxHeapRegion;
xDefinedRegions++; xDefinedRegions++;
xRegIdx++; xRegIdx++;
pxHeapRegion = &( pxHeapRegions[ xRegIdx ] ); pxHeapRegion = &( pxHeapRegions[ xRegIdx ] );
#if (configENABLE_MEMORY_DEBUG == 1)
{
mem_init_dog(pxFirstFreeBlockInRegion);
mem_init_dog(pxEnd);
}
#endif
} }
xMinimumEverFreeBytesRemaining = xTotalHeapSize; xMinimumEverFreeBytesRemaining = xTotalHeapSize;
@ -555,5 +594,12 @@ const HeapRegionTagged_t *pxHeapRegion;
/* Work out the position of the top bit in a size_t variable. */ /* Work out the position of the top bit in a size_t variable. */
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ); 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
} }

View 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

View File

@ -188,8 +188,12 @@ extern "C" {
#endif #endif
#ifndef INCLUDE_pcTaskGetTaskName #ifndef INCLUDE_pcTaskGetTaskName
#if ( configENABLE_MEMORY_DEBUG == 1)
#define INCLUDE_pcTaskGetTaskName 1
#else
#define INCLUDE_pcTaskGetTaskName 0 #define INCLUDE_pcTaskGetTaskName 0
#endif #endif
#endif
#ifndef configUSE_APPLICATION_TASK_TAG #ifndef configUSE_APPLICATION_TASK_TAG
#define configUSE_APPLICATION_TASK_TAG 0 #define configUSE_APPLICATION_TASK_TAG 0

View File

@ -222,6 +222,10 @@
#define INCLUDE_vTaskDelay 1 #define INCLUDE_vTaskDelay 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1 #define INCLUDE_uxTaskGetStackHighWaterMark 1
#ifndef configENABLE_MEMORY_DEBUG
#define configENABLE_MEMORY_DEBUG 0
#endif
#define INCLUDE_xSemaphoreGetMutexHolder 1 #define INCLUDE_xSemaphoreGetMutexHolder 1
/* The priority at which the tick interrupt runs. This should probably be /* The priority at which the tick interrupt runs. This should probably be

View 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