Add xPortGetFreeHeapSizeCaps and xPortGetMinimumEverFreeHeapSizeCaps plus everything it entails. Allows querying the available memory for various capabilities. Also: xPortGetFreeHeapSize and xPortGetMinimumEverFreeHeapSize now return the expected value. Bonus: the linked list used in the allocator is now 4 bytes smaller, which should save some memory.

This commit is contained in:
Jeroen Domburg 2016-12-09 17:13:45 +08:00 committed by Jeroen Domburg
parent 3f3cf397f7
commit 293ad4cd36
6 changed files with 175 additions and 137 deletions

View file

@ -43,6 +43,7 @@ typedef struct {
/* /*
Tag descriptors. These describe the capabilities of a bit of memory that's tagged with the index into this table. Tag descriptors. These describe the capabilities of a bit of memory that's tagged with the index into this table.
Each tag contains NO_PRIOS entries; later entries are only taken if earlier ones can't fulfill the memory request. Each tag contains NO_PRIOS entries; later entries are only taken if earlier ones can't fulfill the memory request.
Make sure there are never more than HEAPREGIONS_MAX_TAGCOUNT (in heap_regions.h) tags (ex the last empty marker)
*/ */
static const tag_desc_t tag_desc[]={ static const tag_desc_t tag_desc[]={
{ "DRAM", { MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT, 0 }}, //Tag 0: Plain ole D-port RAM { "DRAM", { MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT, 0 }}, //Tag 0: Plain ole D-port RAM
@ -268,3 +269,52 @@ void *pvPortMallocCaps( size_t xWantedSize, uint32_t caps )
return NULL; return NULL;
} }
size_t xPortGetFreeHeapSizeCaps( uint32_t caps )
{
int prio;
int tag;
size_t ret=0;
for (prio=0; prio<NO_PRIOS; prio++) {
//Iterate over tag descriptors for this priority
for (tag=0; tag_desc[tag].prio[prio]!=MALLOC_CAP_INVALID; tag++) {
if ((tag_desc[tag].prio[prio]&caps)!=0) {
ret+=xPortGetFreeHeapSizeTagged(tag);
}
}
}
return ret;
}
size_t xPortGetMinimumEverFreeHeapSizeCaps( uint32_t caps )
{
int prio;
int tag;
size_t ret=0;
for (prio=0; prio<NO_PRIOS; prio++) {
//Iterate over tag descriptors for this priority
for (tag=0; tag_desc[tag].prio[prio]!=MALLOC_CAP_INVALID; tag++) {
if ((tag_desc[tag].prio[prio]&caps)!=0) {
ret+=xPortGetMinimumEverFreeHeapSizeTagged(tag);
}
}
}
return ret;
}
size_t xPortGetFreeHeapSize( void )
{
return xPortGetFreeHeapSizeCaps( MALLOC_CAP_8BIT );
}
size_t xPortGetMinimumEverFreeHeapSize( void )
{
return xPortGetMinimumEverFreeHeapSizeCaps( MALLOC_CAP_8BIT );
}

View file

@ -30,5 +30,7 @@
void heap_alloc_caps_init(); void heap_alloc_caps_init();
void *pvPortMallocCaps(size_t xWantedSize, uint32_t caps); void *pvPortMallocCaps(size_t xWantedSize, uint32_t caps);
size_t xPortGetFreeHeapSizeCaps( uint32_t caps );
size_t xPortGetMinimumEverFreeHeapSizeCaps( uint32_t caps );
#endif #endif

View file

@ -148,11 +148,13 @@ task.h is included from an application file. */
/* Define the linked list structure. This is used to link free blocks in order /* Define the linked list structure. This is used to link free blocks in order
of their memory address. */ of their memory address. */
/* This is optimized and assumes a region is never larger than 16MiB. */
typedef struct A_BLOCK_LINK typedef struct A_BLOCK_LINK
{ {
struct A_BLOCK_LINK *pxNextFreeBlock; /*<< The next free block in the list. */ struct A_BLOCK_LINK *pxNextFreeBlock; /*<< The next free block in the list. */
size_t xBlockSize; /*<< The size of the free block. */ int xBlockSize: 24; /*<< The size of the free block. */
BaseType_t xTag; /*<< Tag of this region */ int xTag: 7; /*<< Tag of this region */
int xAllocated: 1; /*<< 1 if allocated */
} BlockLink_t; } BlockLink_t;
//Mux to protect the memory status data //Mux to protect the memory status data
@ -179,14 +181,9 @@ static BlockLink_t xStart, *pxEnd = NULL;
/* Keeps track of the number of free bytes remaining, but says nothing about /* Keeps track of the number of free bytes remaining, but says nothing about
fragmentation. */ fragmentation. */
static size_t xFreeBytesRemaining = 0; static size_t xFreeBytesRemaining[HEAPREGIONS_MAX_TAGCOUNT] = {0};
static size_t xMinimumEverFreeBytesRemaining = 0; static size_t xMinimumEverFreeBytesRemaining[HEAPREGIONS_MAX_TAGCOUNT] = {0};
/* Gets set to the top bit of an size_t type. When this bit in the xBlockSize
member of an BlockLink_t structure is set then the block belongs to the
application. When the bit is free the block is still part of the free heap
space. */
static size_t xBlockAllocatedBit = 0;
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
@ -200,12 +197,6 @@ void *pvReturn = NULL;
configASSERT( pxEnd ); configASSERT( pxEnd );
taskENTER_CRITICAL(&xMallocMutex); taskENTER_CRITICAL(&xMallocMutex);
{
/* Check the requested block size is not so large that the top bit is
set. The top bit of the block size member of the BlockLink_t structure
is used to determine who owns the block - the application or the
kernel, so it must be free. */
if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
{ {
/* The wanted size is increased so it can contain a BlockLink_t /* The wanted size is increased so it can contain a BlockLink_t
structure in addition to the requested amount of bytes. */ structure in addition to the requested amount of bytes. */
@ -230,7 +221,7 @@ void *pvReturn = NULL;
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
} }
if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) ) if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining[ tag ] ) )
{ {
/* Traverse the list from the start (lowest address) block until /* Traverse the list from the start (lowest address) block until
one of adequate size is found. */ one of adequate size is found. */
@ -294,11 +285,11 @@ void *pvReturn = NULL;
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
} }
xFreeBytesRemaining -= pxBlock->xBlockSize; xFreeBytesRemaining[ tag ] -= pxBlock->xBlockSize;
if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining ) if( xFreeBytesRemaining[ tag ] < xMinimumEverFreeBytesRemaining[ tag ] )
{ {
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; xMinimumEverFreeBytesRemaining[ tag ] = xFreeBytesRemaining[ tag ];
} }
else else
{ {
@ -307,7 +298,7 @@ void *pvReturn = NULL;
/* The block is being returned - it is allocated and owned /* The block is being returned - it is allocated and owned
by the application and has no "next" block. */ by the application and has no "next" block. */
pxBlock->xBlockSize |= xBlockAllocatedBit; pxBlock->xAllocated = 1;
pxBlock->pxNextFreeBlock = NULL; pxBlock->pxNextFreeBlock = NULL;
#if (configENABLE_MEMORY_DEBUG == 1) #if (configENABLE_MEMORY_DEBUG == 1)
@ -326,11 +317,6 @@ void *pvReturn = NULL;
{ {
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
} }
}
else
{
mtCOVERAGE_TEST_MARKER();
}
traceMALLOC( pvReturn, xWantedSize ); traceMALLOC( pvReturn, xWantedSize );
} }
@ -378,21 +364,21 @@ BlockLink_t *pxLink;
#endif #endif
/* Check the block is actually allocated. */ /* Check the block is actually allocated. */
configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ); configASSERT( ( pxLink->xAllocated ) != 0 );
configASSERT( pxLink->pxNextFreeBlock == NULL ); configASSERT( pxLink->pxNextFreeBlock == NULL );
if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ) if( pxLink->xAllocated != 0 )
{ {
if( pxLink->pxNextFreeBlock == NULL ) if( pxLink->pxNextFreeBlock == NULL )
{ {
/* The block is being returned to the heap - it is no longer /* The block is being returned to the heap - it is no longer
allocated. */ allocated. */
pxLink->xBlockSize &= ~xBlockAllocatedBit; pxLink->xAllocated = 0;
taskENTER_CRITICAL(&xMallocMutex); taskENTER_CRITICAL(&xMallocMutex);
{ {
/* Add this block to the list of free blocks. */ /* Add this block to the list of free blocks. */
xFreeBytesRemaining += pxLink->xBlockSize; xFreeBytesRemaining[ pxLink->xTag ] += pxLink->xBlockSize;
traceFREE( pv, pxLink->xBlockSize ); traceFREE( pv, pxLink->xBlockSize );
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
} }
@ -411,15 +397,15 @@ BlockLink_t *pxLink;
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
size_t xPortGetFreeHeapSize( void ) size_t xPortGetFreeHeapSizeTagged( BaseType_t tag )
{ {
return xFreeBytesRemaining; return xFreeBytesRemaining[ tag ];
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
size_t xPortGetMinimumEverFreeHeapSize( void ) size_t xPortGetMinimumEverFreeHeapSizeTagged( BaseType_t tag )
{ {
return xMinimumEverFreeBytesRemaining; return xMinimumEverFreeBytesRemaining[ tag ];
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
@ -509,6 +495,7 @@ const HeapRegionTagged_t *pxHeapRegion;
continue; continue;
} }
configASSERT(pxHeapRegion->xTag < HEAPREGIONS_MAX_TAGCOUNT);
xTotalRegionSize = pxHeapRegion->xSizeInBytes; xTotalRegionSize = pxHeapRegion->xSizeInBytes;
/* Ensure the heap region starts on a correctly aligned boundary. */ /* Ensure the heap region starts on a correctly aligned boundary. */
@ -572,6 +559,8 @@ const HeapRegionTagged_t *pxHeapRegion;
} }
xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize; xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize;
xMinimumEverFreeBytesRemaining[ pxHeapRegion->xTag ] += pxFirstFreeBlockInRegion->xBlockSize;
xFreeBytesRemaining[ pxHeapRegion->xTag ] += pxFirstFreeBlockInRegion->xBlockSize;
/* Move onto the next HeapRegionTagged_t structure. */ /* Move onto the next HeapRegionTagged_t structure. */
xDefinedRegions++; xDefinedRegions++;
@ -586,14 +575,9 @@ const HeapRegionTagged_t *pxHeapRegion;
#endif #endif
} }
xMinimumEverFreeBytesRemaining = xTotalHeapSize;
xFreeBytesRemaining = xTotalHeapSize;
/* Check something was actually defined before it is accessed. */ /* Check something was actually defined before it is accessed. */
configASSERT( xTotalHeapSize ); configASSERT( xTotalHeapSize );
/* 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) #if (configENABLE_MEMORY_DEBUG == 1)
{ {

View file

@ -12,19 +12,17 @@ static size_t g_heap_struct_size;
static mem_dbg_ctl_t g_mem_dbg; static mem_dbg_ctl_t g_mem_dbg;
char g_mem_print = 0; char g_mem_print = 0;
static portMUX_TYPE *g_malloc_mutex = NULL; static portMUX_TYPE *g_malloc_mutex = NULL;
static unsigned int g_alloc_bit;
#define MEM_DEBUG(...) #define MEM_DEBUG(...)
void mem_debug_init(size_t size, void *start, void *end, portMUX_TYPE *mutex, unsigned int alloc_bit) void mem_debug_init(size_t size, void *start, void *end, portMUX_TYPE *mutex)
{ {
MEM_DEBUG("size=%d start=%p end=%p mutex=%p alloc_bit=0x%x\n", size, start, end, mutex, alloc_bit); MEM_DEBUG("size=%d start=%p end=%p mutex=%p%x\n", size, start, end, mutex);
memset(&g_mem_dbg, 0, sizeof(g_mem_dbg)); memset(&g_mem_dbg, 0, sizeof(g_mem_dbg));
memset(&g_malloc_list, 0, sizeof(g_malloc_list)); memset(&g_malloc_list, 0, sizeof(g_malloc_list));
g_malloc_mutex = mutex; g_malloc_mutex = mutex;
g_heap_struct_size = size; g_heap_struct_size = size;
g_free_list = start; g_free_list = start;
g_end = end; g_end = end;
g_alloc_bit = alloc_bit;
} }
void mem_debug_push(char type, void *addr) void mem_debug_push(char type, void *addr)
@ -35,9 +33,9 @@ void mem_debug_push(char type, void *addr)
MEM_DEBUG("push type=%d addr=%p\n", type, addr); MEM_DEBUG("push type=%d addr=%p\n", type, addr);
if (g_mem_print){ if (g_mem_print){
if (type == DEBUG_TYPE_MALLOC){ 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); 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, addr);
} else { } 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); 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, addr);
} }
} else { } else {
mem_dbg_info_t *info = &g_mem_dbg.info[g_mem_dbg.cnt%DEBUG_MAX_INFO_NUM]; mem_dbg_info_t *info = &g_mem_dbg.info[g_mem_dbg.cnt%DEBUG_MAX_INFO_NUM];
@ -58,7 +56,7 @@ void mem_debug_malloc_show(void)
while (b){ while (b){
d = DEBUG_BLOCK(b); d = DEBUG_BLOCK(b);
d->head.task[3] = '\0'; 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); ets_printf("t=%s s=%u a=%p\n", d->head.task?d->head.task:"", b->size, b);
b = b->next; b = b->next;
} }
taskEXIT_CRITICAL(g_malloc_mutex); taskEXIT_CRITICAL(g_malloc_mutex);
@ -140,7 +138,7 @@ void mem_malloc_show(void)
while (b){ while (b){
debug_b = DEBUG_BLOCK(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)); ets_printf("%s %p %p %u\n", debug_b->head.task, debug_b, b, b->size);
b = b->next; b = b->next;
} }
} }
@ -149,7 +147,7 @@ void mem_malloc_block(void *data)
{ {
os_block_t *b = (os_block_t*)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("mem malloc block data=%p, size=%u\n", data, b->size);
mem_debug_push(DEBUG_TYPE_MALLOC, data); mem_debug_push(DEBUG_TYPE_MALLOC, data);
if (b){ if (b){
@ -165,7 +163,7 @@ void mem_free_block(void *data)
os_block_t *pre = &g_malloc_list; os_block_t *pre = &g_malloc_list;
debug_block_t *debug_b; debug_block_t *debug_b;
MEM_DEBUG("mem free block data=%p, size=%d\n", data, del->size&(~g_alloc_bit)); MEM_DEBUG("mem free block data=%p, size=%d\n", data, del->size);
mem_debug_push(DEBUG_TYPE_FREE, data); mem_debug_push(DEBUG_TYPE_FREE, data);
if (!del) { if (!del) {
@ -183,7 +181,7 @@ void mem_free_block(void *data)
} }
debug_b = DEBUG_BLOCK(del); 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)); ets_printf("%s %p %p %u already free\n", debug_b->head.task, debug_b, del, del->size);
mem_malloc_show(); mem_malloc_show();
abort(); abort();
} }

View file

@ -16,6 +16,9 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
/* The maximum amount of tags in use */
#define HEAPREGIONS_MAX_TAGCOUNT 16
typedef struct HeapRegionTagged typedef struct HeapRegionTagged
{ {
@ -28,7 +31,8 @@ typedef struct HeapRegionTagged
void vPortDefineHeapRegionsTagged( const HeapRegionTagged_t * const pxHeapRegions ); void vPortDefineHeapRegionsTagged( const HeapRegionTagged_t * const pxHeapRegions );
void *pvPortMallocTagged( size_t xWantedSize, BaseType_t tag ); void *pvPortMallocTagged( size_t xWantedSize, BaseType_t tag );
size_t xPortGetMinimumEverFreeHeapSizeTagged( BaseType_t tag );
size_t xPortGetFreeHeapSizeTagged( BaseType_t tag );
#endif #endif

View file

@ -60,7 +60,7 @@ typedef struct _mem_dbg_ctl{
extern void mem_check_block(void * data); extern void mem_check_block(void * data);
extern void mem_init_dog(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_debug_init(size_t size, void *start, void *end, portMUX_TYPE *mutex);
extern void mem_malloc_block(void *data); extern void mem_malloc_block(void *data);
extern void mem_free_block(void *data); extern void mem_free_block(void *data);
extern void mem_check_all(void* pv); extern void mem_check_all(void* pv);