diff --git a/src/Power.cpp b/src/Power.cpp index e43fe77c9..28c8a7d5a 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -86,6 +86,12 @@ #define DELAY_FOREVER portMAX_DELAY #endif +// How often the free-heap line is written to the debug log. The Power thread polls every +// 20s once it is initialized, so that is the effective granularity. Set to 0 to disable. +#ifndef HEAP_LOG_INTERVAL_MS +#define HEAP_LOG_INTERVAL_MS (5 * 60 * 1000) +#endif + #if defined(BATTERY_PIN) && defined(ARCH_ESP32) #ifndef BAT_MEASURE_ADC_UNIT // ADC1 is default @@ -1075,9 +1081,42 @@ void Power::readPowerStatus() } } +/** + * Emit a free-heap line to the debug log every HEAP_LOG_INTERVAL_MS, so a slow leak shows up + * as a trend in a field log instead of only as an out-of-memory reboot. Unlike the DEBUG_HEAP + * instrumentation above this is always on, and costs one line per interval. + */ +void Power::logHeapUsage() +{ +#if HEAP_LOG_INTERVAL_MS > 0 + if (Throttle::isWithinTimespanMs(lastHeapLogTime, HEAP_LOG_INTERVAL_MS)) + return; + + const uint32_t heapTotal = memGet.getHeapSize(); + // Platforms without heap accounting report UINT32_MAX (or 0) - nothing worth logging + if (heapTotal == 0 || heapTotal == UINT32_MAX) + return; + + const uint32_t heapFree = memGet.getFreeHeap(); + // The first line has no earlier sample to difference against + const int32_t delta = lastHeapLogTime ? (int32_t)(heapFree - lastHeapLogFree) : 0; + + const uint32_t psramTotal = memGet.getPsramSize(); + if (psramTotal) + LOG_INFO("Heap: %u/%u bytes free (%d since last), PSRAM: %u/%u bytes free", heapFree, heapTotal, delta, + memGet.getFreePsram(), psramTotal); + else + LOG_INFO("Heap: %u/%u bytes free (%d since last)", heapFree, heapTotal, delta); + + lastHeapLogFree = heapFree; + lastHeapLogTime = millis(); +#endif +} + int32_t Power::runOnce() { readPowerStatus(); + logHeapUsage(); #ifdef HAS_PMU // WE no longer use the IRQ line to wake the CPU (due to false wakes from diff --git a/src/Power.h b/src/Power.h index a4243a306..38a65b081 100644 --- a/src/Power.h +++ b/src/Power.h @@ -93,6 +93,7 @@ class Power : public concurrency::OSThread void powerCommandsCheck(); void readPowerStatus(); + void logHeapUsage(); virtual bool setup(); virtual int32_t runOnce() override; void setStatusHandler(meshtastic::PowerStatus *handler) { statusHandler = handler; } @@ -131,6 +132,9 @@ class Power : public concurrency::OSThread // open circuit voltage lookup table uint8_t low_voltage_counter; uint32_t lastLogTime = 0; + // Periodic free-heap logging: time of the last line emitted, and the reading it carried + uint32_t lastHeapLogTime = 0; + uint32_t lastHeapLogFree = 0; #ifdef ARCH_ESP32 // Get notified when lightsleep begins and ends