diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index e6da7777c8..d7c306c246 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -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 diff --git a/components/freertos/heap_regions.c b/components/freertos/heap_regions.c index 491a1fe6eb..1abcdf3c6f 100644 --- a/components/freertos/heap_regions.c +++ b/components/freertos/heap_regions.c @@ -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 } diff --git a/components/freertos/heap_regions_debug.c b/components/freertos/heap_regions_debug.c new file mode 100644 index 0000000000..d8d444a537 --- /dev/null +++ b/components/freertos/heap_regions_debug.c @@ -0,0 +1,193 @@ +#include "heap_regions_debug.h" +#include "FreeRTOS.h" +#include "task.h" +#include +#include +#include + +#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; ihead.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 + + diff --git a/components/freertos/include/freertos/FreeRTOS.h b/components/freertos/include/freertos/FreeRTOS.h index ff76db27ec..04b39b65e9 100644 --- a/components/freertos/include/freertos/FreeRTOS.h +++ b/components/freertos/include/freertos/FreeRTOS.h @@ -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 diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index e95abaa9cc..95bf1e103a 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -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 diff --git a/components/freertos/include/freertos/heap_regions_debug.h b/components/freertos/include/freertos/heap_regions_debug.h new file mode 100644 index 0000000000..81bf1d6c3b --- /dev/null +++ b/components/freertos/include/freertos/heap_regions_debug.h @@ -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