mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-19 23:22:16 +03:00
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()).
This commit is contained in:
parent
7b2c627ed4
commit
d3eddeb501
@ -33,6 +33,7 @@ extern "C" {
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "umm_malloc/umm_malloc_cfgport.h"
|
||||||
#include "stdlib_noniso.h"
|
#include "stdlib_noniso.h"
|
||||||
#include "binary.h"
|
#include "binary.h"
|
||||||
#include "esp8266_peri.h"
|
#include "esp8266_peri.h"
|
||||||
@ -311,7 +312,8 @@ void configTime(const char* tz, String server1,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_OOM
|
#ifdef DEBUG_ESP_OOM
|
||||||
// reinclude *alloc redefinition because of <cstdlib> undefining them
|
// Position *alloc redefinition at the end of Arduino.h because <cstdlib> would
|
||||||
// this is mandatory for allowing OOM *alloc definitions in .ino files
|
// have undefined them. Mandatory for supporting OOM and other debug alloc
|
||||||
#include "umm_malloc/umm_malloc_cfg.h"
|
// definitions in .ino files
|
||||||
|
#include "heap_api_debug.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "umm_malloc/umm_malloc.h"
|
#include "umm_malloc/umm_malloc.h"
|
||||||
#include "umm_malloc/umm_malloc_cfg.h"
|
|
||||||
#include "coredecls.h"
|
#include "coredecls.h"
|
||||||
#include "Esp.h"
|
#include "Esp.h"
|
||||||
|
|
||||||
|
#if defined(UMM_INFO)
|
||||||
void EspClass::getHeapStats(uint32_t* hfree, uint32_t* hmax, uint8_t* hfrag)
|
void EspClass::getHeapStats(uint32_t* hfree, uint32_t* hmax, uint8_t* hfrag)
|
||||||
{
|
{
|
||||||
// L2 / Euclidean norm of free block sizes.
|
// L2 / Euclidean norm of free block sizes.
|
||||||
@ -60,3 +60,4 @@ uint8_t EspClass::getHeapFragmentation()
|
|||||||
{
|
{
|
||||||
return (uint8_t)umm_fragmentation_metric();
|
return (uint8_t)umm_fragmentation_metric();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
@ -219,13 +219,15 @@ uint16_t EspClass::getVcc(void)
|
|||||||
|
|
||||||
uint32_t EspClass::getFreeHeap(void)
|
uint32_t EspClass::getFreeHeap(void)
|
||||||
{
|
{
|
||||||
return system_get_free_heap_size();
|
return umm_free_heap_size_lw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(UMM_INFO)
|
||||||
uint32_t EspClass::getMaxFreeBlockSize(void)
|
uint32_t EspClass::getMaxFreeBlockSize(void)
|
||||||
{
|
{
|
||||||
return umm_max_block_size();
|
return umm_max_block_size();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
uint32_t EspClass::getFreeContStack()
|
uint32_t EspClass::getFreeContStack()
|
||||||
{
|
{
|
||||||
|
@ -114,11 +114,12 @@ class EspClass {
|
|||||||
static uint32_t getChipId();
|
static uint32_t getChipId();
|
||||||
|
|
||||||
static uint32_t getFreeHeap();
|
static uint32_t getFreeHeap();
|
||||||
|
#if defined(UMM_INFO)
|
||||||
static uint32_t getMaxFreeBlockSize();
|
static uint32_t getMaxFreeBlockSize();
|
||||||
static uint8_t getHeapFragmentation(); // in %
|
static uint8_t getHeapFragmentation(); // in %
|
||||||
static void getHeapStats(uint32_t* free = nullptr, uint16_t* max = nullptr, uint8_t* frag = nullptr) __attribute__((deprecated("Use 'uint32_t*' on max, 2nd argument")));
|
static void getHeapStats(uint32_t* free = nullptr, uint16_t* max = nullptr, uint8_t* frag = nullptr) __attribute__((deprecated("Use 'uint32_t*' on max, 2nd argument")));
|
||||||
static void getHeapStats(uint32_t* free = nullptr, uint32_t* max = nullptr, uint8_t* frag = nullptr);
|
static void getHeapStats(uint32_t* free = nullptr, uint32_t* max = nullptr, uint8_t* frag = nullptr);
|
||||||
|
#endif
|
||||||
static uint32_t getFreeContStack();
|
static uint32_t getFreeContStack();
|
||||||
static void resetFreeContStack();
|
static void resetFreeContStack();
|
||||||
|
|
||||||
|
@ -342,8 +342,10 @@ size_t IRAM_ATTR xPortWantedSizeAlign(size_t size)
|
|||||||
|
|
||||||
void system_show_malloc(void)
|
void system_show_malloc(void)
|
||||||
{
|
{
|
||||||
|
#ifdef UMM_INFO
|
||||||
HeapSelectDram ephemeral;
|
HeapSelectDram ephemeral;
|
||||||
umm_info(NULL, true);
|
umm_info(NULL, true);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
74
cores/esp8266/heap_api_debug.h
Normal file
74
cores/esp8266/heap_api_debug.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Issolated heap debug helper code from from umm_malloc/umm_malloc_cfg.h.
|
||||||
|
* Updated umm_malloc/umm_malloc.h and Arduino.h to reference.
|
||||||
|
* No #ifdef fenceing was used before. From its previous location, this content
|
||||||
|
* was reassert multiple times through Arduino.h. In case there are legacy
|
||||||
|
* projects that depend on the previous unfenced behavior, no fencing has been
|
||||||
|
* added.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* *alloc redefinition - Included from Arduino.h for DEBUG_ESP_OOM support.
|
||||||
|
*
|
||||||
|
* It can also be directly include by the sketch for UMM_POISON_CHECK or
|
||||||
|
* UMM_POISON_CHECK_LITE builds to get more info about the caller when they
|
||||||
|
* report on a fail.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_OOM
|
||||||
|
#define MEMLEAK_DEBUG
|
||||||
|
|
||||||
|
#include "umm_malloc/umm_malloc_cfg.h"
|
||||||
|
|
||||||
|
#include <pgmspace.h>
|
||||||
|
// Reuse pvPort* calls, since they already support passing location information.
|
||||||
|
// Specifically the debug version (heap_...) that does not force DRAM heap.
|
||||||
|
void *IRAM_ATTR heap_pvPortMalloc(size_t size, const char *file, int line);
|
||||||
|
void *IRAM_ATTR heap_pvPortCalloc(size_t count, size_t size, const char *file, int line);
|
||||||
|
void *IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char *file, int line);
|
||||||
|
void *IRAM_ATTR heap_pvPortZalloc(size_t size, const char *file, int line);
|
||||||
|
void IRAM_ATTR heap_vPortFree(void *ptr, const char *file, int line);
|
||||||
|
|
||||||
|
#define malloc(s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortMalloc(s, mem_debug_file, __LINE__); })
|
||||||
|
#define calloc(n,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortCalloc(n, s, mem_debug_file, __LINE__); })
|
||||||
|
#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortRealloc(p, s, mem_debug_file, __LINE__); })
|
||||||
|
|
||||||
|
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||||
|
#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_vPortFree(p, mem_debug_file, __LINE__); })
|
||||||
|
#else
|
||||||
|
#define dbg_heap_free(p) free(p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) // #elif for #ifdef DEBUG_ESP_OOM
|
||||||
|
#include <pgmspace.h>
|
||||||
|
void *IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char *file, int line);
|
||||||
|
#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortRealloc(p, s, mem_debug_file, __LINE__); })
|
||||||
|
|
||||||
|
void IRAM_ATTR heap_vPortFree(void *ptr, const char *file, int line);
|
||||||
|
// C - to be discussed
|
||||||
|
/*
|
||||||
|
Problem, I would like to report the file and line number with the umm poison
|
||||||
|
event as close as possible to the event. The #define method works for malloc,
|
||||||
|
calloc, and realloc those names are not as generic as free. A #define free
|
||||||
|
captures too much. Classes with methods called free are included :(
|
||||||
|
Inline functions would report the address of the inline function in the .h
|
||||||
|
not where they are called.
|
||||||
|
|
||||||
|
Anybody know a trick to make this work?
|
||||||
|
|
||||||
|
Create dbg_heap_free() as an alternative for free() when you need a little
|
||||||
|
more help in debugging the more challenging problems.
|
||||||
|
*/
|
||||||
|
#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_vPortFree(p, mem_debug_file, __LINE__); })
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define dbg_heap_free(p) free(p)
|
||||||
|
#endif /* DEBUG_ESP_OOM */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@ -56,7 +56,8 @@ extern "C" {
|
|||||||
#define DEV_DEBUG_PRINT
|
#define DEV_DEBUG_PRINT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(DEV_DEBUG_PRINT) || defined(DEBUG_ESP_MMU)
|
#if (defined(DEV_DEBUG_PRINT) || defined(DEBUG_ESP_MMU)) && !defined(HOST_MOCK)
|
||||||
|
// Errors follow when `#include <esp8266_peri.h>` is present when running CI HOST
|
||||||
#include <esp8266_peri.h>
|
#include <esp8266_peri.h>
|
||||||
|
|
||||||
#define DBG_MMU_FLUSH(a) while((USS(a) >> USTXC) & 0xff) {}
|
#define DBG_MMU_FLUSH(a) while((USS(a) >> USTXC) & 0xff) {}
|
||||||
|
@ -276,4 +276,70 @@ Enhancement ideas:
|
|||||||
#endif
|
#endif
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sep 26, 2022
|
||||||
|
|
||||||
|
History/Overview
|
||||||
|
|
||||||
|
ESP.getFreeHeap() needs a function it can call for free Heap size. The legacy
|
||||||
|
method was the SDK function `system_get_free_heap_size()` which is in IRAM.
|
||||||
|
|
||||||
|
`system_get_free_heap_size()` calls `xPortGetFreeHeapSize()` to get free heap
|
||||||
|
size. Our old legacy implementation used umm_info(), employing a
|
||||||
|
time-consuming method for getting free Heap size and runs with interrupts
|
||||||
|
blocked.
|
||||||
|
|
||||||
|
Later we used a distributed method that maintained the free heap size with
|
||||||
|
each malloc API call that changed the Heap. (enabled by build option UMM_STATS
|
||||||
|
or UMM_STATS_FULL) We used an internally function `umm_free_heap_size_lw()` to
|
||||||
|
report free heap size. We satisfied the requirements for
|
||||||
|
`xPortGetFreeHeapSize()` with an alias to `umm_free_heap_size_lw()`
|
||||||
|
in replacement for the legacy umm_info() call wrapper.
|
||||||
|
|
||||||
|
The upstream umm_malloc later implemented a similar method enabled by build
|
||||||
|
option UMM_INLINE_METRICS and introduced the function `umm_free_heap_size()`.
|
||||||
|
|
||||||
|
The NONOS SDK alloc request must use the DRAM Heap. Need to Ensure DRAM Heap
|
||||||
|
results when multiple Heap support is enabled. Since the SDK uses portable
|
||||||
|
malloc calls pvPortMalloc, ... we leveraged that for a solution - force
|
||||||
|
pvPortMalloc, ... APIs to serve DRAM only.
|
||||||
|
|
||||||
|
In an oversight, `xPortGetFreeHeapSize()` was left reporting the results for
|
||||||
|
the current heap selection via `umm_free_heap_size_lw()`. Thus, if an SDK
|
||||||
|
function like os_printf_plus were called when the current heap selection was
|
||||||
|
IRAM, it would get the results for the IRAM Heap. Then would receive DRAM with
|
||||||
|
an alloc request. However, when the free IRAM size is too small, it would
|
||||||
|
skip the Heap alloc request and use stack space.
|
||||||
|
|
||||||
|
Solution
|
||||||
|
|
||||||
|
The resolution is to rely on build UMM_STATS(default) or UMM_STATS_FULL for
|
||||||
|
free heap size information. When not available in the build, fallback to the
|
||||||
|
upstream umm_malloc's `umm_free_heap_size()` and require the build option
|
||||||
|
UMM_INLINE_METRICS. Otherwise, fail the build.
|
||||||
|
|
||||||
|
Use function name `umm_free_heap_size_lw()` to support external request for
|
||||||
|
current heap size. When build options result in fallback using umm_info.c,
|
||||||
|
ensure UMM_INLINE_METRICS enabled and alias to `umm_free_heap_size()`.
|
||||||
|
|
||||||
|
For the multiple Heap case, `xPortGetFreeHeapSize()` becomes a unique function
|
||||||
|
and reports only DRAM free heap size. Now `system_get_free_heap_size()` will
|
||||||
|
always report DRAM free Heap size. This might be a breaking change.
|
||||||
|
|
||||||
|
Specifics:
|
||||||
|
|
||||||
|
* Support `umm_free_heap_size_lw()` as an `extern`.
|
||||||
|
|
||||||
|
* When the build options UMM_STATS/UMM_STATS_FULL are not used, fallback to
|
||||||
|
the upstream umm_malloc's `umm_free_heap_size()` function in umm_info.c
|
||||||
|
* require the UMM_INLINE_METRICS build option.
|
||||||
|
* assign `umm_free_heap_size_lw()` as an alias to `umm_free_heap_size()`
|
||||||
|
|
||||||
|
* `xPortGetFreeHeapSize()`
|
||||||
|
* For single heap builds, alias to `umm_free_heap_size_lw()`
|
||||||
|
* For multiple Heaps builds, add a dedicated function that always reports
|
||||||
|
DRAM results.
|
||||||
|
|
||||||
|
*/
|
||||||
#endif
|
#endif
|
||||||
|
@ -174,6 +174,14 @@ size_t umm_free_heap_size_core(umm_heap_context_t *_context) {
|
|||||||
return (size_t)_context->info.freeBlocks * sizeof(umm_block);
|
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) {
|
size_t umm_free_heap_size(void) {
|
||||||
#ifndef UMM_INLINE_METRICS
|
#ifndef UMM_INLINE_METRICS
|
||||||
umm_info(NULL, false);
|
umm_info(NULL, false);
|
||||||
|
@ -161,35 +161,97 @@ void umm_poison_free_fl(void *ptr, const char *file, int line) {
|
|||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO)
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO)
|
||||||
|
/*
|
||||||
|
For internal, mainly used by UMM_STATS_FULL; exported so external components
|
||||||
|
can perform Heap related calculations.
|
||||||
|
*/
|
||||||
size_t umm_block_size(void) {
|
size_t umm_block_size(void) {
|
||||||
return sizeof(umm_block);
|
return sizeof(umm_block);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Need to expose a function to support getting the current free heap size.
|
||||||
|
Export `size_t umm_free_heap_size_lw(void)` for this purpose.
|
||||||
|
Used by ESP.getFreeHeap().
|
||||||
|
|
||||||
|
For an expanded discussion see Notes.h, entry dated "Sep 26, 2022"
|
||||||
|
*/
|
||||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
// Keep complete call path in IRAM
|
/*
|
||||||
|
Default build option to support export.
|
||||||
|
|
||||||
|
Keep complete call path in IRAM.
|
||||||
|
*/
|
||||||
size_t umm_free_heap_size_lw(void) {
|
size_t umm_free_heap_size_lw(void) {
|
||||||
UMM_CHECK_INITIALIZED();
|
UMM_CHECK_INITIALIZED();
|
||||||
|
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return (size_t)_context->UMM_FREE_BLOCKS * sizeof(umm_block);
|
return (size_t)_context->UMM_FREE_BLOCKS * sizeof(umm_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif defined(UMM_INLINE_METRICS)
|
||||||
|
/*
|
||||||
|
For the fallback option using `size_t umm_free_heap_size(void)`, we must have
|
||||||
|
the UMM_INLINE_METRICS build option enabled to support free heap size
|
||||||
|
reporting without the use of `umm_info()`.
|
||||||
|
*/
|
||||||
|
size_t umm_free_heap_size_lw(void) __attribute__ ((alias("umm_free_heap_size")));
|
||||||
|
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
We require a resource to track and report free Heap size with low overhead.
|
||||||
|
For an expanded discussion see Notes.h, entry dated "Sep 26, 2022"
|
||||||
|
*/
|
||||||
|
#error UMM_INLINE_METRICS, UMM_STATS, or UMM_STATS_FULL needs to be defined.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
|
size_t umm_free_heap_size_core_lw(umm_heap_context_t *_context) {
|
||||||
|
return (size_t)_context->UMM_FREE_BLOCKS * sizeof(umm_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(UMM_INFO)
|
||||||
|
// Backfill support for umm_free_heap_size_core_lw()
|
||||||
|
size_t umm_free_heap_size_core_lw(umm_heap_context_t *_context) __attribute__ ((alias("umm_free_heap_size_core")));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
I assume xPortGetFreeHeapSize needs to be in IRAM. Since
|
This API is called by `system_get_free_heap_size()` which is in IRAM. Driving
|
||||||
system_get_free_heap_size is in IRAM. Which would mean, umm_free_heap_size()
|
the assumption the callee may be in an ISR or Cache_Read_Disable state. Use
|
||||||
in flash, was not a safe alternative for returning the same information.
|
IRAM to ensure that the complete call chain is in IRAM.
|
||||||
|
|
||||||
|
To satisfy this requirement, we need UMM_STATS... or UMM_INLINE_METRICS
|
||||||
|
defined. These support an always available without intense computation
|
||||||
|
free-Heap value.
|
||||||
|
|
||||||
|
Like the other vPort... APIs used by the SDK, this must always report on the
|
||||||
|
DRAM Heap not the current Heap.
|
||||||
*/
|
*/
|
||||||
|
#if (UMM_NUM_HEAPS == 1)
|
||||||
|
// Reduce IRAM usage for the single Heap case
|
||||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size_lw")));
|
size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size_lw")));
|
||||||
#elif defined(UMM_INFO)
|
#else
|
||||||
#ifndef UMM_INLINE_METRICS
|
|
||||||
#warning "No ISR safe function available to implement xPortGetFreeHeapSize()"
|
|
||||||
#endif
|
|
||||||
size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size")));
|
size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size")));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
size_t xPortGetFreeHeapSize(void) {
|
||||||
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INLINE_METRICS)
|
||||||
|
UMM_CHECK_INITIALIZED();
|
||||||
|
umm_heap_context_t *_context = umm_get_heap_by_id(UMM_HEAP_DRAM);
|
||||||
|
|
||||||
|
return umm_free_heap_size_core_lw(_context);
|
||||||
|
#else
|
||||||
|
// At this time, this build path is not reachable. In case things change,
|
||||||
|
// keep build check.
|
||||||
|
// Not in IRAM, umm_info() would have been used to complete this operation.
|
||||||
|
#error "No ISR safe function available to implement xPortGetFreeHeapSize()"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
void umm_print_stats(int force) {
|
void umm_print_stats(int force) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
|
@ -10,8 +10,10 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// C This include is not in upstream
|
// C These includes are not in the upstream
|
||||||
#include "umm_malloc_cfg.h" /* user-dependent */
|
#include "umm_malloc_cfg.h" /* user-dependent */
|
||||||
|
#include <osapi.h>
|
||||||
|
#include <heap_api_debug.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -106,31 +106,6 @@ extern "C" {
|
|||||||
#include "umm_malloc_cfgport.h"
|
#include "umm_malloc_cfgport.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define UMM_BEST_FIT
|
|
||||||
#define UMM_INFO
|
|
||||||
// #define UMM_INLINE_METRICS
|
|
||||||
#define UMM_STATS
|
|
||||||
|
|
||||||
/*
|
|
||||||
* To support API call, system_show_malloc(), -DUMM_INFO is required.
|
|
||||||
*
|
|
||||||
* For the ESP8266 we need an ISR safe function to call for implementing
|
|
||||||
* xPortGetFreeHeapSize(). We can get this with one of these options:
|
|
||||||
* 1) -DUMM_STATS or -DUMM_STATS_FULL
|
|
||||||
* 2) -DUMM_INLINE_METRICS (and implicitly includes -DUMM_INFO)
|
|
||||||
*
|
|
||||||
* If frequent calls are made to ESP.getHeapFragmentation(),
|
|
||||||
* -DUMM_INLINE_METRICS would reduce long periods of interrupts disabled caused
|
|
||||||
* by frequent calls to `umm_info()`. Instead, the computations get distributed
|
|
||||||
* across each malloc, realloc, and free. This appears to require an additional
|
|
||||||
* 116 bytes of IRAM vs using `UMM_STATS` with `UMM_INFO`.
|
|
||||||
*
|
|
||||||
* When both UMM_STATS and UMM_INLINE_METRICS are defined, macros and structures
|
|
||||||
* have been optimized to reduce duplications.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/* A couple of macros to make packing structures less compiler dependent */
|
/* A couple of macros to make packing structures less compiler dependent */
|
||||||
|
|
||||||
#define UMM_H_ATTPACKPRE
|
#define UMM_H_ATTPACKPRE
|
||||||
@ -177,12 +152,22 @@ extern "C" {
|
|||||||
#define UMM_FRAGMENTATION_METRIC_REMOVE(c)
|
#define UMM_FRAGMENTATION_METRIC_REMOVE(c)
|
||||||
#endif // UMM_INLINE_METRICS
|
#endif // UMM_INLINE_METRICS
|
||||||
|
|
||||||
|
struct UMM_HEAP_CONTEXT;
|
||||||
|
typedef struct UMM_HEAP_CONTEXT umm_heap_context_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Must always be defined. Core support for getting free Heap size.
|
||||||
|
When possible, access via ESP.getFreeHeap();
|
||||||
|
*/
|
||||||
|
extern size_t umm_free_heap_size_lw(void);
|
||||||
|
extern size_t umm_free_heap_size_core_lw(umm_heap_context_t *_context);
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* -D UMM_INFO :
|
* -D UMM_INFO :
|
||||||
*
|
*
|
||||||
* Enables a dup of the heap contents and a function to return the total
|
* Enables a dump of the heap contents and a function to return the total
|
||||||
* heap size that is unallocated - note this is not the same as the largest
|
* heap size that is unallocated - note this is not the same as the largest
|
||||||
* unallocated block on the heap!
|
* unallocated block on the heap!
|
||||||
*/
|
*/
|
||||||
@ -209,32 +194,23 @@ typedef struct UMM_HEAP_INFO_t {
|
|||||||
UMM_HEAP_INFO;
|
UMM_HEAP_INFO;
|
||||||
|
|
||||||
// extern UMM_HEAP_INFO ummHeapInfo;
|
// extern UMM_HEAP_INFO ummHeapInfo;
|
||||||
struct UMM_HEAP_CONTEXT;
|
|
||||||
typedef struct UMM_HEAP_CONTEXT umm_heap_context_t;
|
|
||||||
|
|
||||||
extern ICACHE_FLASH_ATTR void *umm_info(void *ptr, bool force);
|
extern ICACHE_FLASH_ATTR void *umm_info(void *ptr, bool force);
|
||||||
#ifdef UMM_INLINE_METRICS
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
extern size_t umm_free_heap_size(void);
|
|
||||||
#else
|
|
||||||
extern ICACHE_FLASH_ATTR size_t umm_free_heap_size(void);
|
extern ICACHE_FLASH_ATTR size_t umm_free_heap_size(void);
|
||||||
|
extern ICACHE_FLASH_ATTR size_t umm_free_heap_size_core(umm_heap_context_t *_context);
|
||||||
|
#else
|
||||||
|
extern size_t umm_free_heap_size(void);
|
||||||
|
extern size_t umm_free_heap_size_core(umm_heap_context_t *_context);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// umm_max_block_size changed to umm_max_free_block_size in upstream.
|
// umm_max_block_size changed to umm_max_free_block_size in upstream.
|
||||||
extern ICACHE_FLASH_ATTR size_t umm_max_block_size(void);
|
extern ICACHE_FLASH_ATTR size_t umm_max_block_size(void);
|
||||||
extern ICACHE_FLASH_ATTR int umm_usage_metric(void);
|
extern ICACHE_FLASH_ATTR int umm_usage_metric(void);
|
||||||
extern ICACHE_FLASH_ATTR int umm_fragmentation_metric(void);
|
extern ICACHE_FLASH_ATTR int umm_fragmentation_metric(void);
|
||||||
extern ICACHE_FLASH_ATTR size_t umm_free_heap_size_core(umm_heap_context_t *_context);
|
|
||||||
extern ICACHE_FLASH_ATTR size_t umm_max_block_size_core(umm_heap_context_t *_context);
|
extern ICACHE_FLASH_ATTR size_t umm_max_block_size_core(umm_heap_context_t *_context);
|
||||||
extern ICACHE_FLASH_ATTR int umm_usage_metric_core(umm_heap_context_t *_context);
|
extern ICACHE_FLASH_ATTR int umm_usage_metric_core(umm_heap_context_t *_context);
|
||||||
extern ICACHE_FLASH_ATTR int umm_fragmentation_metric_core(umm_heap_context_t *_context);
|
extern ICACHE_FLASH_ATTR int umm_fragmentation_metric_core(umm_heap_context_t *_context);
|
||||||
#else
|
|
||||||
#define umm_info(p,b)
|
|
||||||
#define umm_free_heap_size() (0)
|
|
||||||
#define umm_max_block_size() (0)
|
|
||||||
#define umm_fragmentation_metric() (0)
|
|
||||||
#define umm_usage_metric() (0)
|
|
||||||
#define umm_free_heap_size_core() (0)
|
|
||||||
#define umm_max_block_size_core() (0)
|
|
||||||
#define umm_fragmentation_metric_core() (0)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -305,7 +281,6 @@ UMM_STATISTICS;
|
|||||||
|
|
||||||
#define STATS__OOM_UPDATE() _context->UMM_OOM_COUNT += 1
|
#define STATS__OOM_UPDATE() _context->UMM_OOM_COUNT += 1
|
||||||
|
|
||||||
extern size_t umm_free_heap_size_lw(void);
|
|
||||||
extern size_t umm_get_oom_count(void);
|
extern size_t umm_get_oom_count(void);
|
||||||
|
|
||||||
#else // not UMM_STATS or UMM_STATS_FULL
|
#else // not UMM_STATS or UMM_STATS_FULL
|
||||||
@ -720,91 +695,9 @@ struct UMM_TIME_STATS_t {
|
|||||||
UMM_TIME_STAT id_no_tag;
|
UMM_TIME_STAT id_no_tag;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
/////////////////////////////////////////////////
|
|
||||||
#ifdef DEBUG_ESP_OOM
|
|
||||||
|
|
||||||
#define MEMLEAK_DEBUG
|
|
||||||
|
|
||||||
// umm_*alloc are not renamed to *alloc
|
|
||||||
// Assumes umm_malloc.h has already been included.
|
|
||||||
|
|
||||||
#define umm_zalloc(s) umm_calloc(1,s)
|
|
||||||
|
|
||||||
void *malloc_loc(size_t s, const char *file, int line);
|
|
||||||
void *calloc_loc(size_t n, size_t s, const char *file, int line);
|
|
||||||
void *realloc_loc(void *p, size_t s, const char *file, int line);
|
|
||||||
// *alloc are macro calling *alloc_loc calling+checking umm_*alloc()
|
|
||||||
// they are defined at the bottom of this file
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#elif defined(UMM_POISON_CHECK)
|
|
||||||
void *realloc_loc(void *p, size_t s, const char *file, int line);
|
|
||||||
void free_loc(void *p, const char *file, int line);
|
|
||||||
#else // !defined(ESP_DEBUG_OOM)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* _UMM_MALLOC_CFG_H */
|
#endif /* _UMM_MALLOC_CFG_H */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
#ifdef DEBUG_ESP_OOM
|
|
||||||
// this must be outside from "#ifndef _UMM_MALLOC_CFG_H"
|
|
||||||
// because Arduino.h's <cstdlib> does #undef *alloc
|
|
||||||
// Arduino.h recall us to redefine them
|
|
||||||
#include <pgmspace.h>
|
|
||||||
// Reuse pvPort* calls, since they already support passing location information.
|
|
||||||
// Specifically the debug version (heap_...) that does not force DRAM heap.
|
|
||||||
void *IRAM_ATTR heap_pvPortMalloc(size_t size, const char *file, int line);
|
|
||||||
void *IRAM_ATTR heap_pvPortCalloc(size_t count, size_t size, const char *file, int line);
|
|
||||||
void *IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char *file, int line);
|
|
||||||
void *IRAM_ATTR heap_pvPortZalloc(size_t size, const char *file, int line);
|
|
||||||
void IRAM_ATTR heap_vPortFree(void *ptr, const char *file, int line);
|
|
||||||
|
|
||||||
#define malloc(s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortMalloc(s, mem_debug_file, __LINE__); })
|
|
||||||
#define calloc(n,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortCalloc(n, s, mem_debug_file, __LINE__); })
|
|
||||||
#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortRealloc(p, s, mem_debug_file, __LINE__); })
|
|
||||||
|
|
||||||
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
|
||||||
#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_vPortFree(p, mem_debug_file, __LINE__); })
|
|
||||||
#else
|
|
||||||
#define dbg_heap_free(p) free(p)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) // #elif for #ifdef DEBUG_ESP_OOM
|
|
||||||
#include <pgmspace.h>
|
|
||||||
void *IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char *file, int line);
|
|
||||||
#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortRealloc(p, s, mem_debug_file, __LINE__); })
|
|
||||||
|
|
||||||
void IRAM_ATTR heap_vPortFree(void *ptr, const char *file, int line);
|
|
||||||
// C - to be discussed
|
|
||||||
/*
|
|
||||||
Problem, I would like to report the file and line number with the umm poison
|
|
||||||
event as close as possible to the event. The #define method works for malloc,
|
|
||||||
calloc, and realloc those names are not as generic as free. A #define free
|
|
||||||
captures too much. Classes with methods called free are included :(
|
|
||||||
Inline functions would report the address of the inline function in the .h
|
|
||||||
not where they are called.
|
|
||||||
|
|
||||||
Anybody know a trick to make this work?
|
|
||||||
|
|
||||||
Create dbg_heap_free() as an alternative for free() when you need a little
|
|
||||||
more help in debugging the more challenging problems.
|
|
||||||
*/
|
|
||||||
#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_vPortFree(p, mem_debug_file, __LINE__); })
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define dbg_heap_free(p) free(p)
|
|
||||||
#endif /* DEBUG_ESP_OOM */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
#ifndef _UMM_MALLOC_CFGPORT_H
|
|
||||||
#define _UMM_MALLOC_CFGPORT_H
|
|
||||||
|
|
||||||
#ifndef _UMM_MALLOC_CFG_H
|
|
||||||
#error "This include file must be used with umm_malloc_cfg.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Arduino ESP8266 core umm_malloc port config
|
* Arduino ESP8266 core umm_malloc port config
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef _UMM_MALLOC_CFG_H
|
||||||
|
// Additional includes for "umm_malloc_cfg.h" only
|
||||||
#include <pgmspace.h>
|
#include <pgmspace.h>
|
||||||
#include <mmu_iram.h>
|
#include <mmu_iram.h>
|
||||||
#include "../debug.h"
|
#include "../debug.h"
|
||||||
@ -18,6 +14,17 @@
|
|||||||
#include <osapi.h>
|
#include <osapi.h>
|
||||||
|
|
||||||
#include "c_types.h"
|
#include "c_types.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _UMM_MALLOC_CFGPORT_H
|
||||||
|
#define _UMM_MALLOC_CFGPORT_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Between UMM_BEST_FIT or UMM_FIRST_FIT, UMM_BEST_FIT is the better option for
|
||||||
|
* reducing heap fragmentation. With no selection made, UMM_BEST_FIT is used.
|
||||||
|
* See umm_malloc_cfg.h for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* -DUMM_INIT_USE_IRAM
|
* -DUMM_INIT_USE_IRAM
|
||||||
@ -87,5 +94,120 @@ extern char _heap_start[];
|
|||||||
#define UMM_HEAP_STACK_DEPTH 32
|
#define UMM_HEAP_STACK_DEPTH 32
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The NONOS SDK API requires function `umm_info()` for implementing
|
||||||
|
* `system_show_malloc()`. Build option `-DUMM_INFO` enables this support.
|
||||||
|
*
|
||||||
|
* Also, `-DUMM_INFO` is needed to support several EspClass methods.
|
||||||
|
* Partial EspClass method list:
|
||||||
|
* `uint32_t EspClass::getMaxFreeBlockSize()`
|
||||||
|
* `void EspClass::getHeapStats(uint32_t* hfree, uint32_t* hmax, uint8_t* hfrag)`
|
||||||
|
* `uint8_t EspClass::getHeapFragmentation()`
|
||||||
|
*
|
||||||
|
* The NONOS SDK API requires an ISR safe function to call for implementing
|
||||||
|
* `xPortGetFreeHeapSize()`. Use one of these options:
|
||||||
|
* 1) `-DUMM_STATS` or `-DUMM_STATS_FULL`
|
||||||
|
* 2) `-DUMM_INLINE_METRICS` (implicitly includes `-DUMM_INFO`)
|
||||||
|
*
|
||||||
|
* If frequent calls are made to `ESP.getHeapFragmentation()`, using build
|
||||||
|
* option `-DUMM_INLINE_METRICS` would reduce long periods of interrupts
|
||||||
|
* disabled caused by frequent calls to `umm_info().` Instead, the computations
|
||||||
|
* get distributed across each malloc, realloc, and free. Requires approximately
|
||||||
|
* 116 more bytes of IRAM when compared to the build option `-DUMM_STATS` with
|
||||||
|
* `-DUMM_INFO.`
|
||||||
|
*
|
||||||
|
* When both `-DUMM_STATS` and `-DUMM_INLINE_METRICS` are defined, macros and
|
||||||
|
* structures are optimized to reduce duplications.
|
||||||
|
*
|
||||||
|
* You can use `-DUMM_INFO` with `-DUMM_INLINE_METRICS` and drop
|
||||||
|
* `-DUMM_STATS(_FULL)` gaining back some IROM at the expense of IRAM.
|
||||||
|
*
|
||||||
|
* If you don't require the methods in EspClass that are dependent on functions
|
||||||
|
* from the `-DUMM_INFO` build option, you can use only `-DUMM_STATS` and save
|
||||||
|
* on IROM and a little IRAM.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INLINE_METRICS) || defined(UMM_INFO)
|
||||||
|
/*
|
||||||
|
User defined via build options eg. Sketch.ino.globals.h
|
||||||
|
*/
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
Set expected/implicit defaults for complete support of EspClass methods.
|
||||||
|
*/
|
||||||
|
#define UMM_INFO 1
|
||||||
|
#define UMM_STATS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
For `-Dname`, gcc assigns a value of 1 and this works fine; however,
|
||||||
|
if `-Dname=0` is used, the intended results will not be obtained.
|
||||||
|
|
||||||
|
Make value and valueless defines compliant with their usage in umm_malloc:
|
||||||
|
`#define name` => #define name 1
|
||||||
|
`#define name 0` => #undef name
|
||||||
|
*/
|
||||||
|
#if ((1 - UMM_BEST_FIT - 1) == 2)
|
||||||
|
// When UMM_BEST_FIT is defined w/o value, the computation becomes
|
||||||
|
// (1 - - 1) == 2 => (1 + 1) == 2
|
||||||
|
#undef UMM_BEST_FIT
|
||||||
|
#define UMM_BEST_FIT 1
|
||||||
|
#elif ((1 - UMM_BEST_FIT - 1) == 0)
|
||||||
|
#undef UMM_BEST_FIT
|
||||||
|
#endif
|
||||||
|
#if ((1 - UMM_FIRST_FIT - 1) == 2)
|
||||||
|
#undef UMM_FIRST_FIT
|
||||||
|
#define UMM_FIRST_FIT 1
|
||||||
|
#elif ((1 - UMM_FIRST_FIT - 1) == 0)
|
||||||
|
#undef UMM_FIRST_FIT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ((1 - UMM_INFO - 1) == 2)
|
||||||
|
#undef UMM_INFO
|
||||||
|
#define UMM_INFO 1
|
||||||
|
#elif ((1 - UMM_INFO - 1) == 0)
|
||||||
|
#undef UMM_INFO
|
||||||
|
#endif
|
||||||
|
#if ((1 - UMM_INLINE_METRICS - 1) == 2)
|
||||||
|
#undef UMM_INLINE_METRICS
|
||||||
|
#define UMM_INLINE_METRICS 1
|
||||||
|
#elif ((1 - UMM_INLINE_METRICS - 1) == 0)
|
||||||
|
#undef UMM_INLINE_METRICS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ((1 - UMM_STATS - 1) == 2)
|
||||||
|
#undef UMM_STATS
|
||||||
|
#define UMM_STATS 1
|
||||||
|
#elif ((1 - UMM_STATS - 1) == 0)
|
||||||
|
#undef UMM_STATS
|
||||||
|
#endif
|
||||||
|
#if ((1 - UMM_STATS_FULL - 1) == 2)
|
||||||
|
#undef UMM_STATS_FULL
|
||||||
|
#define UMM_STATS_FULL 1
|
||||||
|
#elif ((1 - UMM_STATS_FULL - 1) == 0)
|
||||||
|
#undef UMM_STATS_FULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(UMM_INLINE_METRICS)
|
||||||
|
// Dependent on UMM_INFO if missing enable.
|
||||||
|
#ifndef UMM_INFO
|
||||||
|
#define UMM_INFO 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
|
// We have support for free Heap size
|
||||||
|
#if defined(UMM_STATS) && defined(UMM_STATS_FULL)
|
||||||
|
#error "Build option conflict, specify either UMM_STATS or UMM_STATS_FULL."
|
||||||
|
#endif
|
||||||
|
#elif defined(UMM_INFO)
|
||||||
|
// ensure fallback support for free Heap size
|
||||||
|
#ifndef UMM_INLINE_METRICS
|
||||||
|
#define UMM_INLINE_METRICS 1
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#error "Specify at least one of these build options: (UMM_STATS or UMM_STATS_FULL) and/or UMM_INFO and/or UMM_INLINE_METRICS"
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user