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 <math.h>
|
||||
|
||||
#include "umm_malloc/umm_malloc_cfgport.h"
|
||||
#include "stdlib_noniso.h"
|
||||
#include "binary.h"
|
||||
#include "esp8266_peri.h"
|
||||
@ -311,7 +312,8 @@ void configTime(const char* tz, String server1,
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_ESP_OOM
|
||||
// reinclude *alloc redefinition because of <cstdlib> undefining them
|
||||
// this is mandatory for allowing OOM *alloc definitions in .ino files
|
||||
#include "umm_malloc/umm_malloc_cfg.h"
|
||||
// Position *alloc redefinition at the end of Arduino.h because <cstdlib> would
|
||||
// have undefined them. Mandatory for supporting OOM and other debug alloc
|
||||
// definitions in .ino files
|
||||
#include "heap_api_debug.h"
|
||||
#endif
|
||||
|
@ -19,10 +19,10 @@
|
||||
*/
|
||||
|
||||
#include "umm_malloc/umm_malloc.h"
|
||||
#include "umm_malloc/umm_malloc_cfg.h"
|
||||
#include "coredecls.h"
|
||||
#include "Esp.h"
|
||||
|
||||
#if defined(UMM_INFO)
|
||||
void EspClass::getHeapStats(uint32_t* hfree, uint32_t* hmax, uint8_t* hfrag)
|
||||
{
|
||||
// L2 / Euclidean norm of free block sizes.
|
||||
@ -60,3 +60,4 @@ uint8_t EspClass::getHeapFragmentation()
|
||||
{
|
||||
return (uint8_t)umm_fragmentation_metric();
|
||||
}
|
||||
#endif
|
||||
|
@ -219,13 +219,15 @@ uint16_t EspClass::getVcc(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)
|
||||
{
|
||||
return umm_max_block_size();
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t EspClass::getFreeContStack()
|
||||
{
|
||||
|
@ -114,11 +114,12 @@ class EspClass {
|
||||
static uint32_t getChipId();
|
||||
|
||||
static uint32_t getFreeHeap();
|
||||
#if defined(UMM_INFO)
|
||||
static uint32_t getMaxFreeBlockSize();
|
||||
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, uint32_t* max = nullptr, uint8_t* frag = nullptr);
|
||||
|
||||
#endif
|
||||
static uint32_t getFreeContStack();
|
||||
static void resetFreeContStack();
|
||||
|
||||
|
@ -342,8 +342,10 @@ size_t IRAM_ATTR xPortWantedSizeAlign(size_t size)
|
||||
|
||||
void system_show_malloc(void)
|
||||
{
|
||||
#ifdef UMM_INFO
|
||||
HeapSelectDram ephemeral;
|
||||
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
|
||||
*/
|
||||
|
||||
#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>
|
||||
|
||||
#define DBG_MMU_FLUSH(a) while((USS(a) >> USTXC) & 0xff) {}
|
||||
|
@ -276,4 +276,70 @@ Enhancement ideas:
|
||||
#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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
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);
|
||||
|
@ -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)
|
||||
/*
|
||||
For internal, mainly used by UMM_STATS_FULL; exported so external components
|
||||
can perform Heap related calculations.
|
||||
*/
|
||||
size_t umm_block_size(void) {
|
||||
return sizeof(umm_block);
|
||||
}
|
||||
#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)
|
||||
// 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) {
|
||||
UMM_CHECK_INITIALIZED();
|
||||
|
||||
umm_heap_context_t *_context = umm_get_current_heap();
|
||||
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
|
||||
|
||||
/*
|
||||
I assume xPortGetFreeHeapSize needs to be in IRAM. Since
|
||||
system_get_free_heap_size is in IRAM. Which would mean, umm_free_heap_size()
|
||||
in flash, was not a safe alternative for returning the same information.
|
||||
This API is called by `system_get_free_heap_size()` which is in IRAM. Driving
|
||||
the assumption the callee may be in an ISR or Cache_Read_Disable state. Use
|
||||
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)
|
||||
size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size_lw")));
|
||||
#elif defined(UMM_INFO)
|
||||
#ifndef UMM_INLINE_METRICS
|
||||
#warning "No ISR safe function available to implement xPortGetFreeHeapSize()"
|
||||
#endif
|
||||
#else
|
||||
size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size")));
|
||||
#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)
|
||||
void umm_print_stats(int force) {
|
||||
umm_heap_context_t *_context = umm_get_current_heap();
|
||||
|
@ -10,8 +10,10 @@
|
||||
|
||||
#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 <osapi.h>
|
||||
#include <heap_api_debug.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -106,31 +106,6 @@ extern "C" {
|
||||
#include "umm_malloc_cfgport.h"
|
||||
#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 */
|
||||
|
||||
#define UMM_H_ATTPACKPRE
|
||||
@ -177,12 +152,22 @@ extern "C" {
|
||||
#define UMM_FRAGMENTATION_METRIC_REMOVE(c)
|
||||
#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 :
|
||||
*
|
||||
* 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
|
||||
* unallocated block on the heap!
|
||||
*/
|
||||
@ -209,32 +194,23 @@ typedef struct UMM_HEAP_INFO_t {
|
||||
UMM_HEAP_INFO;
|
||||
|
||||
// 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);
|
||||
#ifdef UMM_INLINE_METRICS
|
||||
extern size_t umm_free_heap_size(void);
|
||||
#else
|
||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||
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
|
||||
|
||||
|
||||
// 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 int umm_usage_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 int umm_usage_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
|
||||
|
||||
/*
|
||||
@ -305,7 +281,6 @@ UMM_STATISTICS;
|
||||
|
||||
#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);
|
||||
|
||||
#else // not UMM_STATS or UMM_STATS_FULL
|
||||
@ -720,91 +695,9 @@ struct UMM_TIME_STATS_t {
|
||||
UMM_TIME_STAT id_no_tag;
|
||||
};
|
||||
#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
|
||||
}
|
||||
#endif
|
||||
|
||||
#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
|
||||
*/
|
||||
|
||||
#ifdef _UMM_MALLOC_CFG_H
|
||||
// Additional includes for "umm_malloc_cfg.h" only
|
||||
#include <pgmspace.h>
|
||||
#include <mmu_iram.h>
|
||||
#include "../debug.h"
|
||||
@ -18,6 +14,17 @@
|
||||
#include <osapi.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
|
||||
@ -87,5 +94,120 @@ extern char _heap_start[];
|
||||
#define UMM_HEAP_STACK_DEPTH 32
|
||||
#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
|
||||
|
Loading…
x
Reference in New Issue
Block a user