4#ifdef USE_ESP32_CRASH_HANDLER
12#include <esp_private/panic_internal.h>
15#if CONFIG_IDF_TARGET_ARCH_XTENSA
16#include <esp_cpu_utils.h>
17#include <esp_debug_helpers.h>
18#include <xtensa_context.h>
19#elif CONFIG_IDF_TARGET_ARCH_RISCV
20#include <riscv/rvruntime-frames.h>
23static constexpr uint32_t CRASH_MAGIC = 0xDEADBEEF;
24static constexpr size_t MAX_BACKTRACE = 16;
28static inline bool IRAM_ATTR is_code_addr(
uint32_t addr) {
29 return (addr >= SOC_IROM_LOW && addr < SOC_IROM_HIGH) || (addr >= SOC_IRAM_LOW && addr < SOC_IRAM_HIGH);
32#if CONFIG_IDF_TARGET_ARCH_RISCV
36static inline bool is_return_addr(
uint32_t addr) {
37 if (!is_code_addr(addr) || addr < 4)
45 memcpy(&inst, (
const void *) (addr - 4),
sizeof(inst));
50 if ((opcode == 0x6f || opcode == 0x67) && rd == 0x80)
56 uint16_t c_inst = *(uint16_t *) (addr - 2);
57 if ((c_inst & 0xf07f) == 0x9002 && (c_inst & 0x0f80) != 0)
67#if CONFIG_IDF_TARGET_ARCH_XTENSA
70static uint8_t IRAM_ATTR walk_xtensa_backtrace(XtExcFrame *frame,
uint32_t *out, uint8_t max) {
71 esp_backtrace_frame_t bt_frame = {
78 uint32_t first_pc = esp_cpu_process_stack_pc(bt_frame.pc);
79 if (is_code_addr(first_pc)) {
80 out[count++] = first_pc;
82 while (count < max && bt_frame.next_pc != 0) {
83 if (!esp_backtrace_get_next_frame(&bt_frame))
85 uint32_t pc = esp_cpu_process_stack_pc(bt_frame.pc);
86 if (is_code_addr(
pc)) {
94#if CONFIG_IDF_TARGET_ARCH_RISCV
97static uint8_t IRAM_ATTR capture_riscv_backtrace(RvExcFrame *frame,
uint32_t *out, uint8_t max, uint8_t *reg_count) {
99 if (is_code_addr(frame->mepc)) {
100 out[count++] = frame->mepc;
102 if (is_code_addr(frame->ra) && frame->ra != frame->mepc) {
103 out[count++] = frame->ra;
108 for (
uint32_t i = 0; i < 64 && count < max; i++) {
110 if (is_code_addr(
val) &&
val != frame->mepc &&
val != frame->ra) {
125static constexpr uint32_t CRASH_DATA_VERSION = 2;
130 uint8_t backtrace_count;
131 uint8_t reg_frame_count;
133 uint8_t pseudo_excause;
136 uint8_t crashed_core;
137#if SOC_CPU_CORES_NUM > 1
138 static_assert(SOC_CPU_CORES_NUM == 2,
"Dual-core logic assumes exactly 2 cores");
139 uint8_t other_backtrace_count;
140 uint8_t other_reg_frame_count;
141 uint32_t other_backtrace[MAX_BACKTRACE];
148static
bool s_crash_data_valid = false;
152static const char *
const TAG =
"esp32.crash";
155 if (s_raw_crash_data.magic == CRASH_MAGIC && s_raw_crash_data.version == CRASH_DATA_VERSION) {
156 s_crash_data_valid =
true;
158 if (s_raw_crash_data.backtrace_count > MAX_BACKTRACE)
159 s_raw_crash_data.backtrace_count = MAX_BACKTRACE;
160 if (s_raw_crash_data.reg_frame_count > s_raw_crash_data.backtrace_count)
161 s_raw_crash_data.reg_frame_count = s_raw_crash_data.backtrace_count;
162 if (s_raw_crash_data.exception > 4)
163 s_raw_crash_data.exception = 4;
164 if (s_raw_crash_data.pseudo_excause > 1)
165 s_raw_crash_data.pseudo_excause = 0;
166 if (s_raw_crash_data.crashed_core >= SOC_CPU_CORES_NUM)
167 s_raw_crash_data.crashed_core = 0;
168#if SOC_CPU_CORES_NUM > 1
169 if (s_raw_crash_data.other_backtrace_count > MAX_BACKTRACE)
170 s_raw_crash_data.other_backtrace_count = MAX_BACKTRACE;
171 if (s_raw_crash_data.other_reg_frame_count > s_raw_crash_data.other_backtrace_count)
172 s_raw_crash_data.other_reg_frame_count = s_raw_crash_data.other_backtrace_count;
185 s_raw_crash_data.magic = 0;
191static const char *get_exception_reason() {
192#if CONFIG_IDF_TARGET_ARCH_XTENSA
193 if (s_raw_crash_data.pseudo_excause) {
196 static const char *
const PSEUDO_REASON[] = {
198 "Unhandled debug exception",
200 "Unhandled kernel exception",
201 "Coprocessor exception",
202 "Interrupt wdt timeout on CPU0",
203 "Interrupt wdt timeout on CPU1",
206 uint32_t cause = s_raw_crash_data.cause;
207 if (cause <
sizeof(PSEUDO_REASON) /
sizeof(PSEUDO_REASON[0]))
208 return PSEUDO_REASON[cause];
209 return PSEUDO_REASON[0];
212 static const char *
const REASON[] = {
213 "IllegalInstruction",
215 "InstructionFetchError",
219 "IntegerDivideByZero",
222 "LoadStoreAlignment",
226 "LoadStorePIFDataError",
228 "LoadStorePIFAddrError",
231 "InstFetchPrivilege",
233 "InstrFetchProhibited",
238 "LoadStoreTLBMultihit",
239 "LoadStorePrivilege",
244 uint32_t cause = s_raw_crash_data.cause;
245 if (cause <
sizeof(REASON) /
sizeof(REASON[0]) && REASON[cause] !=
nullptr)
246 return REASON[cause];
247#elif CONFIG_IDF_TARGET_ARCH_RISCV
251 if (s_raw_crash_data.pseudo_excause)
253 static const char *
const REASON[] = {
254 "Instruction address misaligned",
255 "Instruction access fault",
256 "Illegal instruction",
258 "Load address misaligned",
260 "Store address misaligned",
261 "Store access fault",
262 "Environment call from U-mode",
263 "Environment call from S-mode",
265 "Environment call from M-mode",
266 "Instruction page fault",
271 uint32_t cause = s_raw_crash_data.cause;
272 if (cause <
sizeof(REASON) /
sizeof(REASON[0]) && REASON[cause] !=
nullptr)
273 return REASON[cause];
279static const char *get_exception_type() {
280 static const char *
const TYPES[] = {
287 uint8_t exc = s_raw_crash_data.exception;
288 if (exc <
sizeof(TYPES) /
sizeof(TYPES[0]))
294static void log_backtrace(
const uint32_t *addrs, uint8_t count, uint8_t reg_frame_count) {
296 for (uint8_t i = 0; i < count; i++) {
298#if CONFIG_IDF_TARGET_ARCH_RISCV
299 if (i >= reg_frame_count && !is_return_addr(addr))
301 const char *source = (i < reg_frame_count) ?
"backtrace" :
"stack scan";
303 const char *source =
"backtrace";
305 ESP_LOGE(TAG,
" BT%d: 0x%08" PRIX32
" (%s)", bt_num++, addr, source);
310static int append_addrs_to_hint(
char *buf,
int size,
int pos,
const uint32_t *addrs, uint8_t count,
311 uint8_t reg_frame_count) {
312 for (uint8_t i = 0; i < count &&
pos <
size - 12; i++) {
314#if CONFIG_IDF_TARGET_ARCH_RISCV
315 if (i >= reg_frame_count && !is_return_addr(addr))
318 pos += snprintf(buf + pos, size - pos,
" 0x%08" PRIX32, addr);
329 if (!s_crash_data_valid)
332 ESP_LOGE(TAG,
"*** CRASH DETECTED ON PREVIOUS BOOT ***");
333 const char *reason = get_exception_reason();
334 if (reason !=
nullptr) {
335 ESP_LOGE(TAG,
" Reason: %s - %s", get_exception_type(), reason);
337 ESP_LOGE(TAG,
" Reason: %s", get_exception_type());
339 ESP_LOGE(TAG,
" Crashed core: %d", s_raw_crash_data.crashed_core);
340 ESP_LOGE(TAG,
" PC: 0x%08" PRIX32
" (fault location)", s_raw_crash_data.pc);
341 log_backtrace(s_raw_crash_data.backtrace, s_raw_crash_data.backtrace_count, s_raw_crash_data.reg_frame_count);
343#if SOC_CPU_CORES_NUM > 1
344 if (s_raw_crash_data.other_backtrace_count > 0) {
345 int other_core = 1 - s_raw_crash_data.crashed_core;
346 ESP_LOGE(TAG,
" Other core (%d) backtrace:", other_core);
347 log_backtrace(s_raw_crash_data.other_backtrace, s_raw_crash_data.other_backtrace_count,
348 s_raw_crash_data.other_reg_frame_count);
354 int pos = snprintf(hint,
sizeof(hint),
"Use: addr2line -pfiaC -e firmware.elf 0x%08" PRIX32, s_raw_crash_data.pc);
355 pos = append_addrs_to_hint(hint,
sizeof(hint),
pos, s_raw_crash_data.backtrace, s_raw_crash_data.backtrace_count,
356 s_raw_crash_data.reg_frame_count);
357#if SOC_CPU_CORES_NUM > 1
358 append_addrs_to_hint(hint,
sizeof(hint),
pos, s_raw_crash_data.other_backtrace,
359 s_raw_crash_data.other_backtrace_count, s_raw_crash_data.other_reg_frame_count);
363 ESP_LOGE(TAG,
"%s", hint);
379 s_raw_crash_data.pc = (
uint32_t) info->addr;
380 s_raw_crash_data.backtrace_count = 0;
381 s_raw_crash_data.reg_frame_count = 0;
382 s_raw_crash_data.exception = (uint8_t) info->exception;
383 s_raw_crash_data.pseudo_excause = info->pseudo_excause ? 1 : 0;
384 s_raw_crash_data.crashed_core = (uint8_t) info->core;
385#if SOC_CPU_CORES_NUM > 1
386 s_raw_crash_data.other_backtrace_count = 0;
387 s_raw_crash_data.other_reg_frame_count = 0;
390#if CONFIG_IDF_TARGET_ARCH_XTENSA
392 if (info->frame !=
nullptr) {
393 auto *xt_frame = (XtExcFrame *) info->frame;
394 s_raw_crash_data.cause = xt_frame->exccause;
395 s_raw_crash_data.backtrace_count = walk_xtensa_backtrace(xt_frame, s_raw_crash_data.backtrace, MAX_BACKTRACE);
398#if SOC_CPU_CORES_NUM > 1
402 if (info->core >= 0 && info->core < SOC_CPU_CORES_NUM) {
403 int other_core = 1 - info->core;
404 auto *other_frame = (XtExcFrame *) g_exc_frames[other_core];
405 if (other_frame !=
nullptr) {
406 s_raw_crash_data.other_backtrace_count =
407 walk_xtensa_backtrace(other_frame, s_raw_crash_data.other_backtrace, MAX_BACKTRACE);
412#elif CONFIG_IDF_TARGET_ARCH_RISCV
414 if (info->frame !=
nullptr) {
415 auto *rv_frame = (RvExcFrame *) info->frame;
416 s_raw_crash_data.cause = rv_frame->mcause;
417 s_raw_crash_data.backtrace_count =
418 capture_riscv_backtrace(rv_frame, s_raw_crash_data.backtrace, MAX_BACKTRACE, &s_raw_crash_data.reg_frame_count);
421#if SOC_CPU_CORES_NUM > 1
423 if (info->core >= 0 && info->core < SOC_CPU_CORES_NUM) {
424 int other_core = 1 - info->core;
425 auto *other_frame = (RvExcFrame *) g_exc_frames[other_core];
426 if (other_frame !=
nullptr) {
427 s_raw_crash_data.other_backtrace_count = capture_riscv_backtrace(
428 other_frame, s_raw_crash_data.other_backtrace, MAX_BACKTRACE, &s_raw_crash_data.other_reg_frame_count);
435 s_raw_crash_data.version = CRASH_DATA_VERSION;
436 s_raw_crash_data.magic = CRASH_MAGIC;
struct @65::@66 __attribute__
Wake the main loop task from an ISR. ISR-safe.
void __real_esp_panic_handler(panic_info_t *info)
void IRAM_ATTR __wrap_esp_panic_handler(panic_info_t *info)
bool crash_handler_has_data()
Returns true if crash data was found this boot.
void crash_handler_log()
Log crash data if a crash was detected on previous boot.
void crash_handler_read_and_clear()
Read and validate crash data from NOINIT memory.
void crash_handler_clear()
Clear the magic marker and mark crash data as consumed.