Add save call stack in RTC memory in case of crash

This commit is contained in:
Hadinger 2019-12-07 10:41:29 +01:00
parent f4993736a5
commit 5a0febc64e
7 changed files with 103 additions and 2 deletions

View File

@ -3,6 +3,7 @@
### 7.1.2.2 20191206
- Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config (#7108)
- Add save call stack in RTC memory in case of crash, command ``Status 12`` to dump the stack
### 7.1.2.1 20191206

View File

@ -208,6 +208,7 @@
#define D_STATUS9_MARGIN "PTH"
#define D_STATUS10_SENSOR "SNS"
#define D_STATUS11_STATUS "STS"
#define D_STATUS12_STATUS "STK"
#define D_CMND_STATE "State"
#define D_CMND_POWER "Power"
#define D_CMND_FANSPEED "FanSpeed"
@ -292,6 +293,8 @@
#define D_JSON_FLAG "FLAG"
#define D_JSON_BASE "BASE"
#define D_CMND_TEMPOFFSET "TempOffset"
#define D_CMND_CRASH "Crash"
#define D_JSON_ONE_TO_CRASH "1 to crash"
// Commands xdrv_01_mqtt.ino
#define D_CMND_MQTTLOG "MqttLog"

View File

@ -268,6 +268,9 @@
//#define MY_LANGUAGE zh-CN // Chinese (Simplified) in China
//#define MY_LANGUAGE zh-TW // Chinese (Traditional) in Taiwan
// -- Crash generator ---------------------------
//#define USE_CRASH // add a `Crash` command to test the crash recorder (+48 bytes)
// -- Wifi Config tools ---------------------------
#define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 13 as used by Wifi Manager web GUI

View File

@ -30,7 +30,10 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix
#ifdef USE_I2C
D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|"
#endif
D_CMND_SENSOR "|" D_CMND_DRIVER;
#ifdef USE_CRASH
D_CMND_CRASH "|"
#endif
D_CMND_SENSOR "|" D_CMND_DRIVER ;
void (* const TasmotaCommand[])(void) PROGMEM = {
&CmndBacklog, &CmndDelay, &CmndPower, &CmndStatus, &CmndState, &CmndSleep, &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl,
@ -44,6 +47,9 @@ void (* const TasmotaCommand[])(void) PROGMEM = {
&CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset,
#ifdef USE_I2C
&CmndI2cScan, CmndI2cDriver,
#endif
#ifdef USE_CRASH
&CmndCrash,
#endif
&CmndSensor, &CmndDriver };
@ -483,6 +489,13 @@ void CmndStatus(void)
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11"));
}
if ((0 == payload) || (12 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":"));
CrashDump();
ResponseJsonEnd();
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "12"));
}
#ifdef USE_SCRIPT_STATUS
if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data);
#endif

View File

@ -0,0 +1,80 @@
/*
support_crash_recorder.ino - record the call stack in RTC in cas of crash
Copyright (C) 2019 Stephan Hadinger, Theo Arends,
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
const uint32_t dump_max_len = 64; // dump only 64 call addresses
/**
* Save crash information in RTC memory
* This function is called automatically if ESP8266 suffers an exception
* It should be kept quick / consise to be able to execute before hardware wdt may kick in
*/
extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) {
uint32_t addr_written = 0; // how many addresses have we already written in RTC
uint32_t value; // 4 bytes buffer to write to RTC
for (uint32_t i = stack; i < stack_end; i += 4) {
value = *((uint32_t*) i); // load value from stack
if ((value >= 0x40000000) && (value < 0x40300000)) { // keep only addresses in code area
ESP.rtcUserMemoryWrite(addr_written, (uint32_t*)&value, sizeof(value));
addr_written++;
if (addr_written >= dump_max_len) { break; } // we store only 64 addresses
}
}
// fill the rest of RTC with zeros
value = 0;
while (addr_written < dump_max_len) {
ESP.rtcUserMemoryWrite(addr_written++, (uint32_t*)&value, sizeof(value));
}
}
// Generate a crash to test the crash recorder
void CmndCrash(void)
{
if (1 == XdrvMailbox.payload) {
volatile uint32_t dummy;
dummy = *((uint32_t*) 0x00000000); // invalid address
} else {
ResponseCmndChar(D_JSON_ONE_TO_CRASH);
}
}
// Clear the RTC dump area when we do a normal reboot, this avoids garbage data to stay in RTC
void CrashDumpClear(void) {
uint32_t value = 0;
for (uint32_t i = 0; i < dump_max_len; i++) {
ESP.rtcUserMemoryWrite(i, (uint32_t*)&value, sizeof(value));
}
}
/*********************************************************************************************\
* CmndCrashDump - dump the crash history - called by `Status 12`
\*********************************************************************************************/
void CrashDump(void)
{
ResponseAppend_P(PSTR("{\"call_chain\":["));
for (uint32_t i = 0; i < dump_max_len; i++) {
uint32_t value;
ESP.rtcUserMemoryRead(i, (uint32_t*)&value, sizeof(value));
if ((value >= 0x40000000) && (value < 0x40300000)) {
if (i > 0) { ResponseAppend_P(PSTR(",")); }
ResponseAppend_P(PSTR("\"%08x\""), value);
}
}
ResponseAppend_P(PSTR("]}"));
}

View File

@ -617,6 +617,7 @@ void WifiShutdown(void)
void EspRestart(void)
{
WifiShutdown();
CrashDumpClear(); // Clear the stack dump in RTC
// ESP.restart(); // This results in exception 3 on restarts on core 2.3.0
ESP.reset();
}

View File

@ -131,7 +131,7 @@ const uint32_t SOFT_BAUDRATE = 9600; // Default software serial baudrate
const uint32_t APP_BAUDRATE = 115200; // Default serial baudrate
const uint32_t SERIAL_POLLING = 100; // Serial receive polling in ms
const uint32_t ZIGBEE_POLLING = 100; // Serial receive polling in ms
const uint8_t MAX_STATUS = 11; // Max number of status lines
const uint8_t MAX_STATUS = 12; // Max number of status lines
const uint32_t START_VALID_TIME = 1451602800; // Time is synced and after 2016-01-01