mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-11 02:34:29 +05:00
feat: added crash recorder and ability to save dump
This commit is contained in:
132
src/CrashRecorder.h
Normal file
132
src/CrashRecorder.h
Normal file
@@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "esp_err.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
extern "C" {
|
||||
#include <user_interface.h>
|
||||
}
|
||||
|
||||
// https://github.com/espressif/ESP8266_RTOS_SDK/blob/master/components/esp8266/include/esp_attr.h
|
||||
#define _COUNTER_STRINGIFY(COUNTER) #COUNTER
|
||||
#define _SECTION_ATTR_IMPL(SECTION, COUNTER) __attribute__((section(SECTION "." _COUNTER_STRINGIFY(COUNTER))))
|
||||
#define __NOINIT_ATTR _SECTION_ATTR_IMPL(".noinit", __COUNTER__)
|
||||
#endif
|
||||
|
||||
namespace CrashRecorder {
|
||||
typedef struct {
|
||||
unsigned int data[32];
|
||||
uint8_t length;
|
||||
bool continues;
|
||||
} backtrace_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned int data[4];
|
||||
uint8_t length;
|
||||
} epc_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t core;
|
||||
size_t heap;
|
||||
unsigned long uptime;
|
||||
} ext_t;
|
||||
|
||||
|
||||
__NOINIT_ATTR volatile static backtrace_t backtrace;
|
||||
__NOINIT_ATTR volatile static epc_t epc;
|
||||
__NOINIT_ATTR volatile static ext_t ext;
|
||||
|
||||
uint8_t backtraceMaxLength = sizeof(backtrace.data) / sizeof(*backtrace.data);
|
||||
uint8_t epcMaxLength = sizeof(epc.data) / sizeof(*epc.data);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
void IRAM_ATTR panicHandler(arduino_panic_info_t *info, void *arg) {;
|
||||
ext.core = info->core;
|
||||
ext.heap = ESP.getFreeHeap();
|
||||
ext.uptime = millis() / 1000u;
|
||||
|
||||
// Backtrace
|
||||
backtrace.length = info->backtrace_len < backtraceMaxLength ? info->backtrace_len : backtraceMaxLength;
|
||||
backtrace.continues = false;
|
||||
for (unsigned int i = 0; i < info->backtrace_len; i++) {
|
||||
if (i >= backtraceMaxLength) {
|
||||
backtrace.continues = true;
|
||||
break;
|
||||
}
|
||||
|
||||
backtrace.data[i] = info->backtrace[i];
|
||||
}
|
||||
|
||||
// EPC
|
||||
if (info->pc) {
|
||||
epc.data[0] = (unsigned int) info->pc;
|
||||
epc.length = 1;
|
||||
|
||||
} else {
|
||||
epc.length = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void init() {
|
||||
if (backtrace.length > backtraceMaxLength) {
|
||||
backtrace.length = 0;
|
||||
}
|
||||
|
||||
if (epc.length > epcMaxLength) {
|
||||
epc.length = 0;
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
set_arduino_panic_handler(panicHandler, nullptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
extern "C" void custom_crash_callback(struct rst_info *info, uint32_t stack, uint32_t stack_end) {
|
||||
uint8_t _length = 0;
|
||||
|
||||
CrashRecorder::ext.core = 0;
|
||||
CrashRecorder::ext.heap = ESP.getFreeHeap();
|
||||
CrashRecorder::ext.uptime = millis() / 1000u;
|
||||
|
||||
// Backtrace
|
||||
CrashRecorder::backtrace.continues = false;
|
||||
uint32_t value;
|
||||
for (uint32_t i = stack; i < stack_end; i += 4) {
|
||||
value = *((uint32_t*) i);
|
||||
|
||||
// keep only addresses in code area
|
||||
if ((value >= 0x40000000) && (value < 0x40300000)) {
|
||||
if (_length >= CrashRecorder::backtraceMaxLength) {
|
||||
CrashRecorder::backtrace.continues = true;
|
||||
break;
|
||||
}
|
||||
|
||||
CrashRecorder::backtrace.data[_length++] = value;
|
||||
}
|
||||
}
|
||||
|
||||
CrashRecorder::backtrace.length = _length;
|
||||
|
||||
// EPC
|
||||
_length = 0;
|
||||
if (info->epc1 > 0) {
|
||||
CrashRecorder::epc.data[_length++] = info->epc1;
|
||||
}
|
||||
|
||||
if (info->epc2 > 0) {
|
||||
CrashRecorder::epc.data[_length++] = info->epc2;
|
||||
}
|
||||
|
||||
if (info->epc3 > 0) {
|
||||
CrashRecorder::epc.data[_length++] = info->epc3;
|
||||
}
|
||||
|
||||
CrashRecorder::epc.length = _length;
|
||||
}
|
||||
#endif
|
||||
142
src/PortalTask.h
142
src/PortalTask.h
@@ -504,6 +504,9 @@ protected:
|
||||
bool isConnected = network->isConnected();
|
||||
|
||||
JsonDocument doc;
|
||||
doc["system"]["resetReason"] = getResetReason();
|
||||
doc["system"]["uptime"] = millis() / 1000ul;
|
||||
|
||||
doc["network"]["hostname"] = networkSettings.hostname;
|
||||
doc["network"]["mac"] = network->getStaMac();
|
||||
doc["network"]["connected"] = isConnected;
|
||||
@@ -515,49 +518,124 @@ protected:
|
||||
doc["network"]["gateway"] = isConnected ? network->getStaGateway().toString() : "";
|
||||
doc["network"]["dns"] = isConnected ? network->getStaDns().toString() : "";
|
||||
|
||||
doc["system"]["buildVersion"] = BUILD_VERSION;
|
||||
doc["system"]["buildDate"] = __DATE__ " " __TIME__;
|
||||
doc["system"]["buildEnv"] = BUILD_ENV;
|
||||
doc["system"]["uptime"] = millis() / 1000ul;
|
||||
doc["system"]["totalHeap"] = getTotalHeap();
|
||||
doc["system"]["freeHeap"] = getFreeHeap();
|
||||
doc["system"]["minFreeHeap"] = getFreeHeap(true);
|
||||
doc["system"]["maxFreeBlockHeap"] = getMaxFreeBlockHeap();
|
||||
doc["system"]["minMaxFreeBlockHeap"] = getMaxFreeBlockHeap(true);
|
||||
doc["system"]["resetReason"] = getResetReason();
|
||||
doc["build"]["version"] = BUILD_VERSION;
|
||||
doc["build"]["date"] = __DATE__ " " __TIME__;
|
||||
doc["build"]["env"] = BUILD_ENV;
|
||||
|
||||
doc["heap"]["total"] = getTotalHeap();
|
||||
doc["heap"]["free"] = getFreeHeap();
|
||||
doc["heap"]["minFree"] = getFreeHeap(true);
|
||||
doc["heap"]["maxFreeBlock"] = getMaxFreeBlockHeap();
|
||||
doc["heap"]["minMaxFreeBlock"] = getMaxFreeBlockHeap(true);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
doc["system"]["chipModel"] = esp_is_8285() ? "ESP8285" : "ESP8266";
|
||||
doc["system"]["chipRevision"] = 0;
|
||||
doc["system"]["chipCores"] = 1;
|
||||
doc["system"]["cpuFreq"] = ESP.getCpuFreqMHz();
|
||||
doc["system"]["coreVersion"] = ESP.getCoreVersion();
|
||||
doc["system"]["flashSize"] = ESP.getFlashChipSize();
|
||||
doc["system"]["flashRealSize"] = ESP.getFlashChipRealSize();
|
||||
doc["build"]["core"] = ESP.getCoreVersion();
|
||||
doc["build"]["sdk"] = ESP.getSdkVersion();
|
||||
doc["chip"]["model"] = esp_is_8285() ? "ESP8285" : "ESP8266";
|
||||
doc["chip"]["rev"] = 0;
|
||||
doc["chip"]["cores"] = 1;
|
||||
doc["chip"]["freq"] = ESP.getCpuFreqMHz();
|
||||
doc["flash"]["size"] = ESP.getFlashChipSize();
|
||||
doc["flash"]["realSize"] = ESP.getFlashChipRealSize();
|
||||
#elif ARDUINO_ARCH_ESP32
|
||||
doc["system"]["chipModel"] = ESP.getChipModel();
|
||||
doc["system"]["chipRevision"] = ESP.getChipRevision();
|
||||
doc["system"]["chipCores"] = ESP.getChipCores();
|
||||
doc["system"]["cpuFreq"] = ESP.getCpuFreqMHz();
|
||||
doc["system"]["coreVersion"] = ESP.getSdkVersion();
|
||||
doc["system"]["flashSize"] = ESP.getFlashChipSize();
|
||||
doc["system"]["flashRealSize"] = doc["system"]["flashSize"];
|
||||
doc["build"]["core"] = ESP.getCoreVersion();
|
||||
doc["build"]["sdk"] = ESP.getSdkVersion();
|
||||
doc["chip"]["model"] = ESP.getChipModel();
|
||||
doc["chip"]["rev"] = ESP.getChipRevision();
|
||||
doc["chip"]["cores"] = ESP.getChipCores();
|
||||
doc["chip"]["freq"] = ESP.getCpuFreqMHz();
|
||||
doc["flash"]["size"] = ESP.getFlashChipSize();
|
||||
doc["flash"]["realSize"] = doc["flash"]["size"];
|
||||
#else
|
||||
doc["system"]["chipModel"] = 0;
|
||||
doc["system"]["chipRevision"] = 0;
|
||||
doc["system"]["chipCores"] = 0;
|
||||
doc["system"]["cpuFreq"] = 0;
|
||||
doc["system"]["coreVersion"] = 0;
|
||||
doc["system"]["flashSize"] = 0;
|
||||
doc["system"]["flashRealSize"] = 0;
|
||||
doc["build"]["core"] = 0;
|
||||
doc["build"]["sdk"] = 0;
|
||||
doc["chip"]["model"] = 0;
|
||||
doc["chip"]["rev"] = 0;
|
||||
doc["chip"]["cores"] = 0;
|
||||
doc["chip"]["freq"] = 0;
|
||||
doc["flash"]["size"] = 0;
|
||||
doc["flash"]["realSize"] = 0;
|
||||
#endif
|
||||
|
||||
|
||||
doc.shrinkToFit();
|
||||
|
||||
this->bufferedWebServer->send(200, "application/json", doc);
|
||||
});
|
||||
|
||||
this->webServer->on("/api/debug", HTTP_GET, [this]() {
|
||||
JsonDocument doc;
|
||||
doc["build"]["version"] = BUILD_VERSION;
|
||||
doc["build"]["date"] = __DATE__ " " __TIME__;
|
||||
doc["build"]["env"] = BUILD_ENV;
|
||||
doc["heap"]["total"] = getTotalHeap();
|
||||
doc["heap"]["free"] = getFreeHeap();
|
||||
doc["heap"]["minFree"] = getFreeHeap(true);
|
||||
doc["heap"]["maxFreeBlock"] = getMaxFreeBlockHeap();
|
||||
doc["heap"]["minMaxFreeBlock"] = getMaxFreeBlockHeap(true);
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
auto reason = esp_reset_reason();
|
||||
if (reason != ESP_RST_UNKNOWN && reason != ESP_RST_POWERON && reason != ESP_RST_SW) {
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
auto reason = ESP.getResetInfoPtr()->reason;
|
||||
if (reason != REASON_DEFAULT_RST && reason != REASON_SOFT_RESTART && reason != REASON_EXT_SYS_RST) {
|
||||
#else
|
||||
if (false) {
|
||||
#endif
|
||||
doc["crash"]["reason"] = getResetReason();
|
||||
doc["crash"]["core"] = CrashRecorder::ext.core;
|
||||
doc["crash"]["heap"] = CrashRecorder::ext.heap;
|
||||
doc["crash"]["uptime"] = CrashRecorder::ext.uptime;
|
||||
|
||||
if (CrashRecorder::backtrace.length > 0 && CrashRecorder::backtrace.length <= CrashRecorder::backtraceMaxLength) {
|
||||
String backtraceStr;
|
||||
arr2str(backtraceStr, CrashRecorder::backtrace.data, CrashRecorder::backtrace.length);
|
||||
doc["crash"]["backtrace"]["data"] = backtraceStr;
|
||||
doc["crash"]["backtrace"]["continues"] = CrashRecorder::backtrace.continues;
|
||||
}
|
||||
|
||||
if (CrashRecorder::epc.length > 0 && CrashRecorder::epc.length <= CrashRecorder::epcMaxLength) {
|
||||
String epcStr;
|
||||
arr2str(epcStr, CrashRecorder::epc.data, CrashRecorder::epc.length);
|
||||
doc["crash"]["epc"] = epcStr;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
doc["build"]["core"] = ESP.getCoreVersion();
|
||||
doc["build"]["sdk"] = ESP.getSdkVersion();
|
||||
doc["chip"]["model"] = esp_is_8285() ? "ESP8285" : "ESP8266";
|
||||
doc["chip"]["rev"] = 0;
|
||||
doc["chip"]["cores"] = 1;
|
||||
doc["chip"]["freq"] = ESP.getCpuFreqMHz();
|
||||
doc["flash"]["size"] = ESP.getFlashChipSize();
|
||||
doc["flash"]["realSize"] = ESP.getFlashChipRealSize();
|
||||
#elif ARDUINO_ARCH_ESP32
|
||||
doc["build"]["core"] = ESP.getCoreVersion();
|
||||
doc["build"]["sdk"] = ESP.getSdkVersion();
|
||||
doc["chip"]["model"] = ESP.getChipModel();
|
||||
doc["chip"]["rev"] = ESP.getChipRevision();
|
||||
doc["chip"]["cores"] = ESP.getChipCores();
|
||||
doc["chip"]["freq"] = ESP.getCpuFreqMHz();
|
||||
doc["flash"]["size"] = ESP.getFlashChipSize();
|
||||
doc["flash"]["realSize"] = doc["flash"]["size"];
|
||||
#else
|
||||
doc["build"]["core"] = 0;
|
||||
doc["build"]["sdk"] = 0;
|
||||
doc["chip"]["model"] = 0;
|
||||
doc["chip"]["rev"] = 0;
|
||||
doc["chip"]["cores"] = 0;
|
||||
doc["chip"]["freq"] = 0;
|
||||
doc["flash"]["size"] = 0;
|
||||
doc["flash"]["realSize"] = 0;
|
||||
#endif
|
||||
|
||||
doc.shrinkToFit();
|
||||
|
||||
this->webServer->sendHeader(F("Content-Disposition"), F("attachment; filename=\"debug.json\""));
|
||||
this->bufferedWebServer->send(200, "application/json", doc, true);
|
||||
});
|
||||
|
||||
|
||||
// not found
|
||||
this->webServer->onNotFound([this]() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <Arduino.h>
|
||||
#include "defines.h"
|
||||
#include "strings.h"
|
||||
#include "CrashRecorder.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <FileData.h>
|
||||
#include <LittleFS.h>
|
||||
@@ -45,6 +46,7 @@ MainTask* tMain;
|
||||
|
||||
|
||||
void setup() {
|
||||
CrashRecorder::init();
|
||||
LittleFS.begin();
|
||||
|
||||
Log.setLevel(TinyLogger::Level::VERBOSE);
|
||||
|
||||
16
src/utils.h
16
src/utils.h
@@ -188,6 +188,22 @@ String getResetReason() {
|
||||
return value;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void arr2str(String &str, T arr[], size_t length) {
|
||||
char buffer[12];
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
auto addr = arr[i];
|
||||
if (!addr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sprintf(buffer, "0x%08X ", addr);
|
||||
str.concat(buffer);
|
||||
}
|
||||
|
||||
str.trim();
|
||||
}
|
||||
|
||||
void networkSettingsToJson(const NetworkSettings& src, JsonVariant dst) {
|
||||
dst["hostname"] = src.hostname;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user