mirror of
https://github.com/meshtastic/firmware.git
synced 2026-08-01 10:58:30 -04:00
* Add MemAudit: per-subsystem heap accounting in the boot log The 2.8.0 nRF52840 heap-exhaustion field reports had to be diagnosed by hand, reconstructing each subsystem's heap footprint from source and build flags one report at a time. This makes every future report self-diagnosing from the serial log: a tiny fixed-size registry (src/memory/MemAudit.*) that big long-lived allocations report into, printed as one line at the end of setup() and alongside the periodic "Heap free:" log: MemAudit[boot]: tmm=2500 warm=4000 pkthist=5824 nodedb=13440 msgstore=2200 pktpool(live)=3270 total=31234 Instrumented: NodeDB hot vector (nodedb) + satellite maps (satmaps, rb-tree overhead estimated), WarmNodeStore (warm), PacketHistory records and hash index (pkthist), TrafficManagement caches (tmm/tmm_ni), MessageStore text pool (msgstore), TFT line/repaint buffers (display), and live in-flight packets (pktpool(live)) via an optional audit tag on the packet pool allocator - the one hot path, counted with a relaxed 32-bit atomic add (single instructions on Cortex-M, no locks). Cost: 128 B RAM for the 16-tag table, well under 1 KB flash on rak4631. MESHTASTIC_MEM_AUDIT=0 compiles it out to inline no-op stubs (call sites need no ifdefs); STM32WL, the tightest flash target, defaults off. New native suite test_mem_audit covers add/set/snapshot arithmetic, tag reuse (pointer and cross-TU strcmp fallback), null/unknown tags, and table-full behavior; test/native-suite-count bumped to 28. * native-wasm: add src/memory/ to the curated source filter The wasm env denies all sources and adds an explicit file list; MemAudit callers (main, MeshService, NodeDB, PacketHistory) are in that list but src/memory/MemAudit.cpp was not, so wasm-ld failed on undefined memaudit:: symbols.
123 lines
3.4 KiB
C++
123 lines
3.4 KiB
C++
/**
|
|
* @file memGet.cpp
|
|
* @brief Implementation of MemGet class that provides functions to get memory information.
|
|
*
|
|
* This file contains the implementation of MemGet class that provides functions to get
|
|
* information about free heap, heap size, free psram and psram size. The functions are
|
|
* implemented for ESP32 and NRF52 architectures. If the platform does not have heap
|
|
* management function implemented, the functions return UINT32_MAX or 0.
|
|
*/
|
|
#include "memGet.h"
|
|
#include "configuration.h"
|
|
#include "memory/MemAudit.h"
|
|
|
|
#if defined(MESHTASTIC_DYNAMIC_SBRK_HEAP)
|
|
#include <malloc.h>
|
|
#include <unistd.h> // sbrk
|
|
|
|
#if defined(ARCH_STM32)
|
|
// Returns the uncommitted sbrk headroom: addressable space between the current heap
|
|
// break and the stack pointer that has not yet been committed to the arena.
|
|
static uint32_t sbrkHeadroom()
|
|
{
|
|
// defined in STM32 linker script
|
|
extern char _estack;
|
|
extern char _Min_Stack_Size;
|
|
|
|
uint32_t max_sp = (uint32_t)(&_estack - &_Min_Stack_Size);
|
|
uint32_t heap_end = (uint32_t)sbrk(0);
|
|
return (max_sp > heap_end) ? (max_sp - heap_end) : 0;
|
|
}
|
|
#else
|
|
#error Unsupported architecture!
|
|
#endif
|
|
#endif
|
|
|
|
MemGet memGet;
|
|
|
|
/**
|
|
* Returns the amount of free heap memory in bytes.
|
|
* @return uint32_t The amount of free heap memory in bytes.
|
|
*/
|
|
uint32_t MemGet::getFreeHeap()
|
|
{
|
|
#ifdef ARCH_ESP32
|
|
return ESP.getFreeHeap();
|
|
#elif defined(ARCH_NRF52)
|
|
return dbgHeapFree();
|
|
#elif defined(ARCH_RP2040)
|
|
return rp2040.getFreeHeap();
|
|
#elif defined(MESHTASTIC_DYNAMIC_SBRK_HEAP) // Currently: ARCH_STM32WL
|
|
struct mallinfo m = mallinfo();
|
|
return m.fordblks + sbrkHeadroom(); // Free space within arena + uncommitted sbrk headroom
|
|
#else
|
|
// this platform does not have heap management function implemented
|
|
return UINT32_MAX;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Returns the size of the heap memory in bytes.
|
|
* @return uint32_t The size of the heap memory in bytes.
|
|
*/
|
|
uint32_t MemGet::getHeapSize()
|
|
{
|
|
#ifdef ARCH_ESP32
|
|
return ESP.getHeapSize();
|
|
#elif defined(ARCH_NRF52)
|
|
return dbgHeapTotal();
|
|
#elif defined(ARCH_RP2040)
|
|
return rp2040.getTotalHeap();
|
|
#elif defined(MESHTASTIC_DYNAMIC_SBRK_HEAP) // Currently: ARCH_STM32WL
|
|
struct mallinfo m = mallinfo();
|
|
return m.arena + sbrkHeadroom(); // Non-mmapped space allocated + uncommitted sbrk headroom
|
|
#else
|
|
// this platform does not have heap management function implemented
|
|
return UINT32_MAX;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Returns the amount of free psram memory in bytes.
|
|
*
|
|
* @return The amount of free psram memory in bytes.
|
|
*/
|
|
uint32_t MemGet::getFreePsram()
|
|
{
|
|
#ifdef ARCH_ESP32
|
|
return ESP.getFreePsram();
|
|
#elif defined(ARCH_PORTDUINO)
|
|
return 4194252;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the size of the PSRAM memory.
|
|
*
|
|
* @return uint32_t The size of the PSRAM memory.
|
|
*/
|
|
uint32_t MemGet::getPsramSize()
|
|
{
|
|
#ifdef ARCH_ESP32
|
|
return ESP.getPsramSize();
|
|
#elif defined(ARCH_PORTDUINO)
|
|
return 4194252;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void displayPercentHeapFree()
|
|
{
|
|
uint32_t freeHeap = memGet.getFreeHeap();
|
|
uint32_t totalHeap = memGet.getHeapSize();
|
|
if (totalHeap == 0 || totalHeap == UINT32_MAX) {
|
|
LOG_INFO("Heap size unavailable");
|
|
return;
|
|
}
|
|
int percent = (int)((freeHeap * 100) / totalHeap);
|
|
LOG_INFO("Heap free: %d%% (%u/%u bytes)", percent, freeHeap, totalHeap);
|
|
memaudit::logBreakdown("heap"); // per-subsystem breakdown rides along with the periodic heap log
|
|
} |