Skip to content

Commit

Permalink
Terminal: heap fragmentation stat (#1740)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
mcspr committed Jun 23, 2019
1 parent 0998110 commit dfd3a14
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 31 deletions.
4 changes: 4 additions & 0 deletions code/espurna/config/prototypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ extern "C" {
#endif
}

void infoMemory(const char* , unsigned int, unsigned int);
unsigned int getFreeHeap();
unsigned int getInitialFreeHeap();

// -----------------------------------------------------------------------------
// Domoticz
// -----------------------------------------------------------------------------
Expand Down
7 changes: 6 additions & 1 deletion code/espurna/espurna.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -65,7 +67,7 @@ void setup() {
// -------------------------------------------------------------------------

// Cache initial free heap value
getInitialFreeHeap();
setInitialFreeHeap();

// Serial debug
#if DEBUG_SUPPORT
Expand All @@ -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();

Expand Down
114 changes: 114 additions & 0 deletions code/espurna/libs/HeapStats.h
Original file line number Diff line number Diff line change
@@ -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());
}
}
5 changes: 3 additions & 2 deletions code/espurna/terminal.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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();
});

Expand Down
41 changes: 13 additions & 28 deletions code/espurna/utils.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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();
Expand All @@ -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
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down

0 comments on commit dfd3a14

Please sign in to comment.