Преглед на файлове

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
liuzhifu преди 9 години
родител
ревизия
b21d2dfa6b

+ 6 - 0
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

+ 54 - 8
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
 }
 

+ 193 - 0
components/freertos/heap_regions_debug.c

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

+ 4 - 0
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

+ 4 - 0
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

+ 78 - 0
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