From 767d99546654daa3218efaca6c3ebe4e6075f47b Mon Sep 17 00:00:00 2001 From: dongheng Date: Thu, 22 Aug 2019 20:11:59 +0800 Subject: [PATCH] feat(esp8266): refactor ESP8266(xtensa lx106) panic backtrace function --- components/esp8266/CMakeLists.txt | 1 + .../esp8266/include/esp8266/backtrace.h | 61 +++++ .../esp8266/include/esp8266/eagle_soc.h | 4 +- components/esp8266/source/backtrace.c | 119 +++++++++ components/freertos/port/esp8266/panic.c | 237 +++++++----------- components/freertos/port/esp8266/port.c | 3 - 6 files changed, 269 insertions(+), 156 deletions(-) create mode 100644 components/esp8266/include/esp8266/backtrace.h create mode 100644 components/esp8266/source/backtrace.c diff --git a/components/esp8266/CMakeLists.txt b/components/esp8266/CMakeLists.txt index ee344914..36c8ee0d 100644 --- a/components/esp8266/CMakeLists.txt +++ b/components/esp8266/CMakeLists.txt @@ -23,6 +23,7 @@ else() set(srcs "source/chip_boot.c" + "source/backtrace.c" "source/esp_err_to_name.c" "source/esp_timer.c" "source/esp_wifi_os_adapter.c" diff --git a/components/esp8266/include/esp8266/backtrace.h b/components/esp8266/include/esp8266/backtrace.h new file mode 100644 index 00000000..e2475fea --- /dev/null +++ b/components/esp8266/include/esp8266/backtrace.h @@ -0,0 +1,61 @@ +// Copyright 2019-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Check if PC pointer locates at code section + * + * @param pc the PC pointer + * + * @return 1 if valid or 0 if invalid + */ +int xt_pc_is_valid(const void *pc); + +/** + * @brief Detected recursively and serach the previous PC and SP + * + * @param i_pc the PC address for forward recursive detection + * @param i_sp the SP address for forward recursive detection + * @param i_lr the LR address, maybe it the previous PC address + * @param o_pc the detected previous PC address + * @param o_pc the detected previous SP address + * + * @return 1 if found or 0 if not found + */ +int xt_retaddr_callee(const void *i_pc, const void *i_sp, const void *i_lr, void **o_pc, void **o_sp); + +/** + * @brief These functions may be used to get information about the callers of a function. + * + * Using this API instead of "__builtin_return_address". + * + * This function returns the return address of the current function, or of one of its callers. + * The level argument is number of frames to scan up the call stack. A value of 0 yields the + * return address of the current function, a value of 1 yields the return address of the caller + * of the current function, and so forth.. + * + * @param lvl caller level + * + * @return the return address of the current function + */ +void *xt_return_address(int lvl); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp8266/include/esp8266/eagle_soc.h b/components/esp8266/include/esp8266/eagle_soc.h index 1c2d2ac9..2777a9a0 100644 --- a/components/esp8266/include/esp8266/eagle_soc.h +++ b/components/esp8266/include/esp8266/eagle_soc.h @@ -25,7 +25,9 @@ #ifndef _EAGLE_SOC_H_ #define _EAGLE_SOC_H_ +#include "sdkconfig.h" #include +#include #include "driver/soc.h" /* IO definitions (access restrictions to peripheral registers) */ @@ -190,7 +192,7 @@ #define DRAM_SIZE (96 * 1024) #define IRAM_BASE (0x40100000) -#define IRAM_SIZE (48 * 1024) +#define IRAM_SIZE (CONFIG_SOC_IRAM_SIZE) #define FLASH_BASE (0x40200000) #define FLASH_SIZE (1 * 1024 * 1024) diff --git a/components/esp8266/source/backtrace.c b/components/esp8266/source/backtrace.c new file mode 100644 index 00000000..beec6b3d --- /dev/null +++ b/components/esp8266/source/backtrace.c @@ -0,0 +1,119 @@ +// Copyright 2019-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "esp8266/eagle_soc.h" + +static inline uint32_t prev_text_size(const uint32_t pc) +{ + uint32_t size; + extern uint32_t _text_start, _text_end; + + if (pc > (uint32_t)&_text_start && pc < (uint32_t)&_text_end) { + size = pc - (uint32_t )&_text_start; + } else if (IS_IRAM(pc)) { + size = pc - IRAM_BASE; + } else { + size = 0; + } + + return size; +} + +int xt_pc_is_valid(const void *pc) +{ + return prev_text_size((uint32_t)pc) ? 1 : 0; +} + +int xt_retaddr_callee(const void *i_pc, const void *i_sp, const void *i_lr, void **o_pc, void **o_sp) +{ + uint32_t lr = (uint32_t)i_lr; + uint32_t pc = (uint32_t)i_pc; + uint32_t sp = (uint32_t)i_sp; + + uint32_t off = 0; + const uint32_t text_size = prev_text_size(pc); + + for (; off < text_size; off++) { + if ((*(uint8_t *)(pc - off) == 0x12) && + (*(uint8_t *)(pc - off + 1) == 0xc1)) { + const int8_t stk_size = *(int8_t *)(pc - off + 2); + + if (stk_size >= 0 || stk_size % 16 != 0) { + continue; + } + + sp -= stk_size; + pc = *(uint32_t *)(sp - 4); + + *o_sp = (void *)sp; + *o_pc = (void *)pc; + + break; + } else if ((*(uint8_t *)(pc - off) == 0x92) && + (((*(uint8_t *)(pc - off + 1)) & 0xf0) == 0xa0) && + (*(uint8_t *)(pc - off + 3) == 0x90) && + (*(uint8_t *)(pc - off + 4) == 0x11) && + (*(uint8_t *)(pc - off + 5) == 0xc0)) { + const uint16_t stk_size = ((*(uint8_t *)(pc - off + 1)) & 0x0f) + (*(uint8_t *)(pc - off + 2)); + + if (!stk_size || stk_size >= 2048) { + continue; + } + + sp += stk_size; + pc = *(uint32_t *)(sp - 4); + + *o_sp = (void *)sp; + *o_pc = (void *)pc; + + break; + } else if ((*(uint8_t *)(pc - off) == 0x0d) && + (*(uint8_t *)(pc - off + 1) == 0xf0)) { + pc = lr; + + *o_sp = (void *)sp; + *o_pc = (void *)pc; + + break; + } + } + + return off < text_size ? (xt_pc_is_valid(*o_pc) ? 1 : 0) : 0; +} + +void *xt_return_address(int lvl) +{ + void *i_sp; + void *i_lr = NULL; + void *i_pc = (void *)((uint32_t)xt_return_address + 32); + + void *o_pc = NULL; + void *o_sp; + + __asm__ __volatile__( + "mov %0, a1\n" + : "=a"(i_sp) + : + : "memory" + ); + + lvl += 2; + while(lvl-- && xt_retaddr_callee(i_pc, i_sp, i_lr, &o_pc, &o_sp)) { + i_pc = o_pc; + i_sp = o_sp; + } + + return xt_pc_is_valid(o_pc) ? o_pc : NULL; +} diff --git a/components/freertos/port/esp8266/panic.c b/components/freertos/port/esp8266/panic.c index 036a9329..0d2dac9a 100644 --- a/components/freertos/port/esp8266/panic.c +++ b/components/freertos/port/esp8266/panic.c @@ -18,205 +18,138 @@ #include "esp_libc.h" #include "esp_system.h" -#include "esp8266/eagle_soc.h" #include "esp8266/rom_functions.h" +#include "esp8266/backtrace.h" #include "rom/ets_sys.h" #include "esp_err.h" #include "FreeRTOS.h" -#include "task.h" -#include "private/list.h" -#include "private/portable.h" -#define STACK_VOL_NUM 16 +#define PANIC(_fmt, ...) ets_printf(_fmt, ##__VA_ARGS__) #ifndef CONFIG_ESP_PANIC_SILENT_REBOOT -#ifndef DISABLE_FREERTOS -/* - * @Note: If freeRTOS is updated, the structure must be checked. - */ -typedef struct task_info + +typedef struct panic_frame { + uint32_t exit; + + uint32_t pc; + uint32_t ps; + + uint32_t a0; + uint32_t a1; + uint32_t a2; + uint32_t a3; + + uint32_t a4; + uint32_t a5; + uint32_t a6; + uint32_t a7; + + uint32_t a8; + uint32_t a9; + uint32_t a10; + uint32_t a11; + + uint32_t a12; + uint32_t a13; + uint32_t a14; + uint32_t a15; + + uint32_t sar; + uint32_t exccause; +} panic_frame_t; + +static inline void panic_frame(panic_frame_t *frame) { - volatile StackType_t *pxTopOfStack; - -#if ( portUSING_MPU_WRAPPERS == 1 ) - xMPU_SETTINGS xMPUSettings; -#endif - - ListItem_t xStateListItem; - ListItem_t xEventListItem; - UBaseType_t uxPriority; - StackType_t *pxStack; - char pcTaskName[ configMAX_TASK_NAME_LEN ]; - -#if configRECORD_STACK_HIGH_ADDRESS != 1 -#error "configRECORD_STACK_HIGH_ADDRESS must enable" -#endif - -#if portSTACK_GROWTH >= 0 -#error "Task stack must decrease growing." -#endif - - StackType_t *pxEndOfStack; -} task_info_t; - -static inline uint32_t *__get_stack_head(void *task) -{ - return (uint32_t *)((task_info_t *)task)->pxStack; -} - -static inline uint32_t *__get_stack_tail(void *task) -{ - return (uint32_t *)((task_info_t *)task)->pxEndOfStack; -} -#else -typedef void* task_info_t; - -extern uint32_t *__get_stack_head(void *task); -extern uint32_t *__get_stack_tail(void *task); -#endif - -static void panic_stack(const uint32_t *reg, const uint32_t *start_stk, const uint32_t *end_stk) -{ - const uint32_t *stk_ptr = (const uint32_t *)reg[4]; - - if (stk_ptr <= start_stk || stk_ptr >= end_stk) { - ets_printf("register map is %x error\n", stk_ptr); - } else { -#ifndef CONFIG_PANIC_FULL_STACK - start_stk = (const uint32_t *)((uint32_t)stk_ptr & (~(STACK_VOL_NUM * sizeof(const uint32_t *) - 1))); -#endif - } - - size_t size = end_stk - start_stk + 1; - - if (size < STACK_VOL_NUM) { - start_stk = start_stk - (STACK_VOL_NUM - size); - size = STACK_VOL_NUM; - } - - ets_printf("%10s", " "); - for (int i = 0; i < STACK_VOL_NUM; i++) { - ets_printf(" %8x ", i * sizeof(void *)); - } - ets_printf("\r\n\r\n"); - - for (int i = 0; i < size; i += STACK_VOL_NUM) { - size_t len = size > i ? size - i : STACK_VOL_NUM - (i - size); - - if (len > STACK_VOL_NUM) - len = STACK_VOL_NUM; - - ets_printf("%08x ", start_stk + i); - - for (int j = 0; j < len; j++) { - ets_printf("0x%08x ", start_stk[i + j]); - } - ets_printf("\r\n"); - } -} - -/* - * @brief output xtensa register value map when crash - * - * @param frame xtensa register value map pointer - * - * @return none - */ -static __attribute__((noreturn)) void panic_info(void *frame, int wdt) -{ - extern int _chip_nmi_cnt; - extern int __g_is_task_overflow; - - task_info_t *task; - uint32_t *regs = (uint32_t *)frame; - int x, y; - const char *sdesc[] = { + static const char *sdesc[] = { "PC", "PS", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "A11", "A12", "A13", "A14", "A15", "SAR", "EXCCAUSE" }; + static const char *edesc[] = { + "IllegalInstruction", "Syscall", "InstructionFetchError", "LoadStoreError", + "Level1Interrupt", "Alloca", "IntegerDivideByZero", "PCValue", + "Privileged", "LoadStoreAlignment", "res", "res", + "InstrPDAddrError", "LoadStorePIFDataError", "InstrPIFAddrError", "LoadStorePIFAddrError", + "InstTLBMiss", "InstTLBMultiHit", "InstFetchPrivilege", "res", + "InstrFetchProhibited", "res", "res", "res", + "LoadStoreTLBMiss", "LoadStoreTLBMultihit", "LoadStorePrivilege", "res", + "LoadProhibited", "StoreProhibited", + }; - ets_printf("\r\n\r\n"); + void *i_lr = (void *)frame->a0; + void *i_pc = (void *)frame->pc; + void *i_sp = (void *)frame->a1; - if (wdt) { - ets_printf("Task watchdog got triggered.\r\n\r\n"); - } - - if (_chip_nmi_cnt) { - extern const uint32_t _chip_nmi_stk, LoadStoreErrorHandlerStack; + void *o_pc; + void *o_sp; - _chip_nmi_cnt = 0; - ets_printf("Core 0 was running in NMI context:\r\n\r\n"); + const char *reason = frame->exccause < 30 ? edesc[frame->exccause] : "unknown"; + const uint32_t *regs = (const uint32_t *)frame; - panic_stack(regs, &_chip_nmi_stk, &LoadStoreErrorHandlerStack); - } else { - if (xPortInIsrContext() && !wdt && !__g_is_task_overflow) { - extern const uint32_t _chip_interrupt_stk, _chip_interrupt_tmp; + PANIC("Guru Meditation Error: Core 0 panic'ed (%s). Exception was unhandled.\r\n", reason); + PANIC("Core 0 register dump:\n"); - ets_printf("Core 0 was running in ISR context:\r\n\r\n"); - - panic_stack(regs, &_chip_interrupt_stk, &_chip_interrupt_tmp); - } else { - if ((task = (task_info_t *)xTaskGetCurrentTaskHandle())) { - const uint32_t *pdata = __get_stack_head(task); - const uint32_t *end = __get_stack_tail(task); - - ets_printf("Task stack [%s] stack from [%p] to [%p], total [%d] size\r\n\r\n", task->pcTaskName, - pdata, end, (end - pdata + 1) * sizeof(const uint32_t *)); - - panic_stack(regs, pdata, end); - - ets_printf("\r\n\r\n"); - } else { - ets_printf("No task\r\n\r\n"); - } + for (int i = 0; i < 20; i += 4) { + for (int j = 0; j < 4; j++) { + PANIC("%-8s: 0x%08x ", sdesc[i + j], regs[i + j + 1]); } + PANIC("\r\n"); } - for (x = 0; x < 20; x += 4) { - for (y = 0; y < 4; y++) { - ets_printf("%10s: 0x%08x", sdesc[x + y], regs[x + y + 1]); - } - ets_printf("\r\n"); + PANIC("\r\nBacktrace: %p:%p ", i_pc, i_sp); + while(xt_retaddr_callee(i_pc, i_sp, i_lr, &o_pc, &o_sp)) { + PANIC("%p:%p ", o_pc, o_sp); + i_pc = o_pc; + i_sp = o_sp; } - -#ifdef CONFIG_ESP_PANIC_PRINT_HALT - while (1); -#else - esp_restart(); -#endif + PANIC("\r\n"); } #endif /* !CONFIG_ESP_PANIC_SILENT_REBOOT */ -void __attribute__((noreturn)) panicHandler(void *frame, int wdt) +void panicHandler(void *frame, int wdt) { #ifndef CONFIG_ESP_PANIC_SILENT_REBOOT + extern int _chip_nmi_cnt; + + _chip_nmi_cnt = 0; + /* NMI can interrupt exception. */ vPortEnterCritical(); do { REG_WRITE(INT_ENA_WDEV, 0); } while (REG_READ(INT_ENA_WDEV) != 0); - panic_info(frame, wdt); + if (wdt) { + PANIC("Task watchdog got triggered.\r\n\r\n"); + } + + panic_frame(frame); + +#ifdef CONFIG_ESP_PANIC_PRINT_HALT + while (1); #else esp_restart(); +#endif + +#else /* CONFIG_ESP_PANIC_SILENT_REBOOT */ + esp_restart(); #endif /* !CONFIG_ESP_PANIC_SILENT_REBOOT */ } static void esp_error_check_failed_print(const char *msg, esp_err_t rc, const char *file, int line, const char *function, const char *expression) { - ets_printf("%s failed: esp_err_t 0x%x", msg, rc); + PANIC("%s failed: esp_err_t 0x%x", msg, rc); #ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP - ets_printf(" (%s)", esp_err_to_name(rc)); + PANIC(" (%s)", esp_err_to_name(rc)); #endif //CONFIG_ESP_ERR_TO_NAME_LOOKUP - ets_printf(" at 0x%08x\n", (intptr_t)__builtin_return_address(0) - 3); + PANIC(" at 0x%08x\n", (intptr_t)__builtin_return_address(0) - 3); // ESP8266 put main FreeRTOS code at flash //if (spi_flash_cache_enabled()) { // strings may be in flash cache - ets_printf("file: \"%s\" line %d\nfunc: %s\nexpression: %s\n", file, line, function, expression); + PANIC("file: \"%s\" line %d\nfunc: %s\nexpression: %s\n", file, line, function, expression); //} } diff --git a/components/freertos/port/esp8266/port.c b/components/freertos/port/esp8266/port.c index f82347cc..830bc1dc 100644 --- a/components/freertos/port/esp8266/port.c +++ b/components/freertos/port/esp8266/port.c @@ -57,8 +57,6 @@ uint32_t cpu_sr; uint32_t _xt_tick_divisor; -int __g_is_task_overflow; - /* Each task maintains its own interrupt status in the critical nesting variable. */ static uint32_t uxCriticalNesting = 0; @@ -368,7 +366,6 @@ int xPortInIsrContext(void) void __attribute__((weak, noreturn)) vApplicationStackOverflowHook(xTaskHandle xTask, const char *pcTaskName) { ets_printf("***ERROR*** A stack overflow in task %s has been detected.\r\n", pcTaskName); - __g_is_task_overflow = 1; abort(); }