mirror of
https://github.com/xoseperez/espurna.git
synced 2026-03-08 01:07:06 +01:00
Terminal: heap fragmentation stat (#1740)
* debug: heap fragmentation stat * Rework method detection - return check integral type as a result of detection instead of method type - type-tagging instead of enable_if * fix alias * typo * only need one bool_type instance * Move heap debug into HeapStats header - don't show fragmentation stats on the first call - prettify fragmentation display - pack function params into helper struct - naming * pass external flag instead * add missing prototypes
This commit is contained in:
@@ -66,6 +66,10 @@ extern "C" {
|
||||
#endif
|
||||
}
|
||||
|
||||
void infoMemory(const char* , unsigned int, unsigned int);
|
||||
unsigned int getFreeHeap();
|
||||
unsigned int getInitialFreeHeap();
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Domoticz
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -22,6 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "config/all.h"
|
||||
#include <vector>
|
||||
|
||||
#include "libs/HeapStats.h"
|
||||
|
||||
std::vector<void (*)()> _loop_callbacks;
|
||||
std::vector<void (*)()> _reload_callbacks;
|
||||
|
||||
@@ -65,7 +67,7 @@ void setup() {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// Cache initial free heap value
|
||||
getInitialFreeHeap();
|
||||
setInitialFreeHeap();
|
||||
|
||||
// Serial debug
|
||||
#if DEBUG_SUPPORT
|
||||
@@ -81,6 +83,9 @@ void setup() {
|
||||
// Init persistance
|
||||
settingsSetup();
|
||||
|
||||
// Return bogus free heap value for broken devices
|
||||
// XXX: device is likely to trigger other bugs! tread carefuly
|
||||
wtfHeap(getSetting("wtfHeap", 0).toInt());
|
||||
// Init Serial, SPIFFS and system check
|
||||
systemSetup();
|
||||
|
||||
|
||||
114
code/espurna/libs/HeapStats.h
Normal file
114
code/espurna/libs/HeapStats.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
|
||||
Show extended heap stats when EspClass::getHeapStats() is available
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
struct heap_stats_t {
|
||||
uint32_t available;
|
||||
uint16_t usable;
|
||||
uint8_t frag_pct;
|
||||
};
|
||||
|
||||
namespace EspClass_has_getHeapStats {
|
||||
struct _detector {
|
||||
template<typename T, typename = decltype(
|
||||
std::declval<T>().getHeapStats(0,0,0))>
|
||||
static std::true_type detect(int);
|
||||
|
||||
template<typename>
|
||||
static std::false_type detect(...);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct detector : public _detector {
|
||||
using result = decltype(
|
||||
std::declval<detector>().detect<T>(0));
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct typed_check : public detector<T>::result {
|
||||
};
|
||||
|
||||
typed_check<EspClass> check{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void _getHeapStats(std::true_type&, T& instance, heap_stats_t& stats) {
|
||||
instance.getHeapStats(&stats.available, &stats.usable, &stats.frag_pct);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void _getHeapStats(std::false_type&, T& instance, heap_stats_t& stats) {
|
||||
stats.available = instance.getFreeHeap();
|
||||
stats.usable = 0;
|
||||
stats.frag_pct = 0;
|
||||
}
|
||||
|
||||
void getHeapStats(heap_stats_t& stats) {
|
||||
_getHeapStats(EspClass_has_getHeapStats::check, ESP, stats);
|
||||
}
|
||||
|
||||
// WTF
|
||||
// Calling ESP.getFreeHeap() is making the system crash on a specific
|
||||
// AiLight bulb, but anywhere else it should work as expected
|
||||
static bool _heap_value_wtf = false;
|
||||
|
||||
heap_stats_t getHeapStats() {
|
||||
heap_stats_t stats;
|
||||
if (_heap_value_wtf) {
|
||||
stats.available = 9999;
|
||||
stats.usable = 9999;
|
||||
stats.frag_pct = 0;
|
||||
return stats;
|
||||
}
|
||||
getHeapStats(stats);
|
||||
return stats;
|
||||
}
|
||||
|
||||
void wtfHeap(bool value) {
|
||||
_heap_value_wtf = value;
|
||||
}
|
||||
|
||||
unsigned int getFreeHeap() {
|
||||
return getHeapStats().available;
|
||||
}
|
||||
|
||||
static unsigned int _initial_heap_value = 0;
|
||||
void setInitialFreeHeap() {
|
||||
_initial_heap_value = getFreeHeap();
|
||||
}
|
||||
|
||||
unsigned int getInitialFreeHeap() {
|
||||
if (0 == _initial_heap_value) {
|
||||
setInitialFreeHeap();
|
||||
}
|
||||
return _initial_heap_value;
|
||||
}
|
||||
|
||||
void infoMemory(const char* name, const heap_stats_t& stats) {
|
||||
infoMemory(name, getInitialFreeHeap(), stats.available);
|
||||
}
|
||||
|
||||
void infoHeapStats(const char* name, const heap_stats_t& stats) {
|
||||
DEBUG_MSG_P(
|
||||
PSTR("[MAIN] %-6s: %5u bytes available | %5u bytes lost (%2u%%) | %5u bytes free (%2u%%)\n"),
|
||||
name,
|
||||
stats.available,
|
||||
(stats.available - stats.usable),
|
||||
stats.frag_pct,
|
||||
stats.usable,
|
||||
(100 - stats.frag_pct)
|
||||
);
|
||||
}
|
||||
|
||||
void infoHeapStats(bool show_frag_stats = true) {
|
||||
infoMemory("Heap", getHeapStats());
|
||||
if (show_frag_stats && EspClass_has_getHeapStats::check) {
|
||||
infoHeapStats("Heap", getHeapStats());
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
#include "libs/EmbedisWrap.h"
|
||||
#include <Stream.h>
|
||||
#include "libs/StreamInjector.h"
|
||||
#include "libs/HeapStats.h"
|
||||
|
||||
StreamInjector _serial = StreamInjector(TERMINAL_BUFFER_SIZE);
|
||||
EmbedisWrap embedis(_serial, TERMINAL_BUFFER_SIZE);
|
||||
@@ -124,12 +125,12 @@ void _terminalInitCommand() {
|
||||
});
|
||||
|
||||
terminalRegisterCommand(F("HEAP"), [](Embedis* e) {
|
||||
infoMemory("Heap", getInitialFreeHeap(), getFreeHeap());
|
||||
infoHeapStats();
|
||||
terminalOK();
|
||||
});
|
||||
|
||||
terminalRegisterCommand(F("STACK"), [](Embedis* e) {
|
||||
infoMemory("Stack", 4096, getFreeStack());
|
||||
infoMemory("Stack", CONT_STACKSIZE, getFreeStack());
|
||||
terminalOK();
|
||||
});
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
*/
|
||||
|
||||
#include <Ticker.h>
|
||||
#include "libs/HeapStats.h"
|
||||
|
||||
String getIdentifier() {
|
||||
char buffer[20];
|
||||
@@ -63,26 +64,6 @@ unsigned char getHeartbeatInterval() {
|
||||
return getSetting("hbInterval", HEARTBEAT_INTERVAL).toInt();
|
||||
}
|
||||
|
||||
// WTF
|
||||
// Calling ESP.getFreeHeap() is making the system crash on a specific
|
||||
// AiLight bulb, but anywhere else...
|
||||
unsigned int getFreeHeap() {
|
||||
if (getSetting("wtfHeap", 0).toInt() == 1) return 9999;
|
||||
return ESP.getFreeHeap();
|
||||
}
|
||||
|
||||
unsigned int getInitialFreeHeap() {
|
||||
static unsigned int _heap = 0;
|
||||
if (0 == _heap) {
|
||||
_heap = getFreeHeap();
|
||||
}
|
||||
return _heap;
|
||||
}
|
||||
|
||||
unsigned int getUsedHeap() {
|
||||
return getInitialFreeHeap() - getFreeHeap();
|
||||
}
|
||||
|
||||
String getEspurnaModules() {
|
||||
return FPSTR(espurna_modules);
|
||||
}
|
||||
@@ -186,10 +167,10 @@ namespace Heartbeat {
|
||||
void heartbeat() {
|
||||
|
||||
unsigned long uptime_seconds = getUptime();
|
||||
unsigned int free_heap = getFreeHeap();
|
||||
|
||||
heap_stats_t heap_stats = getHeapStats();
|
||||
|
||||
UNUSED(uptime_seconds);
|
||||
UNUSED(free_heap);
|
||||
UNUSED(heap_stats);
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
unsigned char _heartbeat_mode = getHeartbeatMode();
|
||||
@@ -204,7 +185,7 @@ void heartbeat() {
|
||||
|
||||
if (serial) {
|
||||
DEBUG_MSG_P(PSTR("[MAIN] Uptime: %lu seconds\n"), uptime_seconds);
|
||||
infoMemory("Heap", getInitialFreeHeap(), getFreeHeap());
|
||||
infoHeapStats();
|
||||
#if ADC_MODE_VALUE == ADC_VCC
|
||||
DEBUG_MSG_P(PSTR("[MAIN] Power: %lu mV\n"), ESP.getVcc());
|
||||
#endif
|
||||
@@ -264,7 +245,7 @@ void heartbeat() {
|
||||
#endif
|
||||
|
||||
if (hb_cfg & Heartbeat::Freeheap)
|
||||
mqttSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str());
|
||||
mqttSend(MQTT_TOPIC_FREEHEAP, String(heap_stats.available).c_str());
|
||||
|
||||
if (hb_cfg & Heartbeat::Relay)
|
||||
relayMQTT();
|
||||
@@ -311,7 +292,7 @@ void heartbeat() {
|
||||
idbSend(MQTT_TOPIC_UPTIME, String(uptime_seconds).c_str());
|
||||
|
||||
if (hb_cfg & Heartbeat::Freeheap)
|
||||
idbSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str());
|
||||
idbSend(MQTT_TOPIC_FREEHEAP, String(heap_stats.available).c_str());
|
||||
|
||||
if (hb_cfg & Heartbeat::Rssi)
|
||||
idbSend(MQTT_TOPIC_RSSI, String(WiFi.RSSI()).c_str());
|
||||
@@ -455,11 +436,15 @@ void info() {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static bool show_frag_stats = false;
|
||||
|
||||
infoMemory("EEPROM", SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE - settingsSize());
|
||||
infoMemory("Heap", getInitialFreeHeap(), getFreeHeap());
|
||||
infoMemory("Stack", 4096, getFreeStack());
|
||||
infoHeapStats(show_frag_stats);
|
||||
infoMemory("Stack", CONT_STACKSIZE, getFreeStack());
|
||||
DEBUG_MSG_P(PSTR("\n"));
|
||||
|
||||
show_frag_stats = true;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
DEBUG_MSG_P(PSTR("[MAIN] Boot version: %d\n"), ESP.getBootVersion());
|
||||
|
||||
Reference in New Issue
Block a user