4#ifdef USE_ESP8266_CRASH_HANDLER
12#include <user_interface.h>
20static constexpr uint32_t XTENSA_ADDR_MASK = 0x3FFFFFFF;
21static constexpr uint32_t XTENSA_CODE_BASE = 0x40000000;
24static constexpr uint32_t IRAM_START = 0x40100000;
25static constexpr uint32_t IRAM_END = 0x40108000;
40static inline bool IRAM_ATTR is_code_addr(
uint32_t val) {
41 uint32_t addr = (
val & XTENSA_ADDR_MASK) | XTENSA_CODE_BASE;
42 return (addr >= IRAM_START && addr < IRAM_END) ||
47static inline uint32_t IRAM_ATTR recover_code_addr(
uint32_t val) {
return (
val & XTENSA_ADDR_MASK) | XTENSA_CODE_BASE; }
53static constexpr uint8_t RTC_CRASH_BASE = 174;
54static constexpr size_t MAX_BACKTRACE = 16;
60static constexpr uint8_t CRASH_SENTINEL_BITS = 16;
61static constexpr uint8_t CRASH_VERSION_BITS = 8;
63static constexpr uint16_t CRASH_SENTINEL_VALUE = 0xDEAD;
64static constexpr uint8_t CRASH_VERSION_VALUE = 1;
66static constexpr uint32_t CRASH_SENTINEL =
static_cast<uint32_t>(CRASH_SENTINEL_VALUE) << CRASH_SENTINEL_BITS;
67static constexpr uint32_t CRASH_VERSION =
static_cast<uint32_t>(CRASH_VERSION_VALUE) << CRASH_VERSION_BITS;
68static constexpr uint32_t CRASH_SENTINEL_MASK =
static_cast<uint32_t>(0xFFFF) << CRASH_SENTINEL_BITS;
69static constexpr uint32_t CRASH_VERSION_MASK =
static_cast<uint32_t>(0xFF) << CRASH_VERSION_BITS;
70static constexpr uint32_t CRASH_COUNT_MASK = 0xFF;
81static_assert(
sizeof(RtcCrashData) == 72,
"RtcCrashData must fit in 18 RTC blocks");
85static const char *
const TAG =
"esp8266";
87static inline bool is_crash_reason(
uint32_t reason) {
88 return reason == REASON_WDT_RST || reason == REASON_EXCEPTION_RST || reason == REASON_SOFT_WDT_RST;
98static const LogString *get_exception_cause(
uint32_t cause) {
100 return LOG_STR(
"IllegalInst");
102 return LOG_STR(
"InstFetchErr");
104 return LOG_STR(
"LoadStoreErr");
106 return LOG_STR(
"Level1Int");
108 return LOG_STR(
"DivByZero");
110 return LOG_STR(
"Alignment");
112 return LOG_STR(
"InstFetchProhibit");
114 return LOG_STR(
"LoadProhibit");
116 return LOG_STR(
"StoreProhibit");
120static const LogString *get_reset_reason(
uint32_t reason) {
121 if (reason == REASON_WDT_RST)
122 return LOG_STR(
"Hardware WDT");
123 if (reason == REASON_EXCEPTION_RST)
124 return LOG_STR(
"Exception");
125 if (reason == REASON_SOFT_WDT_RST)
126 return LOG_STR(
"Soft WDT");
127 return LOG_STR(
"Unknown");
133 RtcCrashData rtc_data;
134 if (!system_rtc_mem_read(RTC_CRASH_BASE, &rtc_data,
sizeof(rtc_data)))
137 if ((magic & CRASH_SENTINEL_MASK) != CRASH_SENTINEL || (magic & CRASH_VERSION_MASK) != CRASH_VERSION)
139 uint8_t raw_count = magic & CRASH_COUNT_MASK;
140 if (raw_count > MAX_BACKTRACE)
141 raw_count = MAX_BACKTRACE;
148 for (uint8_t i = 0; i < raw_count && out < max_entries; i++) {
149 uint32_t addr = rtc_data.backtrace[i];
150 if (addr != rtc_data.epc1)
171 ESP_LOGE(TAG,
"*** CRASH DETECTED ON PREVIOUS BOOT ***");
175 static constexpr uint32_t EXCCAUSE_ILLEGAL_INSTRUCTION = 0;
176 static constexpr uint32_t EXCCAUSE_INTEGER_DIVIDE_BY_ZERO = 6;
177 static constexpr uint32_t ROM_DIV_ZERO_ADDR_1 = 0x4000dce5;
178 static constexpr uint32_t ROM_DIV_ZERO_ADDR_2 = 0x4000dd3d;
180 if (exccause == EXCCAUSE_ILLEGAL_INSTRUCTION &&
182 exccause = EXCCAUSE_INTEGER_DIVIDE_BY_ZERO;
184 const LogString *cause = get_exception_cause(exccause);
185 if (cause !=
nullptr) {
186 ESP_LOGE(TAG,
" Reason: %s - %s (exccause=%" PRIu32
")", LOG_STR_ARG(get_reset_reason(
resetInfo.reason)),
187 LOG_STR_ARG(cause), exccause);
189 ESP_LOGE(TAG,
" Reason: %s (exccause=%" PRIu32
")", LOG_STR_ARG(get_reset_reason(
resetInfo.reason)), exccause);
191 ESP_LOGE(TAG,
" PC: 0x%08" PRIX32,
resetInfo.epc1);
192 if (
resetInfo.reason == REASON_EXCEPTION_RST) {
193 ESP_LOGE(TAG,
" EXCVADDR: 0x%08" PRIX32,
resetInfo.excvaddr);
195 for (uint8_t i = 0; i <
bt_count; i++) {
196 ESP_LOGE(TAG,
" BT%d: 0x%08" PRIX32, i,
backtrace[i]);
218 for (; scan <
end && count < MAX_BACKTRACE; scan++) {
220 if (is_code_addr(
val)) {
224 data.backtrace[count++] = addr;
229 data.magic = CRASH_SENTINEL | CRASH_VERSION | count;
231 system_rtc_mem_write(RTC_CRASH_BASE, &data,
sizeof(data));
struct rst_info resetInfo
void _irom0_text_end(void)
struct rst_info resetInfo
void IRAM_ATTR custom_crash_callback(struct rst_info *rst_info, uint32_t stack, uint32_t stack_end)
void _irom0_text_start(void)
bool crash_handler_has_data()
Returns true if the previous boot was a crash (exception, WDT, or soft WDT).
void crash_handler_log()
Log crash data if a crash was detected on previous boot.
uint32_t backtrace[MAX_BACKTRACE]