1
0
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:
M Hightower 2022-10-11 04:52:39 -07:00 committed by GitHub
parent 7b2c627ed4
commit d3eddeb501
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 386 additions and 150 deletions

View File

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

View File

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

View File

@ -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()
{ {

View File

@ -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();

View File

@ -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
} }
/* /*

View 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

View File

@ -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) {}

View File

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

View File

@ -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);

View File

@ -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();

View File

@ -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" {

View File

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

View File

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