Merge branch 'feature/memory_canaries_temp' into 'master'

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

See merge request !64
This commit is contained in:
Angus Gratton 2016-09-08 11:20:17 +08:00
commit e9b73e5c0d
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
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

View file

@ -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
}

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
#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

View file

@ -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

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