Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Terminal: heap fragmentation stat #1740

Merged
merged 9 commits into from
Jun 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -60,7 +62,7 @@ void setup() {
// -------------------------------------------------------------------------

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

// Serial debug
#if DEBUG_SUPPORT
Expand All @@ -76,6 +78,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 @@ -202,10 +183,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 @@ -220,7 +201,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 @@ -280,7 +261,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 @@ -327,7 +308,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 @@ -470,11 +451,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