From 46dc36233a0242febcf5a9db7a962769fbbb901f Mon Sep 17 00:00:00 2001 From: Sachin Parekh Date: Wed, 5 May 2021 17:03:22 +0530 Subject: [PATCH] coredump: Parse backtrace info for RISCV For RISCV, backtrace generation on device is not possible without including and parsing DWARF sections. We extract the crash task stack and let the host generate the backtrace --- components/espcoredump/Kconfig | 9 ++++ .../espcoredump/include/esp_core_dump.h | 33 ++++++++++----- ...ra_info.h => esp_core_dump_summary_port.h} | 22 +++++++++- ...ra_info.h => esp_core_dump_summary_port.h} | 16 +++++++ .../include_core_dump/esp_core_dump_port.h | 4 ++ components/espcoredump/src/core_dump_elf.c | 4 ++ .../src/port/riscv/core_dump_port.c | 42 +++++++++++++++++-- .../src/port/xtensa/core_dump_port.c | 8 ++++ 8 files changed, 124 insertions(+), 14 deletions(-) rename components/espcoredump/include/port/riscv/{esp_core_dump_summary_extra_info.h => esp_core_dump_summary_port.h} (51%) rename components/espcoredump/include/port/xtensa/{esp_core_dump_summary_extra_info.h => esp_core_dump_summary_port.h} (70%) diff --git a/components/espcoredump/Kconfig b/components/espcoredump/Kconfig index 9134413832..28a54a02a6 100644 --- a/components/espcoredump/Kconfig +++ b/components/espcoredump/Kconfig @@ -90,6 +90,15 @@ menu "Core dump" To ensure that core dump itself will not overflow task/ISR stack set this to the value above 800. NOTE: It eats DRAM. + config ESP_COREDUMP_SUMMARY_STACKDUMP_SIZE + int "Size of the stack dump buffer" + depends on ESP_COREDUMP_DATA_FORMAT_ELF && ESP_COREDUMP_ENABLE_TO_FLASH && IDF_TARGET_ARCH_RISCV + range 512 4096 + default 1024 + help + Size of the buffer that would be reserved for extracting backtrace info summary. + This buffer will contain the stack dump of the crashed task. This dump is useful in generating backtrace + choice ESP_COREDUMP_DECODE prompt "Handling of UART core dumps in IDF Monitor" depends on ESP_COREDUMP_ENABLE_TO_UART diff --git a/components/espcoredump/include/esp_core_dump.h b/components/espcoredump/include/esp_core_dump.h index 7d41b97e57..40fd90d8b2 100644 --- a/components/espcoredump/include/esp_core_dump.h +++ b/components/espcoredump/include/esp_core_dump.h @@ -14,10 +14,11 @@ #ifndef ESP_CORE_DUMP_H_ #define ESP_CORE_DUMP_H_ +#include "sdkconfig.h" #include #include "esp_err.h" #include "esp_private/panic_internal.h" -#include "esp_core_dump_summary_extra_info.h" +#include "esp_core_dump_summary_port.h" #ifdef __cplusplus extern "C" { @@ -25,14 +26,7 @@ extern "C" { #define APP_ELF_SHA256_SZ (CONFIG_APP_RETRIEVE_LEN_ELF_SHA + 1) -/** - * @brief Backtrace information - */ -typedef struct { - uint32_t bt[16]; /*!< Backtrace (array of PC) */ - uint32_t depth; /*!< Number of backtrace entries */ - bool corrupted; /*!< Status flag for backtrace is corrupt or not */ -} esp_core_dump_bt_info_t; +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF /** * @brief Core dump summary, Most meaningful contents of the core dump @@ -48,6 +42,8 @@ typedef struct { esp_core_dump_summary_extra_info_t ex_info; /*!< Architecture specific extra data */ } esp_core_dump_summary_t; +#endif /* CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF */ + /**************************************************************************************/ /******************************** EXCEPTION MODE API **********************************/ /**************************************************************************************/ @@ -137,15 +133,32 @@ esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size); */ esp_err_t esp_core_dump_image_erase(void); +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF + /** - * @brief Get the summary of a core dump. This function works only with ELF format core dumps. + * @brief Get the summary of a core dump. * * @param summary Summary of the core dump * * @return ESP_OK on success, otherwise \see esp_err_t + * + * @note This function works only if coredump is stored in flash and in ELF format + * + * Example usage: + * @code{c} + * esp_core_dump_summary_t *summary = malloc(sizeof(esp_core_dump_summary_t)); + * if (summary) { + * if (esp_core_dump_get_summary(summary) == ESP_OK) { + * // Do stuff + * } + * } + * free(summary); + * @endcode */ esp_err_t esp_core_dump_get_summary(esp_core_dump_summary_t *summary); +#endif /* CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF */ + #ifdef __cplusplus } #endif diff --git a/components/espcoredump/include/port/riscv/esp_core_dump_summary_extra_info.h b/components/espcoredump/include/port/riscv/esp_core_dump_summary_port.h similarity index 51% rename from components/espcoredump/include/port/riscv/esp_core_dump_summary_extra_info.h rename to components/espcoredump/include/port/riscv/esp_core_dump_summary_port.h index 510fdc06af..f2b5d0d9e6 100644 --- a/components/espcoredump/include/port/riscv/esp_core_dump_summary_extra_info.h +++ b/components/espcoredump/include/port/riscv/esp_core_dump_summary_port.h @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. #pragma once +#include "sdkconfig.h" #include #ifdef __cplusplus @@ -19,6 +20,21 @@ extern "C" { #endif +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF + +/** + * @brief Backtrace information + * + * For RISCV, backtrace cannot be generated on device without including and parsing + * DWARF sections. Including these sections would increase the binary size so provide + * the stackdump that can be later used to generate backtrace with the help of GDB or by parsing the ELF file + * on the host machine + */ +typedef struct { + uint8_t stackdump[CONFIG_ESP_COREDUMP_SUMMARY_STACKDUMP_SIZE]; /*!< Stack dump of the crashing task. */ + uint32_t dump_size; /*!< Size (in bytes) of the stack dump */ +} esp_core_dump_bt_info_t; + /** * @brief RISC-V architecture specific extra information */ @@ -27,9 +43,13 @@ typedef struct { uint32_t mtvec; /* Machine Trap-Vector Base Address */ uint32_t mcause; /* Machine Trap Cause */ uint32_t mtval; /* Machine Trap Value */ - uint32_t exc_a[8]; /*!< a register set when the exception caused */ + uint32_t ra; /* Return Address */ + uint32_t sp; /* Stack pointer */ + uint32_t exc_a[8]; /* A0-A7 registers when the exception caused */ } esp_core_dump_summary_extra_info_t; +#endif /* CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF */ + #ifdef __cplusplus } #endif diff --git a/components/espcoredump/include/port/xtensa/esp_core_dump_summary_extra_info.h b/components/espcoredump/include/port/xtensa/esp_core_dump_summary_port.h similarity index 70% rename from components/espcoredump/include/port/xtensa/esp_core_dump_summary_extra_info.h rename to components/espcoredump/include/port/xtensa/esp_core_dump_summary_port.h index 3db7a70be8..815c1fff70 100644 --- a/components/espcoredump/include/port/xtensa/esp_core_dump_summary_extra_info.h +++ b/components/espcoredump/include/port/xtensa/esp_core_dump_summary_port.h @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. #pragma once +#include "sdkconfig.h" #include #include @@ -20,8 +21,21 @@ extern "C" { #endif +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF + #define EPCx_REGISTER_COUNT XCHAL_NUM_INTLEVELS +/** + * @brief Backtrace information. + * + * For Xtensa, backtrace can be generated on device due to windowed register ABI. + */ +typedef struct { + uint32_t bt[16]; /*!< Backtrace (array of PC) */ + uint32_t depth; /*!< Number of backtrace entries */ + bool corrupted; /*!< Status flag for backtrace is corrupt or not */ +} esp_core_dump_bt_info_t; + /** * @brief Xtensa architecture specific extra information */ @@ -33,6 +47,8 @@ typedef struct { uint8_t epcx_reg_bits; /*!< Bit mask of available EPCx registers */ } esp_core_dump_summary_extra_info_t; +#endif /* CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF */ + #ifdef __cplusplus } #endif diff --git a/components/espcoredump/include_core_dump/esp_core_dump_port.h b/components/espcoredump/include_core_dump/esp_core_dump_port.h index 5c0058ca49..b99beb32ec 100644 --- a/components/espcoredump/include_core_dump/esp_core_dump_port.h +++ b/components/espcoredump/include_core_dump/esp_core_dump_port.h @@ -24,6 +24,7 @@ * both Xtensa and RISC-V architecture. */ +#include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "soc/cpu.h" #include "esp_debug_helpers.h" @@ -165,6 +166,8 @@ void esp_core_dump_port_set_crashed_tcb(uint32_t handle); */ uint32_t esp_core_dump_get_extra_info(void **info); +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF + /** * @brief Parse extra information into summary * @@ -191,6 +194,7 @@ void esp_core_dump_summary_parse_exc_regs(esp_core_dump_summary_t *summary, void */ void esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t *bt_info, const void *vaddr, const void *paddr, uint32_t stack_size); +#endif /* CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF */ #ifdef __cplusplus } diff --git a/components/espcoredump/src/core_dump_elf.c b/components/espcoredump/src/core_dump_elf.c index 3dfbc2de1a..58bd691ecf 100644 --- a/components/espcoredump/src/core_dump_elf.c +++ b/components/espcoredump/src/core_dump_elf.c @@ -646,6 +646,8 @@ esp_err_t esp_core_dump_write_elf(core_dump_write_config_t *write_cfg) return err; } +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH + /* Below are the helper function to parse the core dump ELF stored in flash */ static esp_err_t elf_core_dump_image_mmap(spi_flash_mmap_handle_t* core_data_handle, const void **map_addr) @@ -773,4 +775,6 @@ esp_err_t esp_core_dump_get_summary(esp_core_dump_summary_t *summary) return ESP_OK; } +#endif // CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH + #endif //CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF diff --git a/components/espcoredump/src/port/riscv/core_dump_port.c b/components/espcoredump/src/port/riscv/core_dump_port.c index 837427d802..3336cbbc37 100644 --- a/components/espcoredump/src/port/riscv/core_dump_port.c +++ b/components/espcoredump/src/port/riscv/core_dump_port.c @@ -381,6 +381,8 @@ uint32_t esp_core_dump_get_extra_info(void **info) return size; } +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF + void esp_core_dump_summary_parse_extra_info(esp_core_dump_summary_t *summary, void *ei_data) { riscv_extra_info_t *ei = (riscv_extra_info_t *)ei_data; @@ -400,8 +402,10 @@ void esp_core_dump_summary_parse_exc_regs(esp_core_dump_summary_t *summary, void summary->ex_info.mtvec = stack->mtvec; summary->ex_info.mcause = stack->mcause; summary->ex_info.mtval = stack->mtval; - ESP_COREDUMP_LOGD("mstatus:0x%x mtvec:0x%x mcause:0x%x mval:0x%x", - stack->mstatus, stack->mtvec, stack->mcause, stack->mtval); + summary->ex_info.ra = stack->ra; + summary->ex_info.sp = stack->sp; + ESP_COREDUMP_LOGD("mstatus:0x%x mtvec:0x%x mcause:0x%x mval:0x%x RA: 0x%x SP: 0x%x", + stack->mstatus, stack->mtvec, stack->mcause, stack->mtval, stack->ra, stack->sp); a_reg = &stack->a0; for (i = 0; i < 8; i++) { summary->ex_info.exc_a[i] = a_reg[i]; @@ -412,7 +416,39 @@ void esp_core_dump_summary_parse_exc_regs(esp_core_dump_summary_t *summary, void void esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t *bt_info, const void *vaddr, const void *paddr, uint32_t stack_size) { - return; + if (!vaddr || !paddr || !bt_info) { + bt_info->dump_size = 0; + return; + } + + /* Check whether the stack is a fake stack created during coredump generation + * If its a fake stack, we don't have any actual stack dump + */ + if (vaddr >= COREDUMP_FAKE_STACK_START && vaddr < COREDUMP_FAKE_STACK_LIMIT) { + bt_info->dump_size = 0; + return; + } + + /* Top of the stack consists of the context registers saved after crash, + * extract the value of stack pointer (SP) at the time of crash + */ + RvExcFrame *stack = (RvExcFrame *) paddr; + uint32_t *sp = (uint32_t *)stack->sp; + + /* vaddr is actual stack address when crash occurred. However that stack is now saved + * in the flash at a different location. Hence, we need to adjust the offset + * to point to correct data in the flash */ + int offset = (uint32_t)stack - (uint32_t)vaddr; + + // Skip the context saved register frame + uint32_t regframe_size = (uint32_t)sp - (uint32_t)vaddr; + + uint32_t dump_size = MIN(stack_size - regframe_size, CONFIG_ESP_COREDUMP_SUMMARY_STACKDUMP_SIZE); + + memcpy(&bt_info->stackdump[0], (uint8_t *)sp + offset, dump_size); + bt_info->dump_size = dump_size; } +#endif /* #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF */ + #endif diff --git a/components/espcoredump/src/port/xtensa/core_dump_port.c b/components/espcoredump/src/port/xtensa/core_dump_port.c index 35b74d1968..8fdaacb73a 100644 --- a/components/espcoredump/src/port/xtensa/core_dump_port.c +++ b/components/espcoredump/src/port/xtensa/core_dump_port.c @@ -473,6 +473,8 @@ uint32_t esp_core_dump_get_extra_info(void **info) return sizeof(s_extra_info); } +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF + void esp_core_dump_summary_parse_extra_info(esp_core_dump_summary_t *summary, void *ei_data) { int i; @@ -513,6 +515,10 @@ void esp_core_dump_summary_parse_exc_regs(esp_core_dump_summary_t *summary, void void esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t *bt_info, const void *vaddr, const void *paddr, uint32_t stack_size) { + if (!vaddr || !paddr || !bt_info) { + return; + } + int offset; bool corrupted; esp_backtrace_frame_t frame; @@ -559,4 +565,6 @@ void esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t *bt_info bt_info->corrupted = corrupted; } +#endif /* #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF */ + #endif