1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-22 21:23:07 +03:00
M Hightower d3eddeb501
Ensure xPortGetFreeHeapSize reports DRAM (#8680)
Create dedicated function for xPortGetFreeHeapSize() that only reports on DRAM.
NONOS SDK API system_get_free_heap_size() relies on xPortGetFreeHeapSize() for the free Heap size.

Possible breaking change for multiple Heap Sketches calling system_get_free_heap_size(); it will now always report free DRAM Heap size.

Update and export umm_free_heap_size_lw() to report the free Heap size of the current Heap.
Updated ESP.getFreeHeap() to use umm_free_heap_size_lw().

Updated build options to supply exported umm_free_heap_size_lw() via either UMM_STATS or UMM_INFO.

Improved build option support via the SketchName.ino.globals.h method for Heap options: UMM_INFO, UMM_INLINE_METRICS, UMM_STATS, UMM_STATS_FULL, UMM_BEST_FIT, and UMM_FIRST_FIT. While uncommon to change from the defaults, you can review umm_malloc_cfgport.h for more details, which may help reduce your Sketch's size in dire situations. Assuming you are willing to give up some functionality.
For debugging UMM_STATS_FULL can offer additional stats, like Heap low water mark (umm_free_heap_size_min()).
2022-10-11 14:52:39 +03:00

275 lines
9.8 KiB
C

#if defined(BUILD_UMM_MALLOC_C)
#ifdef UMM_INFO
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <math.h>
/* ----------------------------------------------------------------------------
* One of the coolest things about this little library is that it's VERY
* easy to get debug information about the memory heap by simply iterating
* through all of the memory blocks.
*
* As you go through all the blocks, you can check to see if it's a free
* block by looking at the high order bit of the next block index. You can
* also see how big the block is by subtracting the next block index from
* the current block number.
*
* The umm_info function does all of that and makes the results available
* in the ummHeapInfo structure.
* ----------------------------------------------------------------------------
*/
// UMM_HEAP_INFO ummHeapInfo;
void *umm_info(void *ptr, bool force) {
UMM_CRITICAL_DECL(id_info);
UMM_CHECK_INITIALIZED();
uint16_t blockNo = 0;
/* Protect the critical section... */
UMM_CRITICAL_ENTRY(id_info);
umm_heap_context_t *_context = umm_get_current_heap();
/*
* Clear out all of the entries in the ummHeapInfo structure before doing
* any calculations..
*/
memset(&_context->info, 0, sizeof(_context->info));
DBGLOG_FORCE(force, "\n");
DBGLOG_FORCE(force, "+----------+-------+--------+--------+-------+--------+--------+\n");
DBGLOG_FORCE(force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n",
DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)),
blockNo,
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
UMM_PBLOCK(blockNo),
(UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK) - blockNo,
UMM_NFREE(blockNo),
UMM_PFREE(blockNo));
/*
* Now loop through the block lists, and keep track of the number and size
* of used and free blocks. The terminating condition is an nb pointer with
* a value of zero...
*/
blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK;
while (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK) {
size_t curBlocks = (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK) - blockNo;
++_context->info.totalEntries;
_context->info.totalBlocks += curBlocks;
/* Is this a free block? */
if (UMM_NBLOCK(blockNo) & UMM_FREELIST_MASK) {
++_context->info.freeEntries;
_context->info.freeBlocks += curBlocks;
_context->info.freeBlocksSquared += (curBlocks * curBlocks);
if (_context->info.maxFreeContiguousBlocks < curBlocks) {
_context->info.maxFreeContiguousBlocks = curBlocks;
}
DBGLOG_FORCE(force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|NF %5d|PF %5d|\n",
DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)),
blockNo,
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
UMM_PBLOCK(blockNo),
(uint16_t)curBlocks,
UMM_NFREE(blockNo),
UMM_PFREE(blockNo));
/* Does this block address match the ptr we may be trying to free? */
if (ptr == &UMM_BLOCK(blockNo)) {
/* Release the critical section... */
UMM_CRITICAL_EXIT(id_info);
return ptr;
}
} else {
++_context->info.usedEntries;
_context->info.usedBlocks += curBlocks;
DBGLOG_FORCE(force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|\n",
DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)),
blockNo,
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
UMM_PBLOCK(blockNo),
(uint16_t)curBlocks);
}
blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK;
}
/*
* The very last block is used as a placeholder to indicate that
* there are no more blocks in the heap, so it cannot be used
* for anything - at the same time, the size of this block must
* ALWAYS be exactly 1 !
*/
DBGLOG_FORCE(force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n",
DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)),
blockNo,
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
UMM_PBLOCK(blockNo),
UMM_NUMBLOCKS - blockNo,
UMM_NFREE(blockNo),
UMM_PFREE(blockNo));
DBGLOG_FORCE(force, "+----------+-------+--------+--------+-------+--------+--------+\n");
DBGLOG_FORCE(force, "Total Entries %5d Used Entries %5d Free Entries %5d\n",
_context->info.totalEntries,
_context->info.usedEntries,
_context->info.freeEntries);
DBGLOG_FORCE(force, "Total Blocks %5d Used Blocks %5d Free Blocks %5d\n",
_context->info.totalBlocks,
_context->info.usedBlocks,
_context->info.freeBlocks);
DBGLOG_FORCE(force, "+--------------------------------------------------------------+\n");
DBGLOG_FORCE(force, "Usage Metric: %5d\n", umm_usage_metric_core(_context));
DBGLOG_FORCE(force, "Fragmentation Metric: %5d\n", umm_fragmentation_metric_core(_context));
DBGLOG_FORCE(force, "+--------------------------------------------------------------+\n");
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
#if !defined(UMM_INLINE_METRICS)
if (_context->info.freeBlocks == _context->stats.free_blocks) {
DBGLOG_FORCE(force, "heap info Free blocks and heap statistics Free blocks match.\n");
} else {
DBGLOG_FORCE(force, "\nheap info Free blocks %5d != heap statistics Free Blocks %5d\n\n",
_context->info.freeBlocks,
_context->stats.free_blocks);
}
DBGLOG_FORCE(force, "+--------------------------------------------------------------+\n");
#endif
umm_print_stats(force);
#endif
/* Release the critical section... */
UMM_CRITICAL_EXIT(id_info);
return NULL;
}
/* ------------------------------------------------------------------------ */
size_t umm_free_heap_size_core(umm_heap_context_t *_context) {
return (size_t)_context->info.freeBlocks * sizeof(umm_block);
}
/*
When used as the fallback option for supporting exported function
`umm_free_heap_size_lw()`, the build option UMM_INLINE_METRICS is required.
Otherwise, umm_info() would be used to complete the operation, which uses a
time-consuming method for getting free Heap and runs with interrupts off,
which can negatively impact WiFi operations. Also, it cannot support calls
from ISRs, `umm_info()` runs from flash.
*/
size_t umm_free_heap_size(void) {
#ifndef UMM_INLINE_METRICS
umm_info(NULL, false);
#endif
return umm_free_heap_size_core(umm_get_current_heap());
}
// C Breaking change in upstream umm_max_block_size() was changed to
// C umm_max_free_block_size() keeping old function name for (dot) releases.
// C TODO: update at next major release.
// C size_t umm_max_free_block_size( void ) {
size_t umm_max_block_size_core(umm_heap_context_t *_context) {
return _context->info.maxFreeContiguousBlocks * sizeof(umm_block);
}
size_t umm_max_block_size(void) {
umm_info(NULL, false);
return umm_max_block_size_core(umm_get_current_heap());
}
/*
Without build option UMM_INLINE_METRICS, calls to umm_usage_metric() or
umm_fragmentation_metric() must to be preceded by a call to umm_info(NULL, false)
for updated results.
*/
int umm_usage_metric_core(umm_heap_context_t *_context) {
// C Note, umm_metrics also appears in the upstrean w/o definition. I suspect it is suppose to be ummHeapInfo.
// DBGLOG_DEBUG( "usedBlocks %d totalBlocks %d\n", umm_metrics.usedBlocks, ummHeapInfo.totalBlocks);
DBGLOG_DEBUG("usedBlocks %d totalBlocks %d\n", _context->info.usedBlocks, _context->info.totalBlocks);
if (_context->info.freeBlocks) {
return (int)((_context->info.usedBlocks * 100) / (_context->info.freeBlocks));
}
return -1; // no freeBlocks
}
int umm_usage_metric(void) {
#ifndef UMM_INLINE_METRICS
umm_info(NULL, false);
#endif
return umm_usage_metric_core(umm_get_current_heap());
}
uint32_t sqrt32(uint32_t n);
int umm_fragmentation_metric_core(umm_heap_context_t *_context) {
// DBGLOG_DEBUG( "freeBlocks %d freeBlocksSquared %d\n", umm_metrics.freeBlocks, ummHeapInfo.freeBlocksSquared);
DBGLOG_DEBUG("freeBlocks %d freeBlocksSquared %d\n", _context->info.freeBlocks, _context->info.freeBlocksSquared);
if (0 == _context->info.freeBlocks) {
return 0;
} else {
// upstream version: return (100 - (((uint32_t)(sqrtf(ummHeapInfo.freeBlocksSquared)) * 100)/(ummHeapInfo.freeBlocks)));
return 100 - (((uint32_t)(sqrt32(_context->info.freeBlocksSquared)) * 100) / (_context->info.freeBlocks));
}
}
int umm_fragmentation_metric(void) {
#ifndef UMM_INLINE_METRICS
umm_info(NULL, false);
#endif
return umm_fragmentation_metric_core(umm_get_current_heap());
}
#ifdef UMM_INLINE_METRICS
static void umm_fragmentation_metric_init(umm_heap_context_t *_context) {
_context->info.freeBlocks = UMM_NUMBLOCKS - 2;
_context->info.freeBlocksSquared = _context->info.freeBlocks * _context->info.freeBlocks;
}
static void umm_fragmentation_metric_add(umm_heap_context_t *_context, uint16_t c) {
uint16_t blocks = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) - c;
DBGLOG_DEBUG("Add block %d size %d to free metric\n", c, blocks);
_context->info.freeBlocks += blocks;
_context->info.freeBlocksSquared += (blocks * blocks);
}
static void umm_fragmentation_metric_remove(umm_heap_context_t *_context, uint16_t c) {
uint16_t blocks = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) - c;
DBGLOG_DEBUG("Remove block %d size %d from free metric\n", c, blocks);
_context->info.freeBlocks -= blocks;
_context->info.freeBlocksSquared -= (blocks * blocks);
}
#endif // UMM_INLINE_METRICS
/* ------------------------------------------------------------------------ */
#endif
#endif // defined(BUILD_UMM_MALLOC_C)