From 4a3e160888792809996c5325a1d6fd9d652b3252 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Thu, 22 Dec 2016 02:56:23 +0300 Subject: [PATCH 1/5] esp32: Add core dump saving to flash feature Complimentary changes: 1) Partition table definitions files with core dump partition 2) Special sub-type for core dump partition 3) Special version of spi_flash_xxx 4) espcoredump.py is script to get core dump from flash and print useful info 5) FreeRTOS API was extended to get tasks snapshots --- components/esp32/Kconfig | 18 + components/esp32/core_dump.c | 276 +++++ components/esp32/cpu_start.c | 5 + components/esp32/include/esp_core_dump.h | 21 + components/esp32/include/esp_panic.h | 4 + components/esp32/panic.c | 125 +- components/espcoredump/espcoredump.py | 1013 +++++++++++++++++ .../freertos/include/freertos/FreeRTOS.h | 4 +- .../include/freertos/FreeRTOSConfig.h | 4 +- components/freertos/include/freertos/task.h | 15 + components/freertos/tasks.c | 104 +- components/partition_table/Kconfig.projbuild | 6 +- .../partitions_singleapp_coredump.csv | 6 + .../partitions_two_ota_coredump.csv | 9 + components/spi_flash/cache_utils.c | 39 + components/spi_flash/cache_utils.h | 9 + components/spi_flash/flash_ops.c | 82 +- components/spi_flash/include/esp_partition.h | 1 + components/spi_flash/include/esp_spi_flash.h | 50 + 19 files changed, 1715 insertions(+), 76 deletions(-) create mode 100644 components/esp32/core_dump.c create mode 100644 components/esp32/include/esp_core_dump.h create mode 100755 components/espcoredump/espcoredump.py create mode 100644 components/partition_table/partitions_singleapp_coredump.csv create mode 100644 components/partition_table/partitions_two_ota_coredump.csv diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index bc35ca0e8f..9811585140 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -54,6 +54,24 @@ config TRACEMEM_RESERVE_DRAM default 0x4000 if MEMMAP_TRACEMEM && !MEMMAP_TRACEMEM_TWOBANKS default 0x0 +choice ESP32_COREDUMP_TO_FLASH_OR_UART + prompt "Core dump destination" + default ESP32_ENABLE_COREDUMP_TO_NONE + help + Select place to store core dump: flash, uart or none (to disable core dumps generation). + + If core dump is configured to be stored in flash and custom partition table is used add + corresponding entry to your CSV. For examples, please see predefined partition table CSV descriptions + in the components/partition_table directory. + +config ESP32_ENABLE_COREDUMP_TO_FLASH + bool "Flash" +config ESP32_ENABLE_COREDUMP_TO_UART + bool "UART" +config ESP32_ENABLE_COREDUMP_TO_NONE + bool "None" +endchoice + # Not implemented and/or needs new silicon rev to work config MEMMAP_SPISRAM bool "Use external SPI SRAM chip as main memory" diff --git a/components/esp32/core_dump.c b/components/esp32/core_dump.c new file mode 100644 index 0000000000..165ec74192 --- /dev/null +++ b/components/esp32/core_dump.c @@ -0,0 +1,276 @@ +// Copyright 2015-2016 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 "freertos/FreeRTOS.h" +#include "freertos/task.h" + +//#include "esp_attr.h" +#include "esp_panic.h" +#include "esp_partition.h" + +#ifdef ESP_PLATFORM +// Uncomment this line to force output from this module +#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG +#include "esp_log.h" +static const char* TAG = "esp_core_dump_init"; +#else +#define ESP_LOGD(...) +#endif + +// TODO: allow user to set this in menuconfig or get tasks iteratively +#define COREDUMP_MAX_TASKS_NUM 32 + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + +// magic numbers to control core dump data consistency +#define COREDUMP_FLASH_MAGIC_START 0xDEADBEEFUL +#define COREDUMP_FLASH_MAGIC_END 0xACDCFEEDUL + +// core dump partition start +static uint32_t s_core_part_start; +// core dump partition size +static uint32_t s_core_part_size; + +static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint32_t data_size) +{ + esp_err_t err; + uint32_t data_len = 0, k, len; + union + { + uint8_t data8[4]; + uint32_t data32; + } rom_data; + + data_len = (data_size / sizeof(uint32_t)) * sizeof(uint32_t); + err = spi_flash_write_panic(off, data, data_len); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write data"); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return 0; + } + + len = data_size % sizeof(uint32_t); + if (len) { + // write last bytes with padding, actual TCB len can be retrieved by esptool from core dump header + rom_data.data32 = 0; + for (k = 0; k < len; k++) + rom_data.data8[k] = *(data + data_len + k); + err = spi_flash_write_panic(off + data_len, &rom_data, sizeof(uint32_t)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write data end"); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return 0; + } + data_len += sizeof(uint32_t); + } + + return data_len; +} + +/* + * | MAGIC1 | + * | TOTAL_LEN | TASKS_NUM | TCB_SIZE | + * | TCB_ADDR_1 | STACK_TOP_1 | STACK_END_1 | TCB_1 | STACK_1 | + * . . . . + * . . . . + * | TCB_ADDR_N | STACK_TOP_N | STACK_END_N | TCB_N | STACK_N | + * | MAGIC2 | + */ +void esp_core_dump_to_flash(XtExcFrame *frame) +{ + union + { + uint8_t data8[16]; + uint32_t data32[4]; + } rom_data; + //const esp_partition_t *core_part; + esp_err_t err; + TaskSnapshot_t tasks[COREDUMP_MAX_TASKS_NUM]; + UBaseType_t tcb_sz, task_num; + uint32_t data_len = 0, i, len, sec_num; + size_t off; + + esp_panicPutStr("Save core dump to flash...\r\n"); + task_num = uxTaskGetSnapshotAll(tasks, COREDUMP_MAX_TASKS_NUM, &tcb_sz); + // take TCB padding into account, actual TCB size will be stored in header + if (tcb_sz % sizeof(uint32_t)) + len = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t); + else + len = tcb_sz; + // header + magic2 + tasknum*(tcb + stack start/end + tcb addr) + data_len = 5*sizeof(uint32_t) + task_num*(len + 2*sizeof(uint32_t) + sizeof(uint32_t *)); + for (i = 0; i < task_num; i++) { + if (tasks[i].pxTCB == xTaskGetCurrentTaskHandle()) { + // set correct stack top for current task + tasks[i].pxTopOfStack = (StackType_t *)frame; + esp_panicPutStr("Current task PC/A0/SP "); + esp_panicPutHex(frame->pc); + esp_panicPutStr(" "); + esp_panicPutHex(frame->a0); + esp_panicPutStr(" "); + esp_panicPutHex(frame->a1); + esp_panicPutStr("\r\n"); + } +#if( portSTACK_GROWTH < 0 ) + len = (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack; +#else + len = (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack; +#endif + esp_panicPutStr("stack len = "); + esp_panicPutHex(len); + esp_panicPutStr(" "); + esp_panicPutHex((int)tasks[i].pxTopOfStack); + esp_panicPutStr(" "); + esp_panicPutHex((int)tasks[i].pxEndOfStack); + esp_panicPutStr("\r\n"); + // take stack padding into account + if (len % sizeof(uint32_t)) + len = (len / sizeof(uint32_t) + 1) * sizeof(uint32_t); + data_len += len; + } + esp_panicPutStr("Core dump len ="); + esp_panicPutHex(data_len); + esp_panicPutStr("\r\n"); + if (data_len > s_core_part_size) { + esp_panicPutStr("ERROR: Not enough space to save core dump!"); + return; + } + + // TEST READ START + err = spi_flash_read_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to read flash "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + else { + esp_panicPutStr("Data from flash:\r\n"); + for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { + esp_panicPutHex(rom_data.data32[i]); + esp_panicPutStr("\r\n"); + } +// rom_data[4] = 0; +// esp_panicPutStr(rom_data); +// esp_panicPutStr("\r\n"); + } + // TEST READ END + + sec_num = data_len / SPI_FLASH_SEC_SIZE; + if (data_len % SPI_FLASH_SEC_SIZE) + sec_num++; + err = spi_flash_erase_range_panic(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to erase flash "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + + rom_data.data32[0] = COREDUMP_FLASH_MAGIC_START; + rom_data.data32[1] = data_len; + rom_data.data32[2] = task_num; + rom_data.data32[3] = tcb_sz; + err = spi_flash_write_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write core dump header "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + off = sizeof(rom_data); + + for (i = 0; i < task_num; i++) { + esp_panicPutStr("Dump task "); + esp_panicPutHex((int)tasks[i].pxTCB); + esp_panicPutStr("\r\n"); + + // save TCB address, stack base and stack top addr + rom_data.data32[0] = (uint32_t)tasks[i].pxTCB; + rom_data.data32[1] = (uint32_t)tasks[i].pxTopOfStack; + rom_data.data32[2] = (uint32_t)tasks[i].pxEndOfStack; + err = spi_flash_write_panic(s_core_part_start + off, &rom_data, 3*sizeof(uint32_t)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write task header "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + off += 3*sizeof(uint32_t); + // save TCB + len = esp_core_dump_write_flash_padded(s_core_part_start + off, tasks[i].pxTCB, tcb_sz); + if (len == 0) + return; + off += len; + // save task stack + /*int k; + for (k = 0; k < 8*4; k++) { + esp_panicPutStr("stack["); + esp_panicPutDec(k); + esp_panicPutStr("] = "); + esp_panicPutHex(((uint8_t *)tasks[i].pxTopOfStack)[k]); + esp_panicPutStr("\r\n"); + }*/ + len = esp_core_dump_write_flash_padded(s_core_part_start + off, +#if( portSTACK_GROWTH < 0 ) + tasks[i].pxTopOfStack, + (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack +#else + tasks[i].pxEndOfStack, + (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack +#endif + ); + if (len == 0) + return; + off += len; + } + + rom_data.data32[0] = COREDUMP_FLASH_MAGIC_END; + err = spi_flash_write_panic(s_core_part_start + off, &rom_data, sizeof(uint32_t)); + if (err != ESP_OK) { + esp_panicPutStr("Failed to write to flash "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + + esp_panicPutStr("Core dump has been saved to flash partition.\r\n"); +} +#endif + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART +void esp_core_dump_to_uart(XtExcFrame *frame) +{ +} +#endif + +void esp_core_dump_init() +{ +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + const esp_partition_t *core_part; + + core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL); + if (!core_part) { + ESP_LOGE(TAG, "No core dump partition found!"); + return; + } + ESP_LOGI(TAG, "Found partition '%s' @ %x %d bytes", core_part->label, core_part->address, core_part->size); + s_core_part_start = core_part->address; + s_core_part_size = core_part->size; +#endif +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART +#endif +} + diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 864a17a1b6..95d5c5e6a2 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -55,6 +55,7 @@ #include "esp_task_wdt.h" #include "esp_phy_init.h" #include "esp_coexist.h" +#include "esp_core_dump.h" #include "trax.h" #define STRINGIFY(s) STRINGIFY2(s) @@ -214,6 +215,10 @@ void start_cpu0_default(void) } #endif +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + esp_core_dump_init(); +#endif + xTaskCreatePinnedToCore(&main_task, "main", ESP_TASK_MAIN_STACK, NULL, ESP_TASK_MAIN_PRIO, NULL, 0); diff --git a/components/esp32/include/esp_core_dump.h b/components/esp32/include/esp_core_dump.h new file mode 100644 index 0000000000..d130aa237b --- /dev/null +++ b/components/esp32/include/esp_core_dump.h @@ -0,0 +1,21 @@ +// Copyright 2015-2016 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. +#ifndef ESP_CORE_DUMP_H_ +#define ESP_CORE_DUMP_H_ + +void esp_core_dump_init(); +void esp_core_dump_to_flash(); +void esp_core_dump_to_uart(); + +#endif diff --git a/components/esp32/include/esp_panic.h b/components/esp32/include/esp_panic.h index aa83c6d381..25816d31f2 100644 --- a/components/esp32/include/esp_panic.h +++ b/components/esp32/include/esp_panic.h @@ -24,6 +24,10 @@ */ void esp_set_breakpoint_if_jtag(void *fn); +void esp_panicPutchar(char c); +void esp_panicPutStr(const char *c); +void esp_panicPutHex(int a); +void esp_panicPutDec(int a); #define ESP_WATCHPOINT_LOAD 0x40000000 #define ESP_WATCHPOINT_STORE 0x80000000 diff --git a/components/esp32/panic.c b/components/esp32/panic.c index c5b18870a9..94aebfbac4 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -33,6 +33,7 @@ #include "esp_panic.h" #include "esp_attr.h" #include "esp_err.h" +#include "esp_core_dump.h" /* Panic handlers; these get called when an unhandled exception occurs or the assembly-level @@ -46,61 +47,61 @@ #if !CONFIG_ESP32_PANIC_SILENT_REBOOT //printf may be broken, so we fix our own printing fns... -inline static void panicPutChar(char c) +void esp_panicPutChar(char c) { while (((READ_PERI_REG(UART_STATUS_REG(0)) >> UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT) >= 126) ; WRITE_PERI_REG(UART_FIFO_REG(0), c); } -inline static void panicPutStr(const char *c) +void esp_panicPutStr(const char *c) { int x = 0; while (c[x] != 0) { - panicPutChar(c[x]); + esp_panicPutChar(c[x]); x++; } } -inline static void panicPutHex(int a) +void esp_panicPutHex(int a) { int x; int c; for (x = 0; x < 8; x++) { c = (a >> 28) & 0xf; if (c < 10) { - panicPutChar('0' + c); + esp_panicPutChar('0' + c); } else { - panicPutChar('a' + c - 10); + esp_panicPutChar('a' + c - 10); } a <<= 4; } } -inline static void panicPutDec(int a) +void esp_panicPutDec(int a) { int n1, n2; n1 = a % 10; n2 = a / 10; if (n2 == 0) { - panicPutChar(' '); + esp_panicPutChar(' '); } else { - panicPutChar(n2 + '0'); + esp_panicPutChar(n2 + '0'); } - panicPutChar(n1 + '0'); + esp_panicPutChar(n1 + '0'); } #else //No printing wanted. Stub out these functions. -inline static void panicPutChar(char c) { } -inline static void panicPutStr(const char *c) { } -inline static void panicPutHex(int a) { } -inline static void panicPutDec(int a) { } +void esp_panicPutChar(char c) { } +void esp_panicPutStr(const char *c) { } +void esp_panicPutHex(int a) { } +void esp_panicPutDec(int a) { } #endif void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ) { - panicPutStr("***ERROR*** A stack overflow in task "); - panicPutStr((char *)pcTaskName); - panicPutStr(" has been detected.\r\n"); + esp_panicPutStr("***ERROR*** A stack overflow in task "); + esp_panicPutStr((char *)pcTaskName); + esp_panicPutStr(" has been detected.\r\n"); abort(); } @@ -161,39 +162,39 @@ void panicHandler(XtExcFrame *frame) reason = reasons[regs[20]]; } haltOtherCore(); - panicPutStr("Guru Meditation Error: Core "); - panicPutDec(xPortGetCoreID()); - panicPutStr(" panic'ed ("); + esp_panicPutStr("Guru Meditation Error: Core "); + esp_panicPutDec(xPortGetCoreID()); + esp_panicPutStr(" panic'ed ("); if (!abort_called) { - panicPutStr(reason); - panicPutStr(")\r\n"); + esp_panicPutStr(reason); + esp_panicPutStr(")\r\n"); if (regs[20]==PANIC_RSN_DEBUGEXCEPTION) { int debugRsn; asm("rsr.debugcause %0":"=r"(debugRsn)); - panicPutStr("Debug exception reason: "); - if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) panicPutStr("SingleStep "); - if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) panicPutStr("HwBreakpoint "); + esp_panicPutStr("Debug exception reason: "); + if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) esp_panicPutStr("SingleStep "); + if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) esp_panicPutStr("HwBreakpoint "); if (debugRsn&XCHAL_DEBUGCAUSE_DBREAK_MASK) { //Unlike what the ISA manual says, this core seemingly distinguishes from a DBREAK //reason caused by watchdog 0 and one caused by watchdog 1 by setting bit 8 of the //debugcause if the cause is watchdog 1 and clearing it if it's watchdog 0. if (debugRsn&(1<<8)) { #if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK - panicPutStr("Stack canary watchpoint triggered "); + esp_panicPutStr("Stack canary watchpoint triggered "); #else - panicPutStr("Watchpoint 1 triggered "); + esp_panicPutStr("Watchpoint 1 triggered "); #endif } else { - panicPutStr("Watchpoint 0 triggered "); + esp_panicPutStr("Watchpoint 0 triggered "); } } - if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) panicPutStr("BREAK instr "); - if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) panicPutStr("BREAKN instr "); - if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) panicPutStr("DebugIntr "); - panicPutStr("\r\n"); + if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) esp_panicPutStr("BREAK instr "); + if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) esp_panicPutStr("BREAKN instr "); + if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) esp_panicPutStr("DebugIntr "); + esp_panicPutStr("\r\n"); } } else { - panicPutStr("abort)\r\n"); + esp_panicPutStr("abort)\r\n"); } if (esp_cpu_in_ocd_debug_mode()) { @@ -219,25 +220,25 @@ void xt_unhandled_exception(XtExcFrame *frame) int x; haltOtherCore(); - panicPutStr("Guru Meditation Error of type "); + esp_panicPutStr("Guru Meditation Error of type "); x = regs[20]; if (x < 40) { - panicPutStr(edesc[x]); + esp_panicPutStr(edesc[x]); } else { - panicPutStr("Unknown"); + esp_panicPutStr("Unknown"); } - panicPutStr(" occurred on core "); - panicPutDec(xPortGetCoreID()); + esp_panicPutStr(" occurred on core "); + esp_panicPutDec(xPortGetCoreID()); if (esp_cpu_in_ocd_debug_mode()) { - panicPutStr(" at pc="); - panicPutHex(regs[1]); - panicPutStr(". Setting bp and returning..\r\n"); + esp_panicPutStr(" at pc="); + esp_panicPutHex(regs[1]); + esp_panicPutStr(". Setting bp and returning..\r\n"); //Stick a hardware breakpoint on the address the handler returns to. This way, the OCD debugger //will kick in exactly at the context the error happened. setFirstBreakpoint(regs[1]); return; } - panicPutStr(". Exception was unhandled.\r\n"); + esp_panicPutStr(". Exception was unhandled.\r\n"); commonErrorHandler(frame); } @@ -292,16 +293,16 @@ static void putEntry(uint32_t pc, uint32_t sp) if (pc & 0x80000000) { pc = (pc & 0x3fffffff) | 0x40000000; } - panicPutStr(" 0x"); - panicPutHex(pc); - panicPutStr(":0x"); - panicPutHex(sp); + esp_panicPutStr(" 0x"); + esp_panicPutHex(pc); + esp_panicPutStr(":0x"); + esp_panicPutHex(sp); } static void doBacktrace(XtExcFrame *frame) { uint32_t i = 0, pc = frame->pc, sp = frame->a1; - panicPutStr("\nBacktrace:"); + esp_panicPutStr("\r\nBacktrace:"); /* Do not check sanity on first entry, PC could be smashed. */ putEntry(pc, sp); pc = frame->a0; @@ -317,7 +318,7 @@ static void doBacktrace(XtExcFrame *frame) break; } } - panicPutStr("\n\n"); + esp_panicPutStr("\r\n\r\n"); } /* @@ -341,18 +342,18 @@ static void commonErrorHandler(XtExcFrame *frame) the register window is no longer useful. */ if (!abort_called) { - panicPutStr("Register dump:\r\n"); + esp_panicPutStr("Register dump:\r\n"); for (x = 0; x < 24; x += 4) { for (y = 0; y < 4; y++) { if (sdesc[x + y][0] != 0) { - panicPutStr(sdesc[x + y]); - panicPutStr(": 0x"); - panicPutHex(regs[x + y + 1]); - panicPutStr(" "); + esp_panicPutStr(sdesc[x + y]); + esp_panicPutStr(": 0x"); + esp_panicPutHex(regs[x + y + 1]); + esp_panicPutStr(" "); } + esp_panicPutStr("\r\n"); } - panicPutStr("\r\n"); } } @@ -361,19 +362,27 @@ static void commonErrorHandler(XtExcFrame *frame) #if CONFIG_ESP32_PANIC_GDBSTUB disableAllWdts(); - panicPutStr("Entering gdb stub now.\r\n"); + esp_panicPutStr("Entering gdb stub now.\r\n"); esp_gdbstub_panic_handler(frame); -#elif CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT - panicPutStr("Rebooting...\r\n"); +#else +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + esp_core_dump_to_flash(frame); +#endif +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART && !CONFIG_ESP32_PANIC_SILENT_REBOOT + esp_core_dump_to_uart(frame); +#endif +#if CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT + esp_panicPutStr("Rebooting...\r\n"); for (x = 0; x < 100; x++) { ets_delay_us(1000); } software_reset(); #else disableAllWdts(); - panicPutStr("CPU halted.\r\n"); + esp_panicPutStr("CPU halted.\r\n"); while (1); #endif +#endif } diff --git a/components/espcoredump/espcoredump.py b/components/espcoredump/espcoredump.py new file mode 100755 index 0000000000..065b323aa1 --- /dev/null +++ b/components/espcoredump/espcoredump.py @@ -0,0 +1,1013 @@ +#!/usr/bin/env python +# +# ESP32 core dump Utility + +import sys +import os +import argparse +import subprocess +import tempfile +import struct +import array +import errno + +try: + import esptool +except ImportError: + idf_path = os.getenv('IDF_PATH') + if idf_path is None: + print "Esptool is not found! Install it or set proper $IDF_PATH in environment." + sys.exit(2) + sys.path.append('%s/components/esptool_py/esptool' % idf_path) + import esptool + +__version__ = "0.1-dev" + +ESP32_COREDUMP_HDR_FMT = '<4L' +ESP32_COREDUMP_FLASH_MAGIC_START = 0xDEADBEEF +ESP32_COREDUMP_FLASH_MAGIC_END = 0xACDCFEED + + +class Struct(object): + def __init__(self, buf=None): + if buf is None: + buf = b'\0' * self.sizeof() + fields = struct.unpack(self.__class__.fmt, buf[:self.sizeof()]) + self.__dict__.update(zip(self.__class__.fields, fields)) + + def sizeof(self): + return struct.calcsize(self.__class__.fmt) + + def dump(self): + keys = self.__class__.fields + if sys.version_info > (3, 0): + # Convert strings into bytearrays if this is Python 3 + for k in keys: + if type(self.__dict__[k]) is str: + self.__dict__[k] = bytearray(self.__dict__[k], encoding='ascii') + return struct.pack(self.__class__.fmt, *(self.__dict__[k] for k in keys)) + + def __str__(self): + keys = self.__class__.fields + return (self.__class__.__name__ + "({" + + ", ".join("%s:%r" % (k, self.__dict__[k]) for k in keys) + + "})") + + +class Elf32FileHeader(Struct): + """ELF32 File header""" + fields = ("e_ident", + "e_type", + "e_machine", + "e_version", + "e_entry", + "e_phoff", + "e_shoff", + "e_flags", + "e_ehsize", + "e_phentsize", + "e_phnum", + "e_shentsize", + "e_shnum", + "e_shstrndx") + fmt = "<16sHHLLLLLHHHHHH" + + def __init__(self, buf=None): + super(Elf32FileHeader, self).__init__(buf) + if buf is None: + # Fill in sane ELF header for LSB32 + self.e_ident = "\x7fELF\1\1\1\0\0\0\0\0\0\0\0\0" + self.e_version = ESPCoreDumpFile.EV_CURRENT + self.e_ehsize = self.sizeof() + + +class Elf32ProgramHeader(Struct): + """ELF32 Program Header""" + fields = ("p_type", + "p_offset", + "p_vaddr", + "p_paddr", + "p_filesz", + "p_memsz", + "p_flags", + "p_align") + fmt = " 0: + self._read_sections(f, shoff, shstrndx) + else: + self.sections = [] + if phnum > 0: + self._read_program_segments(f, phoff, phentsize, phnum) + else: + self.program_segments = [] + + def _read_sections(self, f, section_header_offs, shstrndx): + f.seek(section_header_offs) + section_header = f.read() + LEN_SEC_HEADER = 0x28 + if len(section_header) == 0: + raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs) + if len(section_header) % LEN_SEC_HEADER != 0: + print 'WARNING: Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header),LEN_SEC_HEADER) + + # walk through the section header and extract all sections + section_header_offsets = range(0, len(section_header), LEN_SEC_HEADER) + + def read_section_header(offs): + name_offs,sec_type,flags,lma,sec_offs,size = struct.unpack_from("= ps.addr and addr < (ps.addr + seg_len): + raise FatalError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % + (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1)) + if (addr + data_sz) > ps.addr and (addr + data_sz) <= (ps.addr + seg_len): + raise FatalError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % + (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1)) + # append + self.program_segments.append(ESPCoreDumpSegment(addr, data, type, flags)) + + # currently dumps only program segments. + # dumping sections is not supported yet + def dump(self, f): + print "dump to '%s'" % f + # write ELF header + ehdr = Elf32FileHeader() + ehdr.e_type = self.e_type + ehdr.e_machine = self.e_machine + ehdr.e_entry = 0 + ehdr.e_phoff = ehdr.sizeof() + ehdr.e_shoff = 0 + ehdr.e_flags = 0 + ehdr.e_phentsize = Elf32ProgramHeader().sizeof() + ehdr.e_phnum = len(self.program_segments) + ehdr.e_shentsize = 0 + ehdr.e_shnum = 0 + ehdr.e_shstrndx = self.SHN_UNDEF + f.write(ehdr.dump()) + # write program header table + cur_off = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize +# print "" % (ehdr.e_ehsize, ehdr.e_phnum, ehdr.e_phentsize) + for i in range(len(self.program_segments)): + print "dump header for seg '%s'" % self.program_segments[i] + phdr = Elf32ProgramHeader() + phdr.p_type = self.program_segments[i].type + phdr.p_offset = cur_off + phdr.p_vaddr = self.program_segments[i].addr + phdr.p_paddr = phdr.p_vaddr # TODO + phdr.p_filesz = len(self.program_segments[i].data) + phdr.p_memsz = phdr.p_filesz # TODO + phdr.p_flags = self.program_segments[i].flags + phdr.p_align = 0 # TODO +# print "header '%s'" % phdr + f.write(phdr.dump()) + cur_off += phdr.p_filesz + # write program segments + for i in range(len(self.program_segments)): + print "dump seg '%s'" % self.program_segments[i] + f.write(self.program_segments[i].data) + + +class ESPCoreDumpError(RuntimeError): + """ + TBD + """ + def __init__(self, message): + super(ESPCoreDumpError, self).__init__(message) + + +class ESPCoreDumpLoaderError(ESPCoreDumpError): + """ + TBD + """ + def __init__(self, message): + super(ESPCoreDumpLoaderError, self).__init__(message) + + +class ESPCoreDumpLoader(object): + """ + TBD + """ + FLASH_READ_BLOCK_SZ = 0x2000 + def __init__(self, off, path=None, chip='esp32', port=None, baud=None): +# print "esptool.__file__ %s" % esptool.__file__ + if not path: + self.path = esptool.__file__ + self.path = self.path[:-1] + else: + self.path = path + self.port = port + self.baud = baud + self.chip = chip + self.fcores = [] + self.fgdbcore = None + self._load_coredump(off) + + def _load_coredump(self, off): + args = [self.path, '-c', self.chip] + if self.port: + args.extend(['-p', self.port]) + if self.baud: + args.extend(['-b', str(self.baud)]) + read_sz = self.FLASH_READ_BLOCK_SZ + read_off = off + args.extend(['read_flash', str(read_off), str(read_sz), '']) + try: + dump_sz = 0 + tot_len = 0 + while True: + fhnd,fname = tempfile.mkstemp() +# print "tmpname %s" % fname +# os.close(fhnd) + args[-1] = fname + et_out = subprocess.check_output(args) + print et_out + # data = os.fdopen(fhnd, 'r').read(sz) + self.fcores.append(os.fdopen(fhnd, 'r')) + if dump_sz == 0: + # read dump length from the first block + dump_sz = self._read_core_dump_length(self.fcores[0]) + tot_len += read_sz + if tot_len >= dump_sz: + break + read_off += read_sz + if dump_sz - tot_len >= self.FLASH_READ_BLOCK_SZ: + read_sz = self.FLASH_READ_BLOCK_SZ + else: + read_sz = dump_sz - tot_len + args[-3] = str(read_off) + args[-2] = str(read_sz) + + except subprocess.CalledProcessError as e: + print "esptool script execution failed with err %d" % e.returncode + print "Command ran: '%s'" % e.cmd + print "Command out:" + print e.output + self.cleanup() + return [] + + def _read_core_dump_length(self, f): + global ESP32_COREDUMP_HDR_FMT + global ESP32_COREDUMP_FLASH_MAGIC_START + print "Read core dump header from '%s'" % f.name + data = f.read(4*4) + mag1,tot_len,task_num,tcbsz = struct.unpack_from(ESP32_COREDUMP_HDR_FMT, data) + if mag1 != ESP32_COREDUMP_FLASH_MAGIC_START: + raise ESPCoreDumpLoaderError("Invalid start magic number!") + return tot_len + + def remove_tmp_file(self, fname): + try: + os.remove(fname) + except OSError as e: + if e.errno != errno.ENOENT: + print "Warning failed to remove temp file '%s'!" % fname + + def _get_registers_from_stack(self, data, grows_down): + # from "gdb/xtensa-tdep.h" + # typedef struct + # { + #0 xtensa_elf_greg_t pc; + #1 xtensa_elf_greg_t ps; + #2 xtensa_elf_greg_t lbeg; + #3 xtensa_elf_greg_t lend; + #4 xtensa_elf_greg_t lcount; + #5 xtensa_elf_greg_t sar; + #6 xtensa_elf_greg_t windowstart; + #7 xtensa_elf_greg_t windowbase; + #8..63 xtensa_elf_greg_t reserved[8+48]; + #64 xtensa_elf_greg_t ar[64]; + # } xtensa_elf_gregset_t; + REG_PC_IDX=0 + REG_PS_IDX=1 + REG_LB_IDX=2 + REG_LE_IDX=3 + REG_LC_IDX=4 + REG_SAR_IDX=5 + REG_WS_IDX=6 + REG_WB_IDX=7 + REG_AR_START_IDX=64 + REG_AR_NUM=64 + # FIXME: acc to xtensa_elf_gregset_t number of regs must be 128, + # but gdb complanis when it less then 129 + REG_NUM=129 + + XT_SOL_EXIT=0 + XT_SOL_PC=1 + XT_SOL_PS=2 + XT_SOL_NEXT=3 + XT_SOL_AR_START=4 + XT_SOL_AR_NUM=4 + XT_SOL_FRMSZ=8 + + XT_STK_EXIT=0 + XT_STK_PC=1 + XT_STK_PS=2 + XT_STK_AR_START=3 + XT_STK_AR_NUM=16 + XT_STK_SAR=19 + XT_STK_EXCCAUSE=20 + XT_STK_EXCVADDR=21 + XT_STK_LBEG=22 + XT_STK_LEND=23 + XT_STK_LCOUNT=24 + XT_STK_FRMSZ=25 + + regs = [0] * REG_NUM + # TODO: support for growing up stacks + if not grows_down: + print "Growing up stacks are not supported for now!" + return regs + # for i in range(REG_NUM): + # regs[i] = i + # return regs + ex_struct = "<%dL" % XT_STK_FRMSZ + if len(data) < struct.calcsize(ex_struct): + print "Too small stack to keep frame: %d bytes!" % len(data) + return regs + + stack = struct.unpack(ex_struct, data[:struct.calcsize(ex_struct)]) + # Stack frame type indicator is always the first item + rc = stack[XT_STK_EXIT] + if rc != 0: + print "EXCSTACKFRAME %d" % rc + regs[REG_PC_IDX] = stack[XT_STK_PC] + regs[REG_PS_IDX] = stack[XT_STK_PS] + for i in range(XT_STK_AR_NUM): + regs[REG_AR_START_IDX + i] = stack[XT_STK_AR_START + i] + regs[REG_SAR_IDX] = stack[XT_STK_SAR] + regs[REG_LB_IDX] = stack[XT_STK_LBEG] + regs[REG_LE_IDX] = stack[XT_STK_LEND] + regs[REG_LC_IDX] = stack[XT_STK_LCOUNT] + print "get_registers_from_stack: pc %x ps %x a0 %x a1 %x a2 %x a3 %x" % ( + regs[REG_PC_IDX], regs[REG_PS_IDX], regs[REG_AR_NUM + 0], + regs[REG_AR_NUM + 1], regs[REG_AR_NUM + 2], regs[REG_AR_NUM + 3]) + else: + print "SOLSTACKFRAME %d" % rc + regs[REG_PC_IDX] = stack[XT_SOL_PC] + regs[REG_PS_IDX] = stack[XT_SOL_PS] + for i in range(XT_SOL_AR_NUM): + regs[REG_AR_START_IDX + i] = stack[XT_SOL_AR_START + i] + nxt = stack[XT_SOL_NEXT] + print "get_registers_from_stack: pc %x ps %x a0 %x a1 %x a2 %x a3 %x" % ( + regs[REG_PC_IDX], regs[REG_PS_IDX], regs[REG_AR_NUM + 0], + regs[REG_AR_NUM + 1], regs[REG_AR_NUM + 2], regs[REG_AR_NUM + 3]) + + # TODO: remove magic hack with saved PC to get proper value + regs[REG_PC_IDX] = ((regs[REG_PC_IDX] & 0x3FFFFFFF) | 0x40000000) + if regs[REG_PC_IDX] & 0x80000000: + regs[REG_PC_IDX] = (regs[REG_PC_IDX] & 0x3fffffff) | 0x40000000; + if regs[REG_AR_START_IDX + 0] & 0x80000000: + regs[REG_AR_START_IDX + 0] = (regs[REG_AR_START_IDX + 0] & 0x3fffffff) | 0x40000000; + return regs + + def cleanup(self): +# if self.fgdbcore: +# self.fgdbcore.close() +# self.remove_tmp_file(self.fgdbcore.name) + for f in self.fcores: + if f: + f.close() + self.remove_tmp_file(f.name) + + def get_corefile_from_flash(self): + """ TBD + """ + global ESP32_COREDUMP_HDR_FMT + ESP32_COREDUMP_HDR_SZ = struct.calcsize(ESP32_COREDUMP_HDR_FMT) + ESP32_COREDUMP_TSK_HDR_FMT = ' stack_top: + stack_len = stack_end - stack_top + stack_base = stack_top + else: + stack_len = stack_top - stack_end + stack_base = stack_end + print "tcb_addr=%x, stack_top=%x, stack_end=%x, stack_len=%d" % (tcb_addr,stack_top,stack_end,stack_len) + + stack_len_aligned = stack_len + if stack_len_aligned % 4: + stack_len_aligned = 4*(stack_len_aligned/4 + 1) + + core_off += ESP32_COREDUMP_TSK_HDR_SZ + print "Read task[%d] TCB" % i + data = self.read_flash(core_off, tcbsz_aligned, flash_progress) + if tcbsz != tcbsz_aligned: + core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + else: + core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + # print "tcb=%s" % data + core_off += tcbsz_aligned + print "Read task[%d] stack %d bytes" % (i,stack_len) + data = self.read_flash(core_off, stack_len_aligned, flash_progress) + # print "stk=%s" % data + if stack_len != stack_len_aligned: + data = data[:stack_len - stack_len_aligned] + core_elf.add_program_segment(stack_base, data, ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + core_off += stack_len_aligned + + task_regs = self._get_registers_from_stack(data, stack_end > stack_top) + prstatus = XtensaPrStatus() + prstatus.pr_cursig = 0 # TODO: set sig only for current/failed task + prstatus.pr_pid = i # TODO: use pid assigned by OS + note = Elf32NoteDesc("CORE", 1, prstatus.dump() + struct.pack("<%dL" % len(task_regs), *task_regs)).dump() + print "NOTE_LEN %d" % len(note) + notes += note + + print "Read core dump endmarker" + data = self.read_flash(core_off, ESP32_COREDUMP_MAGIC_SZ, flash_progress) + mag = struct.unpack_from(ESP32_COREDUMP_MAGIC_FMT, data) + print "mag2=%x" % (mag) + + # add notes + core_elf.add_program_segment(0, notes, ESPCoreDumpFile.PT_NOTE, 0) + + core_elf.e_type = ESPCoreDumpFile.ET_CORE + core_elf.e_machine = ESPCoreDumpFile.EM_XTENSA + fhnd,fname = tempfile.mkstemp() + self.fgdbcore = os.fdopen(fhnd, 'wb') + core_elf.dump(self.fgdbcore) + return fname + ######################### END ########################### + + def read_flash(self, off, sz, progress=None): +# print "read_flash: %x %d" % (off, sz) + id = off / self.FLASH_READ_BLOCK_SZ + if id >= len(self.fcores): + return '' + self.fcores[id].seek(off % self.FLASH_READ_BLOCK_SZ) + data = self.fcores[id].read(sz) +# print "data1: %s" % data + return data + +class GDBMIOutRecordHandler(object): + """ TBD + """ + TAG = '' + + def __init__(self, f, verbose=False): + self.verbose = verbose + + def execute(self, ln): + if self.verbose: + print "%s.execute '%s'" % (self.__class__.__name__, ln) + + +class GDBMIOutStreamHandler(GDBMIOutRecordHandler): + """ TBD + """ + def __init__(self, f, verbose=False): + super(GDBMIOutStreamHandler, self).__init__(None, verbose) + self.func = f + + def execute(self, ln): + GDBMIOutRecordHandler.execute(self, ln) + if self.func: + # remove TAG / quotes and replace c-string \n with actual NL + self.func(ln[1:].strip('"').replace('\\n', '\n').replace('\\t', '\t')) + + +class GDBMIResultHandler(GDBMIOutRecordHandler): + """ TBD + """ + TAG = '^' + RC_DONE = 'done' + RC_RUNNING = 'running' + RC_CONNECTED = 'connected' + RC_ERROR = 'error' + RC_EXIT = 'exit' + + def __init__(self, verbose=False): + super(GDBMIResultHandler, self).__init__(None, verbose) + self.result_class = None + self.result_str = None + + def _parse_rc(self, ln, rc): + rc_str = "{0}{1}".format(self.TAG, rc) + if ln.startswith(rc_str): + self.result_class = rc + sl = len(rc_str) + if len(ln) > sl: + self.result_str = ln[sl:] + if self.result_str.startswith(','): + self.result_str = self.result_str[1:] + else: + print "Invalid result format: '%s'" % ln + else: + self.result_str = '' + return True + return False + + def execute(self, ln): + GDBMIOutRecordHandler.execute(self, ln) + if self._parse_rc(ln, self.RC_DONE): + return + if self._parse_rc(ln, self.RC_RUNNING): + return + if self._parse_rc(ln, self.RC_CONNECTED): + return + if self._parse_rc(ln, self.RC_ERROR): + return + if self._parse_rc(ln, self.RC_EXIT): + return + print "Unknown result: '%s'" % ln + + +class GDBMIStreamConsoleHandler(GDBMIOutStreamHandler): + """ TBD + """ + TAG = '~' + + +def dbg_corefile(args): + """ TBD + """ + print "dbg_corefile %s %s %s" % (args.gdb, args.prog, args.core) + loader = None + if not args.core: + loader = ESPCoreDumpLoader(args.off, port=args.port) + core_fname = loader.get_corefile_from_flash() + loader.fgdbcore.close() +# core_fname = 'esp_core.elf' + else: + core_fname = args.core +# print core_fname +# return + + p = subprocess.Popen( + bufsize = 0, + args = [args.gdb, + '--nw', # ignore .gdbinit + '--core=%s' % core_fname, # core file + args.prog], + stdin = None, stdout = None, stderr = None, + close_fds = True + ) + p.wait() + if loader: + loader.remove_tmp_file(loader.fgdbcore.name) + loader.cleanup() + print 'Done!' + + +def info_corefile(args): +# def info_corefile(args): + """ TBD + """ + print "info_corefile %s %s %s" % (args.gdb, args.prog, args.core) + + + def gdbmi_console_stream_handler(ln): + # print ln + sys.stdout.write(ln) + sys.stdout.flush() + + + def gdbmi_read2prompt(f, out_handlers=None): + """ TBD + """ + while True: + ln = f.readline().rstrip(' \n') + # print "LINE='{0}'".format(ln) + if ln == '(gdb)': + break + elif len(ln) == 0: + break + elif out_handlers: + for h in out_handlers: + if ln.startswith(out_handlers[h].TAG): + out_handlers[h].execute(ln) + break + + loader = None + if not args.core: + loader = ESPCoreDumpLoader(args.off, port=args.port) + core_fname = loader.get_corefile_from_flash() + loader.fgdbcore.close() + else: + core_fname = args.core + + handlers = {} + handlers[GDBMIResultHandler.TAG] = GDBMIResultHandler(verbose=False) + handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False) + p = subprocess.Popen( + bufsize = 0, + args = [args.gdb, + '--quiet', # inhibit dumping info at start-up + '--nx', # inhibit window interface + '--nw', # ignore .gdbinit + '--interpreter=mi2', # use GDB/MI v2 + '--core=%s' % core_fname, # core file + args.prog], +# ], + stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, + close_fds = True + ) + + gdbmi_read2prompt(p.stdout, handlers) + exe_elf = ESPCoreDumpFile(args.prog) + core_elf = ESPCoreDumpFile(core_fname) + merged_segs = []#[(s, 0) for s in exe_elf.sections if s.flags & (esptool.ELFSection.SHF_ALLOC | esptool.ELFSection.SHF_WRITE)] + for s in exe_elf.sections: + merged = False + for ps in core_elf.program_segments: + if ps.addr <= s.addr and ps.addr + len(ps.data) >= s.addr: + # sec: |XXXXXXXXXX| + # seg: |...XXX.............| + seg_addr = ps.addr + if ps.addr + len(ps.data) <= s.addr + len(s.data): + # sec: |XXXXXXXXXX| + # seg: |XXXXXXXXXXX...| + # merged: |XXXXXXXXXXXXXX| + seg_len = len(s.data) + (s.addr - ps.addr) + else: + # sec: |XXXXXXXXXX| + # seg: |XXXXXXXXXXXXXXXXX| + # merged: |XXXXXXXXXXXXXXXXX| + seg_len = len(ps.data) + merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True)) + merged = True + elif ps.addr >= s.addr and ps.addr <= s.addr + len(s.data): + # sec: |XXXXXXXXXX| + # seg: |...XXX.............| + seg_addr = s.addr + if (ps.addr + len(ps.data)) >= (s.addr + len(s.data)): + # sec: |XXXXXXXXXX| + # seg: |..XXXXXXXXXXX| + # merged: |XXXXXXXXXXXXX| + seg_len = len(s.data) + (ps.addr + len(ps.data)) - (s.addr + len(s.data)) + else: + # sec: |XXXXXXXXXX| + # seg: |XXXXXX| + # merged: |XXXXXXXXXX| + seg_len = len(s.data) + merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True)) + merged = True + + if not merged: + merged_segs.append((s.name, s.addr, len(s.data), s.attr_str(), False)) +# merged_segs.append(('None', ps.addr, len(ps.data), 'None')) + + print "===============================================================" + print "==================== ESP32 CORE DUMP START ====================" + + handlers[GDBMIResultHandler.TAG].result_class = None + handlers[GDBMIStreamConsoleHandler.TAG].func = gdbmi_console_stream_handler + print "\n================== CURRENT THREAD REGISTERS ===================" + p.stdin.write("-interpreter-exec console \"info registers\"\n") + gdbmi_read2prompt(p.stdout, handlers) + if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: + print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) + print "\n==================== CURRENT THREAD STACK =====================" + p.stdin.write("-interpreter-exec console \"bt\"\n") + gdbmi_read2prompt(p.stdout, handlers) + if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: + print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) + print "\n======================== THREADS INFO =========================" + p.stdin.write("-interpreter-exec console \"info threads\"\n") + gdbmi_read2prompt(p.stdout, handlers) + if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: + print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) + print "\n======================= MEMORY REGIONS ========================" + print "Name Address Size Attrs" + for ms in merged_segs: + print "%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3]) + if args.print_mem: + print "\n====================== MEMORY CONTENTS ========================" + for ms in merged_segs: +# if ms[3].find('W') == -1: + if not ms[4]: + continue + print "%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3]) + p.stdin.write("-interpreter-exec console \"x/%dx 0x%x\"\n" % (ms[2]/4, ms[1])) + gdbmi_read2prompt(p.stdout, handlers) + if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: + print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) + + print "\n===================== ESP32 CORE DUMP END =====================" + print "===============================================================" + + p.terminate() + p.stdin.close() + p.stdout.close() + if loader: + loader.remove_tmp_file(loader.fgdbcore.name) + loader.cleanup() + + +def main(): + parser = argparse.ArgumentParser(description='coredumper.py v%s - ESP32 Core Dump Utility' % __version__, prog='coredumper') + + parser.add_argument('--chip', '-c', + help='Target chip type', + choices=['auto', 'esp32'], + default=os.environ.get('ESPTOOL_CHIP', 'auto')) + + parser.add_argument( + '--port', '-p', + help='Serial port device', + default=os.environ.get('ESPTOOL_PORT', esptool.ESPLoader.DEFAULT_PORT)) + + parser.add_argument( + '--baud', '-b', + help='Serial port baud rate used when flashing/reading', + type=int, + default=os.environ.get('ESPTOOL_BAUD', esptool.ESPLoader.ESP_ROM_BAUD)) + +# parser.add_argument( +# '--no-stub', +# help="Disable launching the flasher stub, only talk to ROM bootloader. Some features will not be available.", +# action='store_true') + + subparsers = parser.add_subparsers( + dest='operation', + help='Run coredumper {command} -h for additional help') + + parser_debug_coredump = subparsers.add_parser( + 'dbg_corefile', + help='Starts GDB debugging session with specified corefile') + parser_debug_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb') + parser_debug_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str) + parser_debug_coredump.add_argument('--off', '-o', help='Ofsset of coredump partition in flash (type "make partition_table" to see).', type=int, default=0x110000) + parser_debug_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str) + + parser_info_coredump = subparsers.add_parser( + 'info_corefile', + help='Print core dump info from file') + parser_info_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb') + parser_info_coredump.add_argument('--print-mem', '-m', help='Print memory dump', action='store_true') + parser_info_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str) + parser_info_coredump.add_argument('--off', '-o', help='Ofsset of coredump partition in flash (type "make partition_table" to see).', type=int, default=0x110000) + parser_info_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str) + + # internal sanity check - every operation matches a module function of the same name + for operation in subparsers.choices.keys(): + assert operation in globals(), "%s should be a module function" % operation + + args = parser.parse_args() + + print 'coredumper.py v%s' % __version__ + + # operation function can take 1 arg (args), 2 args (esp, arg) + # or be a member function of the ESPLoader class. + + operation_func = globals()[args.operation] + operation_func(args) + + +if __name__ == '__main__': + try: + main() + except ESPCoreDumpError as e: + print '\nA fatal error occurred: %s' % e + sys.exit(2) diff --git a/components/freertos/include/freertos/FreeRTOS.h b/components/freertos/include/freertos/FreeRTOS.h index 4c60308f78..0e93acf271 100644 --- a/components/freertos/include/freertos/FreeRTOS.h +++ b/components/freertos/include/freertos/FreeRTOS.h @@ -863,8 +863,8 @@ typedef struct xSTATIC_TCB void *pxDummy6; uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ]; UBaseType_t uxDummyCoreId; - #if ( portSTACK_GROWTH > 0 ) - void *pxDummy8; + #if ( portSTACK_GROWTH > 0 || configENABLE_TASK_SNAPSHOT == 1 ) + void *pxDummy8; #endif #if ( portCRITICAL_NESTING_IN_TCB == 1 ) UBaseType_t uxDummy9; diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index 4f1012657a..2bedaa02e8 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -268,7 +268,9 @@ #define configXT_BOARD 1 /* Board mode */ #define configXT_SIMULATOR 0 - +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH | CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH +#define configENABLE_TASK_SNAPSHOT 1 +#endif #endif /* FREERTOS_CONFIG_H */ diff --git a/components/freertos/include/freertos/task.h b/components/freertos/include/freertos/task.h index f7b9181fcf..c6896e5386 100644 --- a/components/freertos/include/freertos/task.h +++ b/components/freertos/include/freertos/task.h @@ -181,6 +181,18 @@ typedef struct xTASK_STATUS uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ } TaskStatus_t; +/* + * Used with the uxTaskGetSnapshotAll() function to save memory snapshot of each task in the system. + * We need this struct because TCB_t is defined (hidden) in tasks.c. + */ +typedef struct xTASK_SNAPSHOT +{ + void *pxTCB; /* Address of task control block. */ + StackType_t *pxTopOfStack; /* Points to the location of the last item placed on the tasks stack. */ + StackType_t *pxEndOfStack; /* Points to the end of the stack. pxTopOfStack < pxEndOfStack, stack grows hi2lo + pxTopOfStack > pxEndOfStack, stack grows lo2hi*/ +} TaskSnapshot_t; + /* Possible return values for eTaskConfirmSleepModeStatus(). */ typedef enum { @@ -2173,6 +2185,9 @@ eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION; */ void *pvTaskIncrementMutexHeldCount( void ); +/* Used by core dump facility to get list of task handles. */ +UBaseType_t uxTaskGetSnapshotAll( TaskSnapshot_t * const pxTaskSnapshotArray, const UBaseType_t uxArraySize, UBaseType_t * const pxTcbSz ); + #ifdef __cplusplus } #endif diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 64031cfbca..100b6e470b 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -181,7 +181,7 @@ typedef struct tskTaskControlBlock char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ BaseType_t xCoreID; /*< Core this task is pinned to */ /* If this moves around (other than pcTaskName size changes), please change the define in xtensa_vectors.S as well. */ - #if ( portSTACK_GROWTH > 0 ) + #if ( portSTACK_GROWTH > 0 || configENABLE_TASK_SNAPSHOT == 1 ) StackType_t *pxEndOfStack; /*< Points to the end of the stack on architectures where the stack grows up from low memory. */ #endif @@ -885,6 +885,12 @@ UBaseType_t x; /* Check the alignment of the calculated top of stack is correct. */ configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + #if ( configENABLE_TASK_SNAPSHOT == 1 ) + { + /* need stack end for core dumps */ + pxNewTCB->pxEndOfStack = pxTopOfStack; + } +#endif } #else /* portSTACK_GROWTH */ { @@ -4912,6 +4918,102 @@ TickType_t uxReturn; #endif /* configUSE_TASK_NOTIFICATIONS */ +#if ( configENABLE_TASK_SNAPSHOT == 1 ) + + static void prvTaskGetSnapshotsFromList( TaskSnapshot_t *pxTaskSnapshotArray, UBaseType_t *uxTask, const UBaseType_t uxArraySize, List_t *pxList ) + { + TCB_t *pxNextTCB, *pxFirstTCB; + + if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + + if( *uxTask >= uxArraySize ) + break; + + pxTaskSnapshotArray[ *uxTask ].pxTCB = pxNextTCB; + pxTaskSnapshotArray[ *uxTask ].pxTopOfStack = (StackType_t *)pxNextTCB->pxTopOfStack; + #if( portSTACK_GROWTH < 0 ) + { + pxTaskSnapshotArray[ *uxTask ].pxEndOfStack = pxNextTCB->pxEndOfStack; + } + #else + { + pxTaskSnapshotArray[ *uxTask ].pxEndOfStack = pxNextTCB->pxStack; + } + #endif + + (*uxTask)++; + + } while( pxNextTCB != pxFirstTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + UBaseType_t uxTaskGetSnapshotAll( TaskSnapshot_t * const pxTaskSnapshotArray, const UBaseType_t uxArraySize, UBaseType_t * const pxTcbSz ) + { + UBaseType_t uxTask = 0, i = 0; + + *pxTcbSz = sizeof(TCB_t); + + //vTaskSuspendAll(); //WARNING: This only suspends one CPU. ToDo: suspend others as well. Mux using taskQueueMutex maybe? + { + /* Fill in an TaskStatus_t structure with information on each + task in the Ready state. */ + i = configMAX_PRIORITIES; + do + { + i--; + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &( pxReadyTasksLists[ i ] ) ); + } while( i > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + /* Fill in an TaskStatus_t structure with information on each + task in the Blocked state. */ + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, ( List_t * ) pxDelayedTaskList ); + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, ( List_t * ) pxOverflowDelayedTaskList ); + + #if( INCLUDE_vTaskDelete == 1 ) + { + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &xTasksWaitingTermination ); + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &xSuspendedTaskList ); + } + #endif + } + //( void ) xTaskResumeAll(); +#if 0 + /* Convention: First num_cpus slots will have current task for that cpu. */ + for (i = 0; i < portNUM_PROCESSORS; i++) { + if (pxCurrentTCB[i] == NULL || pxCurrentTCB == pxTaskSnapshotArray[i]) { + continue; + } else { + UBaseType_t j; + for (j = i; j < uxTask; j++) { + if (pxTaskSnapshotArray[j] == pxCurrentTCB[i]) { + TaskHandle_t tmp = pxTaskSnapshotArray[i]; + pxTaskSnapshotArray[i] = pxTaskSnapshotArray[j]; + pxTaskSnapshotArray[j] = tmp; + break; + } + } + } + } +#endif + return uxTask; + } + +#endif + #ifdef FREERTOS_MODULE_TEST #include "tasks_test_access_functions.h" #endif diff --git a/components/partition_table/Kconfig.projbuild b/components/partition_table/Kconfig.projbuild index 1f019a6e3f..36e435ba2f 100644 --- a/components/partition_table/Kconfig.projbuild +++ b/components/partition_table/Kconfig.projbuild @@ -45,8 +45,10 @@ config PARTITION_TABLE_CUSTOM_PHY_DATA_OFFSET config PARTITION_TABLE_FILENAME string - default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP - default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA + default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP && !ESP32_ENABLE_COREDUMP_TO_FLASH + default partitions_singleapp_coredump.csv if PARTITION_TABLE_SINGLE_APP && ESP32_ENABLE_COREDUMP_TO_FLASH + default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA && !ESP32_ENABLE_COREDUMP_TO_FLASH + default partitions_two_ota_coredump.csv if PARTITION_TABLE_TWO_OTA && ESP32_ENABLE_COREDUMP_TO_FLASH default PARTITION_TABLE_CUSTOM_FILENAME if PARTITION_TABLE_CUSTOM config APP_OFFSET diff --git a/components/partition_table/partitions_singleapp_coredump.csv b/components/partition_table/partitions_singleapp_coredump.csv new file mode 100644 index 0000000000..96719a425a --- /dev/null +++ b/components/partition_table/partitions_singleapp_coredump.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +nvs, data, nvs, 0x9000, 0x6000 +phy_init, data, phy, 0xf000, 0x1000 +factory, app, factory, 0x10000, 1M +coredump, data, 3, , 64K diff --git a/components/partition_table/partitions_two_ota_coredump.csv b/components/partition_table/partitions_two_ota_coredump.csv new file mode 100644 index 0000000000..3d2b34ec9a --- /dev/null +++ b/components/partition_table/partitions_two_ota_coredump.csv @@ -0,0 +1,9 @@ +# Name, Type, SubType, Offset, Size +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +nvs, data, nvs, 0x9000, 0x4000 +otadata, data, ota, 0xd000, 0x2000 +phy_init, data, phy, 0xf000, 0x1000 +factory, 0, 0, 0x10000, 1M +coredump, data, 3, , 64K +ota_0, 0, ota_0, , 1M +ota_1, 0, ota_1, , 1M diff --git a/components/spi_flash/cache_utils.c b/components/spi_flash/cache_utils.c index f30db80cd8..748ccf9042 100644 --- a/components/spi_flash/cache_utils.c +++ b/components/spi_flash/cache_utils.c @@ -141,6 +141,29 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() esp_intr_noniram_enable(); } +void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu_panic() +{ + const uint32_t cpuid = xPortGetCoreID(); + const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0; + + // do not care about other CPU, it was halted upon entering panic handler + spi_flash_disable_cache(other_cpuid, &s_flash_op_cache_state[other_cpuid]); + // Kill interrupts that aren't located in IRAM + esp_intr_noniram_disable(); + // Disable cache on this CPU as well + spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]); +} + +void IRAM_ATTR spi_flash_enable_interrupts_caches_panic() +{ + const uint32_t cpuid = xPortGetCoreID(); + + // Re-enable cache on this CPU + spi_flash_restore_cache(cpuid, s_flash_op_cache_state[cpuid]); + // Re-enable non-iram interrupts + esp_intr_noniram_enable(); +} + #else // CONFIG_FREERTOS_UNICORE void spi_flash_init_lock() @@ -172,6 +195,22 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() esp_intr_noniram_enable(); } +void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu_panic() +{ + // Kill interrupts that aren't located in IRAM + esp_intr_noniram_disable(); + // Disable cache on this CPU as well + spi_flash_disable_cache(0, &s_flash_op_cache_state[0]); +} + +void IRAM_ATTR spi_flash_enable_interrupts_caches_panic() +{ + // Re-enable cache on this CPU + spi_flash_restore_cache(0, s_flash_op_cache_state[0]); + // Re-enable non-iram interrupts + esp_intr_noniram_enable(); +} + #endif // CONFIG_FREERTOS_UNICORE /** diff --git a/components/spi_flash/cache_utils.h b/components/spi_flash/cache_utils.h index 899a31c651..a40bab0465 100644 --- a/components/spi_flash/cache_utils.h +++ b/components/spi_flash/cache_utils.h @@ -40,5 +40,14 @@ void spi_flash_disable_interrupts_caches_and_other_cpu(); // Enable cache, enable interrupts (to be added in future), resume scheduler void spi_flash_enable_interrupts_caches_and_other_cpu(); +// Disables non-IRAM interrupt handlers on current CPU and caches on both CPUs. +// This function is implied to be called from panic handler +// when non-current CPU is halted and can not execute code from flash. +void spi_flash_disable_interrupts_caches_and_other_cpu_panic(); + +// Enable cache, enable interrupts (to be added in future) on current CPU. +// This function is implied to be called from panic handler +// when non-current CPU is halted and can not execute code from flash. +void spi_flash_enable_interrupts_caches_panic(); #endif //ESP_SPI_FLASH_CACHE_UTILS_H diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 993e68a572..7f2d2d4d18 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -58,7 +58,35 @@ static spi_flash_counters_t s_flash_stats; #endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS +/* SPI flash access critical section management functions */ +typedef void (*spi_flash_guard_start_func_t)(void); +typedef void (*spi_flash_guard_end_func_t)(void); + +/** + * Structure holding SPI flash access critical section management functions + */ +typedef struct { + spi_flash_guard_start_func_t start; // critical section start func + spi_flash_guard_end_func_t end; // critical section end func +} spi_flash_guard_funcs_t; + +#define FLASH_GUARD_START(_gp_) do{if((_gp_)) (_gp_)->start();}while(0) +#define FLASH_GUARD_END(_gp_) do{if((_gp_)) (_gp_)->end();}while(0) + static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc); +static esp_err_t spi_flash_erase_range_internal(uint32_t start_addr, uint32_t size, const spi_flash_guard_funcs_t *flash_guard); +static esp_err_t spi_flash_write_internal(size_t dst, const void *srcv, size_t size, const spi_flash_guard_funcs_t *flash_guard); +static esp_err_t spi_flash_read_internal(size_t src, void *dstv, size_t size, const spi_flash_guard_funcs_t *flash_guard); + +const DRAM_ATTR spi_flash_guard_funcs_t s_flash_guard_ops = { + .start = spi_flash_disable_interrupts_caches_and_other_cpu, + .end = spi_flash_enable_interrupts_caches_and_other_cpu +}; + +const DRAM_ATTR spi_flash_guard_funcs_t s_flash_guard_panic_ops = { + .start = spi_flash_disable_interrupts_caches_and_other_cpu_panic, + .end = spi_flash_enable_interrupts_caches_panic +}; void spi_flash_init() { @@ -92,6 +120,16 @@ esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec) } esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) +{ + return spi_flash_erase_range_internal(start_addr, size, &s_flash_guard_ops); +} + +esp_err_t IRAM_ATTR spi_flash_erase_range_panic(uint32_t start_addr, uint32_t size) +{ + return spi_flash_erase_range_internal(start_addr, size, &s_flash_guard_panic_ops); +} + +static esp_err_t IRAM_ATTR spi_flash_erase_range_internal(uint32_t start_addr, uint32_t size, const spi_flash_guard_funcs_t *flash_guard) { if (start_addr % SPI_FLASH_SEC_SIZE != 0) { return ESP_ERR_INVALID_ARG; @@ -106,7 +144,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) size_t end = start + size / SPI_FLASH_SEC_SIZE; const size_t sectors_per_block = BLOCK_ERASE_SIZE / SPI_FLASH_SEC_SIZE; COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_START(flash_guard); SpiFlashOpResult rc; rc = spi_flash_unlock(); if (rc == SPI_FLASH_RESULT_OK) { @@ -122,12 +160,22 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) } } } - spi_flash_enable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_END(flash_guard); COUNTER_STOP(erase); return spi_flash_translate_rc(rc); } esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) +{ + return spi_flash_write_internal(dst, srcv, size, &s_flash_guard_ops); +} + +esp_err_t IRAM_ATTR spi_flash_write_panic(size_t dst, const void *srcv, size_t size) +{ + return spi_flash_write_internal(dst, srcv, size, &s_flash_guard_panic_ops); +} + +static esp_err_t IRAM_ATTR spi_flash_write_internal(size_t dst, const void *srcv, size_t size, const spi_flash_guard_funcs_t *flash_guard) { // Out of bound writes are checked in ROM code, but we can give better // error code here @@ -160,9 +208,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) if (left_size > 0) { uint32_t t = 0xffffffff; memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size); - spi_flash_disable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_START(flash_guard); rc = SPIWrite(left_off, &t, 4); - spi_flash_enable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_END(flash_guard); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -178,9 +226,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) bool in_dram = true; #endif if (in_dram && (((uintptr_t) srcc) + mid_off) % 4 == 0) { - spi_flash_disable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_START(flash_guard); rc = SPIWrite(dst + mid_off, (const uint32_t *) (srcc + mid_off), mid_size); - spi_flash_enable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_END(flash_guard); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -194,9 +242,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) uint32_t t[8]; uint32_t write_size = MIN(mid_size, sizeof(t)); memcpy(t, srcc + mid_off, write_size); - spi_flash_disable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_START(flash_guard); rc = SPIWrite(dst + mid_off, t, write_size); - spi_flash_enable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_END(flash_guard); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -209,9 +257,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) if (right_size > 0) { uint32_t t = 0xffffffff; memcpy(&t, srcc + right_off, right_size); - spi_flash_disable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_START(flash_guard); rc = SPIWrite(dst + right_off, &t, 4); - spi_flash_enable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_END(flash_guard); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -259,6 +307,16 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, } esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) +{ + return spi_flash_read_internal(src, dstv, size, &s_flash_guard_ops); +} + +esp_err_t IRAM_ATTR spi_flash_read_panic(size_t src, void *dstv, size_t size) +{ + return spi_flash_read_internal(src, dstv, size, &s_flash_guard_panic_ops); +} + +static esp_err_t IRAM_ATTR spi_flash_read_internal(size_t src, void *dstv, size_t size, const spi_flash_guard_funcs_t *flash_guard) { // Out of bound reads are checked in ROM code, but we can give better // error code here @@ -271,7 +329,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) SpiFlashOpResult rc = SPI_FLASH_RESULT_OK; COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_START(flash_guard); /* To simplify boundary checks below, we handle small reads separately. */ if (size < 16) { uint32_t t[6]; /* Enough for 16 bytes + 4 on either side for padding. */ @@ -345,7 +403,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) memcpy(dstc + pad_right_off, t, pad_right_size); } out: - spi_flash_enable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_END(flash_guard); COUNTER_STOP(read); return spi_flash_translate_rc(rc); } diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index b67891ae15..b149e10234 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -69,6 +69,7 @@ typedef enum { ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, //!< OTA selection partition ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01, //!< PHY init data partition ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, //!< NVS partition + ESP_PARTITION_SUBTYPE_DATA_COREDUMP = 0x03, //!< COREDUMP partition ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD = 0x80, //!< ESPHTTPD partition ESP_PARTITION_SUBTYPE_DATA_FAT = 0x81, //!< FAT partition diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index bb3ec39b45..e78c389b4e 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -173,6 +173,56 @@ void spi_flash_munmap(spi_flash_mmap_handle_t handle); */ void spi_flash_mmap_dump(); +/** + * @brief Erase a range of flash sectors. + * + * @note This version of function is to be called from panic handler. + * It does not use any OS primitives and IPC and implies that + * only calling CPU is active. + * + * @param start_address Address where erase operation has to start. + * Must be 4kB-aligned + * @param size Size of erased range, in bytes. Must be divisible by 4kB. + * + * @return esp_err_t + */ +esp_err_t spi_flash_erase_range_panic(size_t start_address, size_t size); + + +/** + * @brief Write data to Flash. + * + * @note This version of function is to be called from panic handler. + * It does not use any OS primitives and IPC and implies that + * only calling CPU is active. + + * @note If source address is in DROM, this function will return + * ESP_ERR_INVALID_ARG. + * + * @param dest destination address in Flash. Must be a multiple of 4 bytes. + * @param src pointer to the source buffer. + * @param size length of data, in bytes. Must be a multiple of 4 bytes. + * + * @return esp_err_t + */ +esp_err_t spi_flash_write_panic(size_t dest, const void *src, size_t size); + + +/** + * @brief Read data from Flash. + * + * @note This version of function is to be called from panic handler. + * It does not use any OS primitives and IPC and implies that + * only calling CPU is active. + * + * @param src source address of the data in Flash. + * @param dest pointer to the destination buffer + * @param size length of data + * + * @return esp_err_t + */ +esp_err_t spi_flash_read_panic(size_t src, void *dest, size_t size); + #if CONFIG_SPI_FLASH_ENABLE_COUNTERS /** From 23f836659d1d46e579ddb57bdcbb66ac51b0b048 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Tue, 3 Jan 2017 14:16:41 +0300 Subject: [PATCH 2/5] esp32: Add core dump printing to UART feature --- components/esp32/core_dump.c | 456 +++++++++++++++++- components/esp32/cpu_start.c | 2 +- components/esp32/include/esp_panic.h | 2 +- components/espcoredump/espcoredump.py | 365 +++++++------- .../include/freertos/FreeRTOSConfig.h | 2 +- 5 files changed, 654 insertions(+), 173 deletions(-) diff --git a/components/esp32/core_dump.c b/components/esp32/core_dump.c index 165ec74192..eaab1871c3 100644 --- a/components/esp32/core_dump.c +++ b/components/esp32/core_dump.c @@ -11,6 +11,7 @@ // 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 "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -18,24 +19,199 @@ #include "esp_panic.h" #include "esp_partition.h" -#ifdef ESP_PLATFORM -// Uncomment this line to force output from this module -#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH || CONFIG_ESP32_ENABLE_COREDUMP_TO_UART #include "esp_log.h" -static const char* TAG = "esp_core_dump_init"; -#else -#define ESP_LOGD(...) -#endif +const static char *TAG = "esp_core_dump"; // TODO: allow user to set this in menuconfig or get tasks iteratively #define COREDUMP_MAX_TASKS_NUM 32 +typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len); +typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv); +typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv); +typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len); + +typedef struct _core_dump_write_config_t +{ + esp_core_dump_write_prepare_t prepare; + esp_core_dump_write_start_t start; + esp_core_dump_write_end_t end; + esp_core_dump_flash_write_data_t write; + void * priv; + +} core_dump_write_config_t; + +static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *write_cfg, int verb) +{ + union + { + uint8_t data8[12]; + uint32_t data32[3]; + } rom_data; + //const esp_partition_t *core_part; + esp_err_t err; + TaskSnapshot_t tasks[COREDUMP_MAX_TASKS_NUM]; + UBaseType_t tcb_sz, task_num; + uint32_t data_len = 0, i, len; + //size_t off; + + task_num = uxTaskGetSnapshotAll(tasks, COREDUMP_MAX_TASKS_NUM, &tcb_sz); + // take TCB padding into account, actual TCB size will be stored in header + if (tcb_sz % sizeof(uint32_t)) + len = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t); + else + len = tcb_sz; + // header + tasknum*(tcb + stack start/end + tcb addr) + data_len = 3*sizeof(uint32_t) + task_num*(len + 2*sizeof(uint32_t) + sizeof(uint32_t *)); + for (i = 0; i < task_num; i++) { + if (tasks[i].pxTCB == xTaskGetCurrentTaskHandle()) { + // set correct stack top for current task + tasks[i].pxTopOfStack = (StackType_t *)frame; + if (verb) { + esp_panicPutStr("Current task PC/A0/SP "); + esp_panicPutHex(frame->pc); + esp_panicPutStr(" "); + esp_panicPutHex(frame->a0); + esp_panicPutStr(" "); + esp_panicPutHex(frame->a1); + esp_panicPutStr("\r\n"); + } + } +#if( portSTACK_GROWTH < 0 ) + len = (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack; +#else + len = (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack; +#endif + if (verb) { + esp_panicPutStr("stack len = "); + esp_panicPutHex(len); + esp_panicPutStr(" "); + esp_panicPutHex((int)tasks[i].pxTopOfStack); + esp_panicPutStr(" "); + esp_panicPutHex((int)tasks[i].pxEndOfStack); + esp_panicPutStr("\r\n"); + } + // take stack padding into account + if (len % sizeof(uint32_t)) + len = (len / sizeof(uint32_t) + 1) * sizeof(uint32_t); + data_len += len; + } + + // prepare write + if (write_cfg->prepare) { + err = write_cfg->prepare(write_cfg->priv, &data_len); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to prepare core dump "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + } + + if (verb) { + esp_panicPutStr("Core dump len ="); + esp_panicPutHex(data_len); + esp_panicPutStr("\r\n"); + } + + // write start marker + if (write_cfg->start) { + err = write_cfg->start(write_cfg->priv); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to start core dump "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + } + + // write header + rom_data.data32[0] = data_len; + rom_data.data32[1] = task_num; + rom_data.data32[2] = tcb_sz; + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write core dump header "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + + // write tasks + for (i = 0; i < task_num; i++) { + if (verb) { + esp_panicPutStr("Dump task "); + esp_panicPutHex((int)tasks[i].pxTCB); + esp_panicPutStr("\r\n"); + } + // save TCB address, stack base and stack top addr + rom_data.data32[0] = (uint32_t)tasks[i].pxTCB; + rom_data.data32[1] = (uint32_t)tasks[i].pxTopOfStack; + rom_data.data32[2] = (uint32_t)tasks[i].pxEndOfStack; + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write task header "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + // save TCB + err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write task header "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + // save task stack + /*int k; + for (k = 0; k < 8*4; k++) { + esp_panicPutStr("stack["); + esp_panicPutDec(k); + esp_panicPutStr("] = "); + esp_panicPutHex(((uint8_t *)tasks[i].pxTopOfStack)[k]); + esp_panicPutStr("\r\n"); + }*/ + err = write_cfg->write(write_cfg->priv, +#if( portSTACK_GROWTH < 0 ) + tasks[i].pxTopOfStack, + (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack +#else + tasks[i].pxEndOfStack, + (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack +#endif + ); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write task header "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + } + + // write end marker + if (write_cfg->end) { + err = write_cfg->end(write_cfg->priv); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to end core dump "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + } +} + #if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH // magic numbers to control core dump data consistency #define COREDUMP_FLASH_MAGIC_START 0xDEADBEEFUL #define COREDUMP_FLASH_MAGIC_END 0xACDCFEEDUL +typedef struct _core_dump_write_flash_data_t +{ + uint32_t off; +} core_dump_write_flash_data_t; + // core dump partition start static uint32_t s_core_part_start; // core dump partition size @@ -79,6 +255,115 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint return data_len; } +static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len) +{ + esp_err_t err; + uint32_t sec_num; + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + + esp_panicPutStr("Core dump len1 = "); + esp_panicPutHex(*data_len); + esp_panicPutStr("\r\n"); + + // add space for 2 magics. TODO: change to CRC + *data_len += 2*sizeof(uint32_t); + if (*data_len > s_core_part_size) { + esp_panicPutStr("ERROR: Not enough space to save core dump!"); + return ESP_ERR_NO_MEM; + } + + esp_panicPutStr("Core dump len2 = "); + esp_panicPutHex(*data_len); + esp_panicPutStr("\r\n"); + + wr_data->off = 0; + + sec_num = *data_len / SPI_FLASH_SEC_SIZE; + if (*data_len % SPI_FLASH_SEC_SIZE) + sec_num++; + err = spi_flash_erase_range_panic(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to erase flash "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return err; + } + + return err; +} + +static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr_data, uint32_t word) +{ + esp_err_t err = ESP_OK; + uint32_t data32 = word; + + err = spi_flash_write_panic(s_core_part_start + wr_data->off, &data32, sizeof(uint32_t)); + if (err != ESP_OK) { + esp_panicPutStr("Failed to write to flash "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return err; + } + wr_data->off += sizeof(uint32_t); + + return err; +} + +static esp_err_t esp_core_dump_flash_write_start(void *priv) +{ + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + // save magic 1 + return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_START); +} + +static esp_err_t esp_core_dump_flash_write_end(void *priv) +{ + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + uint32_t i; + union + { + uint8_t data8[16]; + uint32_t data32[4]; + } rom_data; + + // TEST READ START + esp_err_t err = spi_flash_read_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to read flash "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return err; + } + else { + esp_panicPutStr("Data from flash:\r\n"); + for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { + esp_panicPutHex(rom_data.data32[i]); + esp_panicPutStr("\r\n"); + } +// rom_data[4] = 0; +// esp_panicPutStr(rom_data); +// esp_panicPutStr("\r\n"); + } + // TEST READ END + + // save magic 2 + return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_END); +} + +static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len) +{ + esp_err_t err = ESP_OK; + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + + uint32_t len = esp_core_dump_write_flash_padded(s_core_part_start + wr_data->off, data, data_len); + if (len != data_len) + return ESP_FAIL; + + wr_data->off += len; + + return err; +} + /* * | MAGIC1 | * | TOTAL_LEN | TASKS_NUM | TCB_SIZE | @@ -90,6 +375,19 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint */ void esp_core_dump_to_flash(XtExcFrame *frame) { +#if 1 + core_dump_write_config_t wr_cfg; + core_dump_write_flash_data_t wr_data; + + wr_cfg.prepare = esp_core_dump_flash_write_prepare; + wr_cfg.start = esp_core_dump_flash_write_start; + wr_cfg.end = esp_core_dump_flash_write_end; + wr_cfg.write = esp_core_dump_flash_write_data; + wr_cfg.priv = &wr_data; + + esp_panicPutStr("Save core dump to flash...\r\n"); + esp_core_dump_write(frame, &wr_cfg, 1); +#else union { uint8_t data8[16]; @@ -245,14 +543,150 @@ void esp_core_dump_to_flash(XtExcFrame *frame) esp_panicPutStr("!\r\n"); return; } - - esp_panicPutStr("Core dump has been saved to flash partition.\r\n"); +#endif + esp_panicPutStr("Core dump has been saved to flash.\r\n"); } #endif #if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART +#if 0 +#define BASE64_ENCODE_BODY(_src, _src_len, _dst) \ + do { \ + static const char *b64 = \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; \ + int i, j, a, b, c; \ + \ + for (i = j = 0; i < _src_len; i += 3) { \ + a = _src[i]; \ + b = i + 1 >= _src_len ? 0 : _src[i + 1]; \ + c = i + 2 >= _src_len ? 0 : _src[i + 2]; \ + \ + /*BASE64_OUT(b64[a >> 2], _dst[j]);*/ \ + _dst[j++] = b64[a >> 2]; \ + /*BASE64_OUT(b64[((a & 3) << 4) | (b >> 4)], _dst[j]);*/ \ + _dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; \ + j++; \ + if (i + 1 < _src_len) { \ + BASE64_OUT(b64[(b & 15) << 2 | (c >> 6)], _dst[j]); \ + j++; \ + } \ + if (i + 2 < _src_len) { \ + BASE64_OUT(b64[c & 63], _dst[j]); \ + j++; \ + } \ + } \ + \ + while (j % 4 != 0) { \ + BASE64_OUT('=', _dst); \ + } \ + BASE64_FLUSH(_dst) \ + } while(0) + +#define BASE64_OUT(ch, _dst) \ + do { \ + _dst = (ch); \ + } while (0) + +#define BASE64_FLUSH(_dst) \ + do { \ + _dst = '\0'; \ + } while (0) +#endif +static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8_t *dst) { +// BASE64_ENCODE_BODY(src, src_len, dst); + static const char *b64 = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i, j, a, b, c; + + for (i = j = 0; i < src_len; i += 3) { + a = src[i]; + b = i + 1 >= src_len ? 0 : src[i + 1]; + c = i + 2 >= src_len ? 0 : src[i + 2]; + + dst[j++] = b64[a >> 2]; + dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; + if (i + 1 < src_len) { + dst[j++] = b64[(b & 0x0F) << 2 | (c >> 6)]; + } + if (i + 2 < src_len) { + dst[j++] = b64[c & 0x3F]; + } + } + while (j % 4 != 0) { + dst[j++] = '='; + } + dst[j++] = '\0'; +} + +/*static esp_err_t esp_core_dump_uart_write_prepare(void *priv, uint32_t *data_len) +{ + esp_err_t err = ESP_OK; + return err; +}*/ + +static esp_err_t esp_core_dump_uart_write_start(void *priv) +{ +// core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + esp_err_t err = ESP_OK; + esp_panicPutStr("================= CORE DUMP START =================\r\n"); + return err; +} + +static esp_err_t esp_core_dump_uart_write_end(void *priv) +{ +// core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + esp_err_t err = ESP_OK; + esp_panicPutStr("================= CORE DUMP END =================\r\n"); + return err; +} + +static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len) +{ +// core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + esp_err_t err = ESP_OK; + char buf[64 + 4], *addr = data; + char *end = addr + data_len; + +// esp_panicPutStr("CORE DUMP SEC: "); +// esp_panicPutDec(data_len); +// esp_panicPutStr("bytes\r\n"); + + while (addr < end) { + size_t len = end - addr; + if (len > 48) len = 48; + /* Copy to stack to avoid alignment restrictions. */ + char *tmp = buf + (sizeof(buf) - len); + memcpy(tmp, addr, len); + esp_core_dump_b64_encode((const uint8_t *)tmp, len, (uint8_t *)buf); + addr += len; + esp_panicPutStr(buf); +// for (size_t i = 0; buf[i] != '\0'; i++) { +// panicPutChar(buf[i]); +// } + //if (addr % 96 == 0) + esp_panicPutStr("\r\n"); + /* Feed the Cerberus. */ +// TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; +// TIMERG0.wdt_feed = 1; + } + + return err; +} + void esp_core_dump_to_uart(XtExcFrame *frame) { + core_dump_write_config_t wr_cfg; + //core_dump_write_flash_data_t wr_data; + + wr_cfg.prepare = NULL;//esp_core_dump_uart_write_prepare; + wr_cfg.start = esp_core_dump_uart_write_start; + wr_cfg.end = esp_core_dump_uart_write_end; + wr_cfg.write = esp_core_dump_uart_write_data; + wr_cfg.priv = NULL; + + esp_panicPutStr("Save core dump to flash...\r\n"); + esp_core_dump_write(frame, &wr_cfg, 0); + esp_panicPutStr("Core dump has been written to uart.\r\n"); } #endif @@ -261,6 +695,7 @@ void esp_core_dump_init() #if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH const esp_partition_t *core_part; + ESP_LOGI(TAG, "Init core dump to flash"); core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL); if (!core_part) { ESP_LOGE(TAG, "No core dump partition found!"); @@ -271,6 +706,9 @@ void esp_core_dump_init() s_core_part_size = core_part->size; #endif #if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART + ESP_LOGI(TAG, "Init core dump to UART"); #endif } +#endif + diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 95d5c5e6a2..1b8a95b75c 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -215,7 +215,7 @@ void start_cpu0_default(void) } #endif -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH || CONFIG_ESP32_ENABLE_COREDUMP_TO_UART esp_core_dump_init(); #endif diff --git a/components/esp32/include/esp_panic.h b/components/esp32/include/esp_panic.h index 25816d31f2..3dd5d4b185 100644 --- a/components/esp32/include/esp_panic.h +++ b/components/esp32/include/esp_panic.h @@ -24,7 +24,7 @@ */ void esp_set_breakpoint_if_jtag(void *fn); -void esp_panicPutchar(char c); +void esp_panicPutChar(char c); void esp_panicPutStr(const char *c); void esp_panicPutHex(int a); void esp_panicPutDec(int a); diff --git a/components/espcoredump/espcoredump.py b/components/espcoredump/espcoredump.py index 065b323aa1..2f31d3f292 100755 --- a/components/espcoredump/espcoredump.py +++ b/components/espcoredump/espcoredump.py @@ -10,6 +10,7 @@ import tempfile import struct import array import errno +import base64 try: import esptool @@ -23,10 +24,6 @@ except ImportError: __version__ = "0.1-dev" -ESP32_COREDUMP_HDR_FMT = '<4L' -ESP32_COREDUMP_FLASH_MAGIC_START = 0xDEADBEEF -ESP32_COREDUMP_FLASH_MAGIC_END = 0xACDCFEED - class Struct(object): def __init__(self, buf=None): @@ -77,7 +74,7 @@ class Elf32FileHeader(Struct): if buf is None: # Fill in sane ELF header for LSB32 self.e_ident = "\x7fELF\1\1\1\0\0\0\0\0\0\0\0\0" - self.e_version = ESPCoreDumpFile.EV_CURRENT + self.e_version = ESPCoreDumpElfFile.EV_CURRENT self.e_ehsize = self.sizeof() @@ -200,7 +197,7 @@ class ESPCoreDumpSection(esptool.ELFSection): return str -class ESPCoreDumpFile(esptool.ELFFile): +class ESPCoreDumpElfFile(esptool.ELFFile): # ELF file type ET_NONE = 0x0 # No file type ET_REL = 0x1 # Relocatable file @@ -229,7 +226,7 @@ class ESPCoreDumpFile(esptool.ELFFile): def __init__(self, name=None): if name: - super(ESPCoreDumpFile, self).__init__(name) + super(ESPCoreDumpElfFile, self).__init__(name) else: self.sections = [] self.program_segments = [] @@ -409,80 +406,12 @@ class ESPCoreDumpLoader(object): """ TBD """ - FLASH_READ_BLOCK_SZ = 0x2000 - def __init__(self, off, path=None, chip='esp32', port=None, baud=None): -# print "esptool.__file__ %s" % esptool.__file__ - if not path: - self.path = esptool.__file__ - self.path = self.path[:-1] - else: - self.path = path - self.port = port - self.baud = baud - self.chip = chip - self.fcores = [] - self.fgdbcore = None - self._load_coredump(off) - - def _load_coredump(self, off): - args = [self.path, '-c', self.chip] - if self.port: - args.extend(['-p', self.port]) - if self.baud: - args.extend(['-b', str(self.baud)]) - read_sz = self.FLASH_READ_BLOCK_SZ - read_off = off - args.extend(['read_flash', str(read_off), str(read_sz), '']) - try: - dump_sz = 0 - tot_len = 0 - while True: - fhnd,fname = tempfile.mkstemp() -# print "tmpname %s" % fname -# os.close(fhnd) - args[-1] = fname - et_out = subprocess.check_output(args) - print et_out - # data = os.fdopen(fhnd, 'r').read(sz) - self.fcores.append(os.fdopen(fhnd, 'r')) - if dump_sz == 0: - # read dump length from the first block - dump_sz = self._read_core_dump_length(self.fcores[0]) - tot_len += read_sz - if tot_len >= dump_sz: - break - read_off += read_sz - if dump_sz - tot_len >= self.FLASH_READ_BLOCK_SZ: - read_sz = self.FLASH_READ_BLOCK_SZ - else: - read_sz = dump_sz - tot_len - args[-3] = str(read_off) - args[-2] = str(read_sz) - - except subprocess.CalledProcessError as e: - print "esptool script execution failed with err %d" % e.returncode - print "Command ran: '%s'" % e.cmd - print "Command out:" - print e.output - self.cleanup() - return [] - - def _read_core_dump_length(self, f): - global ESP32_COREDUMP_HDR_FMT - global ESP32_COREDUMP_FLASH_MAGIC_START - print "Read core dump header from '%s'" % f.name - data = f.read(4*4) - mag1,tot_len,task_num,tcbsz = struct.unpack_from(ESP32_COREDUMP_HDR_FMT, data) - if mag1 != ESP32_COREDUMP_FLASH_MAGIC_START: - raise ESPCoreDumpLoaderError("Invalid start magic number!") - return tot_len - - def remove_tmp_file(self, fname): - try: - os.remove(fname) - except OSError as e: - if e.errno != errno.ENOENT: - print "Warning failed to remove temp file '%s'!" % fname + ESP32_COREDUMP_HDR_FMT = '<3L' + ESP32_COREDUMP_HDR_SZ = struct.calcsize(ESP32_COREDUMP_HDR_FMT) + ESP32_COREDUMP_TSK_HDR_FMT = '<3L' + ESP32_COREDUMP_TSK_HDR_SZ = struct.calcsize(ESP32_COREDUMP_TSK_HDR_FMT) + def __init__(self): + self.fcore = None def _get_registers_from_stack(self, data, grows_down): # from "gdb/xtensa-tdep.h" @@ -582,51 +511,36 @@ class ESPCoreDumpLoader(object): regs[REG_AR_START_IDX + 0] = (regs[REG_AR_START_IDX + 0] & 0x3fffffff) | 0x40000000; return regs - def cleanup(self): -# if self.fgdbcore: -# self.fgdbcore.close() -# self.remove_tmp_file(self.fgdbcore.name) - for f in self.fcores: - if f: - f.close() - self.remove_tmp_file(f.name) + def remove_tmp_file(self, fname): + try: + os.remove(fname) + except OSError as e: + if e.errno != errno.ENOENT: + print "Warning failed to remove temp file '%s'!" % fname - def get_corefile_from_flash(self): + def cleanup(self): + if self.fcore: + self.fcore.close() + self.remove_tmp_file(self.fcore.name) + + def create_corefile(self, core_fname=None, off=0): """ TBD """ - global ESP32_COREDUMP_HDR_FMT - ESP32_COREDUMP_HDR_SZ = struct.calcsize(ESP32_COREDUMP_HDR_FMT) - ESP32_COREDUMP_TSK_HDR_FMT = ' stack_top: stack_len = stack_end - stack_top stack_base = stack_top @@ -639,23 +553,23 @@ class ESPCoreDumpLoader(object): if stack_len_aligned % 4: stack_len_aligned = 4*(stack_len_aligned/4 + 1) - core_off += ESP32_COREDUMP_TSK_HDR_SZ + core_off += self.ESP32_COREDUMP_TSK_HDR_SZ print "Read task[%d] TCB" % i - data = self.read_flash(core_off, tcbsz_aligned, flash_progress) + data = self.read_data(core_off, tcbsz_aligned) if tcbsz != tcbsz_aligned: - core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) else: - core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) # print "tcb=%s" % data core_off += tcbsz_aligned print "Read task[%d] stack %d bytes" % (i,stack_len) - data = self.read_flash(core_off, stack_len_aligned, flash_progress) + data = self.read_data(core_off, stack_len_aligned) # print "stk=%s" % data if stack_len != stack_len_aligned: data = data[:stack_len - stack_len_aligned] - core_elf.add_program_segment(stack_base, data, ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + core_elf.add_program_segment(stack_base, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) core_off += stack_len_aligned - + task_regs = self._get_registers_from_stack(data, stack_end > stack_top) prstatus = XtensaPrStatus() prstatus.pr_cursig = 0 # TODO: set sig only for current/failed task @@ -663,33 +577,140 @@ class ESPCoreDumpLoader(object): note = Elf32NoteDesc("CORE", 1, prstatus.dump() + struct.pack("<%dL" % len(task_regs), *task_regs)).dump() print "NOTE_LEN %d" % len(note) notes += note - - print "Read core dump endmarker" - data = self.read_flash(core_off, ESP32_COREDUMP_MAGIC_SZ, flash_progress) - mag = struct.unpack_from(ESP32_COREDUMP_MAGIC_FMT, data) - print "mag2=%x" % (mag) - - # add notes - core_elf.add_program_segment(0, notes, ESPCoreDumpFile.PT_NOTE, 0) - - core_elf.e_type = ESPCoreDumpFile.ET_CORE - core_elf.e_machine = ESPCoreDumpFile.EM_XTENSA - fhnd,fname = tempfile.mkstemp() - self.fgdbcore = os.fdopen(fhnd, 'wb') - core_elf.dump(self.fgdbcore) - return fname - ######################### END ########################### - def read_flash(self, off, sz, progress=None): -# print "read_flash: %x %d" % (off, sz) - id = off / self.FLASH_READ_BLOCK_SZ - if id >= len(self.fcores): - return '' - self.fcores[id].seek(off % self.FLASH_READ_BLOCK_SZ) - data = self.fcores[id].read(sz) + # add notes + core_elf.add_program_segment(0, notes, ESPCoreDumpElfFile.PT_NOTE, 0) + + core_elf.e_type = ESPCoreDumpElfFile.ET_CORE + core_elf.e_machine = ESPCoreDumpElfFile.EM_XTENSA + if core_fname: + fce = open(core_fname, 'wb') + else: + fhnd,core_fname = tempfile.mkstemp() + fce = os.fdopen(fhnd, 'wb') + core_elf.dump(fce) + fce.close() + return core_fname + + def read_data(self, off, sz): +# print "read_data: %x %d" % (off, sz) + self.fcore.seek(off) + data = self.fcore.read(sz) # print "data1: %s" % data return data + +class ESPCoreDumpFileLoader(ESPCoreDumpLoader): + """ TBD + """ + def __init__(self, path, b64 = False): + super(ESPCoreDumpFileLoader, self).__init__() + self.fcore = self._load_coredump(path, b64) + + def _load_coredump(self, path, b64): + if b64: + fhnd,fname = tempfile.mkstemp() + print "tmpname %s" % fname + fcore = os.fdopen(fhnd, 'wb') + fb64 = open(path, 'r') + try: + while True: + line = fb64.readline() + if len(line) == 0: + break + data = base64.b64decode(line.rstrip('\r\n'))#, validate=True) + fcore.write(data) + fcore.close() + fcore = open(fname, 'r') + finally: + fb64.close() + else: + fcore = open(path, 'r') + return fcore + + +class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): + """ TBD + """ + ESP32_COREDUMP_FLASH_MAGIC_START = 0xDEADBEEF + ESP32_COREDUMP_FLASH_MAGIC_END = 0xACDCFEED + ESP32_COREDUMP_FLASH_MAGIC_FMT = ' Date: Tue, 3 Jan 2017 22:01:40 +0300 Subject: [PATCH 3/5] esp32: Fixes several issues in core dump feature 1) PS is fixed up to allow GDB backtrace to work properly 2) MR!341 discussion: in core dump module: esp_panicPutXXX was replaced by ets_printf. 3) MR!341 discussion: core dump flash magic number was changed. 4) MR!341 discussion: SPI flash access API was redesigned to allow flexible critical section management. 5) test app for core dump feature was added 6) fixed base64 file reading issues on Windows platform 7) now raw bin core file is deleted upon core loader failure by epscoredump.py --- components/esp32/core_dump.c | 427 ++++--------------- components/esp32/cpu_start.c | 2 + components/esp32/include/esp_panic.h | 4 - components/esp32/panic.c | 82 ++-- components/espcoredump/espcoredump.py | 113 ++--- components/freertos/tasks.c | 20 - components/spi_flash/cache_utils.c | 8 +- components/spi_flash/cache_utils.h | 8 +- components/spi_flash/flash_ops.c | 84 +--- components/spi_flash/include/esp_spi_flash.h | 56 +-- 10 files changed, 241 insertions(+), 563 deletions(-) diff --git a/components/esp32/core_dump.c b/components/esp32/core_dump.c index eaab1871c3..e12fc1d71c 100644 --- a/components/esp32/core_dump.c +++ b/components/esp32/core_dump.c @@ -15,7 +15,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" -//#include "esp_attr.h" #include "esp_panic.h" #include "esp_partition.h" @@ -26,10 +25,10 @@ const static char *TAG = "esp_core_dump"; // TODO: allow user to set this in menuconfig or get tasks iteratively #define COREDUMP_MAX_TASKS_NUM 32 -typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len); -typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv); -typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv); -typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len); +typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len, int verb); +typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv, int verb); +typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv, int verb); +typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len, int verb); typedef struct _core_dump_write_config_t { @@ -67,14 +66,19 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri if (tasks[i].pxTCB == xTaskGetCurrentTaskHandle()) { // set correct stack top for current task tasks[i].pxTopOfStack = (StackType_t *)frame; + if (verb) + ets_printf("Current task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", frame->exit, frame->pc, frame->ps, frame->a0, frame->a1); + } + else { if (verb) { - esp_panicPutStr("Current task PC/A0/SP "); - esp_panicPutHex(frame->pc); - esp_panicPutStr(" "); - esp_panicPutHex(frame->a0); - esp_panicPutStr(" "); - esp_panicPutHex(frame->a1); - esp_panicPutStr("\r\n"); + XtSolFrame *task_frame = (XtSolFrame *)tasks[i].pxTopOfStack; + if (task_frame->exit == 0) { + ets_printf("Task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", task_frame->exit, task_frame->pc, task_frame->ps, task_frame->a0, task_frame->a1); + } + else { + XtExcFrame *task_frame2 = (XtExcFrame *)tasks[i].pxTopOfStack; + ets_printf("Task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1); + } } } #if( portSTACK_GROWTH < 0 ) @@ -83,13 +87,7 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri len = (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack; #endif if (verb) { - esp_panicPutStr("stack len = "); - esp_panicPutHex(len); - esp_panicPutStr(" "); - esp_panicPutHex((int)tasks[i].pxTopOfStack); - esp_panicPutStr(" "); - esp_panicPutHex((int)tasks[i].pxEndOfStack); - esp_panicPutStr("\r\n"); + ets_printf("Stack len = %lu (%x %x)\r\n", len, tasks[i].pxTopOfStack, tasks[i].pxEndOfStack); } // take stack padding into account if (len % sizeof(uint32_t)) @@ -99,28 +97,22 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri // prepare write if (write_cfg->prepare) { - err = write_cfg->prepare(write_cfg->priv, &data_len); + err = write_cfg->prepare(write_cfg->priv, &data_len, verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to prepare core dump "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to prepare core dump (%d)!\r\n", err); return; } } if (verb) { - esp_panicPutStr("Core dump len ="); - esp_panicPutHex(data_len); - esp_panicPutStr("\r\n"); + ets_printf("Core dump len = %lu\r\n", data_len); } - // write start marker + // write start if (write_cfg->start) { - err = write_cfg->start(write_cfg->priv); + err = write_cfg->start(write_cfg->priv, verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to start core dump "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to start core dump (%d)!\r\n", err); return; } } @@ -129,49 +121,33 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri rom_data.data32[0] = data_len; rom_data.data32[1] = task_num; rom_data.data32[2] = tcb_sz; - err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t), verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write core dump header "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to write core dump header (%d)!\r\n", err); return; } // write tasks for (i = 0; i < task_num; i++) { if (verb) { - esp_panicPutStr("Dump task "); - esp_panicPutHex((int)tasks[i].pxTCB); - esp_panicPutStr("\r\n"); + ets_printf("Dump task %x\r\n", tasks[i].pxTCB); } // save TCB address, stack base and stack top addr rom_data.data32[0] = (uint32_t)tasks[i].pxTCB; rom_data.data32[1] = (uint32_t)tasks[i].pxTopOfStack; rom_data.data32[2] = (uint32_t)tasks[i].pxEndOfStack; - err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t), verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write task header "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to write task header (%d)!\r\n", err); return; } // save TCB - err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz); + err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz, verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write task header "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to write TCB (%d)!\r\n", err); return; } // save task stack - /*int k; - for (k = 0; k < 8*4; k++) { - esp_panicPutStr("stack["); - esp_panicPutDec(k); - esp_panicPutStr("] = "); - esp_panicPutHex(((uint8_t *)tasks[i].pxTopOfStack)[k]); - esp_panicPutStr("\r\n"); - }*/ err = write_cfg->write(write_cfg->priv, #if( portSTACK_GROWTH < 0 ) tasks[i].pxTopOfStack, @@ -180,22 +156,18 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri tasks[i].pxEndOfStack, (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack #endif - ); + , verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write task header "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to write task stack (%d)!\r\n", err); return; } } - // write end marker + // write end if (write_cfg->end) { - err = write_cfg->end(write_cfg->priv); + err = write_cfg->end(write_cfg->priv, verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to end core dump "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to end core dump (%d)!\r\n", err); return; } } @@ -204,8 +176,8 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri #if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH // magic numbers to control core dump data consistency -#define COREDUMP_FLASH_MAGIC_START 0xDEADBEEFUL -#define COREDUMP_FLASH_MAGIC_END 0xACDCFEEDUL +#define COREDUMP_FLASH_MAGIC_START 0xE32C04EDUL +#define COREDUMP_FLASH_MAGIC_END 0xE32C04EDUL typedef struct _core_dump_write_flash_data_t { @@ -228,11 +200,9 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint } rom_data; data_len = (data_size / sizeof(uint32_t)) * sizeof(uint32_t); - err = spi_flash_write_panic(off, data, data_len); + err = spi_flash_write(off, data, data_len); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write data"); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to write data to flash (%d)!\r\n", err); return 0; } @@ -242,11 +212,9 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint rom_data.data32 = 0; for (k = 0; k < len; k++) rom_data.data8[k] = *(data + data_len + k); - err = spi_flash_write_panic(off + data_len, &rom_data, sizeof(uint32_t)); + err = spi_flash_write(off + data_len, &rom_data, sizeof(uint32_t)); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write data end"); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to finish write data to flash (%d)!\r\n", err); return 0; } data_len += sizeof(uint32_t); @@ -255,37 +223,27 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint return data_len; } -static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len) +static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len, int verb) { esp_err_t err; uint32_t sec_num; core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; - esp_panicPutStr("Core dump len1 = "); - esp_panicPutHex(*data_len); - esp_panicPutStr("\r\n"); - // add space for 2 magics. TODO: change to CRC - *data_len += 2*sizeof(uint32_t); - if (*data_len > s_core_part_size) { - esp_panicPutStr("ERROR: Not enough space to save core dump!"); + if ((*data_len + 2*sizeof(uint32_t)) > s_core_part_size) { + ets_printf("ERROR: Not enough space to save core dump!\r\n"); return ESP_ERR_NO_MEM; } - - esp_panicPutStr("Core dump len2 = "); - esp_panicPutHex(*data_len); - esp_panicPutStr("\r\n"); + *data_len += 2*sizeof(uint32_t); wr_data->off = 0; sec_num = *data_len / SPI_FLASH_SEC_SIZE; if (*data_len % SPI_FLASH_SEC_SIZE) sec_num++; - err = spi_flash_erase_range_panic(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE); + err = spi_flash_erase_range(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to erase flash "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to erase flash (%d)!\r\n", err); return err; } @@ -297,11 +255,9 @@ static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr esp_err_t err = ESP_OK; uint32_t data32 = word; - err = spi_flash_write_panic(s_core_part_start + wr_data->off, &data32, sizeof(uint32_t)); + err = spi_flash_write(s_core_part_start + wr_data->off, &data32, sizeof(uint32_t)); if (err != ESP_OK) { - esp_panicPutStr("Failed to write to flash "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to write to flash (%d)!\r\n", err); return err; } wr_data->off += sizeof(uint32_t); @@ -309,14 +265,14 @@ static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr return err; } -static esp_err_t esp_core_dump_flash_write_start(void *priv) +static esp_err_t esp_core_dump_flash_write_start(void *priv, int verb) { core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; // save magic 1 return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_START); } -static esp_err_t esp_core_dump_flash_write_end(void *priv) +static esp_err_t esp_core_dump_flash_write_end(void *priv, int verb) { core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; uint32_t i; @@ -326,31 +282,27 @@ static esp_err_t esp_core_dump_flash_write_end(void *priv) uint32_t data32[4]; } rom_data; - // TEST READ START - esp_err_t err = spi_flash_read_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data)); - if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to read flash "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); - return err; - } - else { - esp_panicPutStr("Data from flash:\r\n"); - for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { - esp_panicPutHex(rom_data.data32[i]); - esp_panicPutStr("\r\n"); + if (verb) { + // TEST READ START + esp_err_t err = spi_flash_read(s_core_part_start + 0, &rom_data, sizeof(rom_data)); + if (err != ESP_OK) { + ets_printf("ERROR: Failed to read flash (%d)!\r\n", err); + return err; } -// rom_data[4] = 0; -// esp_panicPutStr(rom_data); -// esp_panicPutStr("\r\n"); + else { + ets_printf("Data from flash:\r\n"); + for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { + ets_printf("%x\r\n", rom_data.data32[i]); + } + } + // TEST READ END } - // TEST READ END // save magic 2 return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_END); } -static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len) +static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len, int verb) { esp_err_t err = ESP_OK; core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; @@ -375,223 +327,25 @@ static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_ */ void esp_core_dump_to_flash(XtExcFrame *frame) { -#if 1 core_dump_write_config_t wr_cfg; core_dump_write_flash_data_t wr_data; + /* init non-OS flash access critical section */ + spi_flash_guard_set(&g_flash_guard_no_os_ops); + wr_cfg.prepare = esp_core_dump_flash_write_prepare; wr_cfg.start = esp_core_dump_flash_write_start; wr_cfg.end = esp_core_dump_flash_write_end; wr_cfg.write = esp_core_dump_flash_write_data; wr_cfg.priv = &wr_data; - esp_panicPutStr("Save core dump to flash...\r\n"); - esp_core_dump_write(frame, &wr_cfg, 1); -#else - union - { - uint8_t data8[16]; - uint32_t data32[4]; - } rom_data; - //const esp_partition_t *core_part; - esp_err_t err; - TaskSnapshot_t tasks[COREDUMP_MAX_TASKS_NUM]; - UBaseType_t tcb_sz, task_num; - uint32_t data_len = 0, i, len, sec_num; - size_t off; - - esp_panicPutStr("Save core dump to flash...\r\n"); - task_num = uxTaskGetSnapshotAll(tasks, COREDUMP_MAX_TASKS_NUM, &tcb_sz); - // take TCB padding into account, actual TCB size will be stored in header - if (tcb_sz % sizeof(uint32_t)) - len = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t); - else - len = tcb_sz; - // header + magic2 + tasknum*(tcb + stack start/end + tcb addr) - data_len = 5*sizeof(uint32_t) + task_num*(len + 2*sizeof(uint32_t) + sizeof(uint32_t *)); - for (i = 0; i < task_num; i++) { - if (tasks[i].pxTCB == xTaskGetCurrentTaskHandle()) { - // set correct stack top for current task - tasks[i].pxTopOfStack = (StackType_t *)frame; - esp_panicPutStr("Current task PC/A0/SP "); - esp_panicPutHex(frame->pc); - esp_panicPutStr(" "); - esp_panicPutHex(frame->a0); - esp_panicPutStr(" "); - esp_panicPutHex(frame->a1); - esp_panicPutStr("\r\n"); - } -#if( portSTACK_GROWTH < 0 ) - len = (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack; -#else - len = (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack; -#endif - esp_panicPutStr("stack len = "); - esp_panicPutHex(len); - esp_panicPutStr(" "); - esp_panicPutHex((int)tasks[i].pxTopOfStack); - esp_panicPutStr(" "); - esp_panicPutHex((int)tasks[i].pxEndOfStack); - esp_panicPutStr("\r\n"); - // take stack padding into account - if (len % sizeof(uint32_t)) - len = (len / sizeof(uint32_t) + 1) * sizeof(uint32_t); - data_len += len; - } - esp_panicPutStr("Core dump len ="); - esp_panicPutHex(data_len); - esp_panicPutStr("\r\n"); - if (data_len > s_core_part_size) { - esp_panicPutStr("ERROR: Not enough space to save core dump!"); - return; - } - - // TEST READ START - err = spi_flash_read_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data)); - if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to read flash "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); - return; - } - else { - esp_panicPutStr("Data from flash:\r\n"); - for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { - esp_panicPutHex(rom_data.data32[i]); - esp_panicPutStr("\r\n"); - } -// rom_data[4] = 0; -// esp_panicPutStr(rom_data); -// esp_panicPutStr("\r\n"); - } - // TEST READ END - - sec_num = data_len / SPI_FLASH_SEC_SIZE; - if (data_len % SPI_FLASH_SEC_SIZE) - sec_num++; - err = spi_flash_erase_range_panic(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE); - if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to erase flash "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); - return; - } - - rom_data.data32[0] = COREDUMP_FLASH_MAGIC_START; - rom_data.data32[1] = data_len; - rom_data.data32[2] = task_num; - rom_data.data32[3] = tcb_sz; - err = spi_flash_write_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data)); - if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write core dump header "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); - return; - } - off = sizeof(rom_data); - - for (i = 0; i < task_num; i++) { - esp_panicPutStr("Dump task "); - esp_panicPutHex((int)tasks[i].pxTCB); - esp_panicPutStr("\r\n"); - - // save TCB address, stack base and stack top addr - rom_data.data32[0] = (uint32_t)tasks[i].pxTCB; - rom_data.data32[1] = (uint32_t)tasks[i].pxTopOfStack; - rom_data.data32[2] = (uint32_t)tasks[i].pxEndOfStack; - err = spi_flash_write_panic(s_core_part_start + off, &rom_data, 3*sizeof(uint32_t)); - if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write task header "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); - return; - } - off += 3*sizeof(uint32_t); - // save TCB - len = esp_core_dump_write_flash_padded(s_core_part_start + off, tasks[i].pxTCB, tcb_sz); - if (len == 0) - return; - off += len; - // save task stack - /*int k; - for (k = 0; k < 8*4; k++) { - esp_panicPutStr("stack["); - esp_panicPutDec(k); - esp_panicPutStr("] = "); - esp_panicPutHex(((uint8_t *)tasks[i].pxTopOfStack)[k]); - esp_panicPutStr("\r\n"); - }*/ - len = esp_core_dump_write_flash_padded(s_core_part_start + off, -#if( portSTACK_GROWTH < 0 ) - tasks[i].pxTopOfStack, - (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack -#else - tasks[i].pxEndOfStack, - (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack -#endif - ); - if (len == 0) - return; - off += len; - } - - rom_data.data32[0] = COREDUMP_FLASH_MAGIC_END; - err = spi_flash_write_panic(s_core_part_start + off, &rom_data, sizeof(uint32_t)); - if (err != ESP_OK) { - esp_panicPutStr("Failed to write to flash "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); - return; - } -#endif - esp_panicPutStr("Core dump has been saved to flash.\r\n"); + ets_printf("Save core dump to flash...\r\n"); + esp_core_dump_write(frame, &wr_cfg, 0); + ets_printf("Core dump has been saved to flash.\r\n"); } #endif #if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART -#if 0 -#define BASE64_ENCODE_BODY(_src, _src_len, _dst) \ - do { \ - static const char *b64 = \ - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; \ - int i, j, a, b, c; \ - \ - for (i = j = 0; i < _src_len; i += 3) { \ - a = _src[i]; \ - b = i + 1 >= _src_len ? 0 : _src[i + 1]; \ - c = i + 2 >= _src_len ? 0 : _src[i + 2]; \ - \ - /*BASE64_OUT(b64[a >> 2], _dst[j]);*/ \ - _dst[j++] = b64[a >> 2]; \ - /*BASE64_OUT(b64[((a & 3) << 4) | (b >> 4)], _dst[j]);*/ \ - _dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; \ - j++; \ - if (i + 1 < _src_len) { \ - BASE64_OUT(b64[(b & 15) << 2 | (c >> 6)], _dst[j]); \ - j++; \ - } \ - if (i + 2 < _src_len) { \ - BASE64_OUT(b64[c & 63], _dst[j]); \ - j++; \ - } \ - } \ - \ - while (j % 4 != 0) { \ - BASE64_OUT('=', _dst); \ - } \ - BASE64_FLUSH(_dst) \ - } while(0) - -#define BASE64_OUT(ch, _dst) \ - do { \ - _dst = (ch); \ - } while (0) - -#define BASE64_FLUSH(_dst) \ - do { \ - _dst = '\0'; \ - } while (0) -#endif static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8_t *dst) { // BASE64_ENCODE_BODY(src, src_len, dst); static const char *b64 = @@ -618,38 +372,26 @@ static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8 dst[j++] = '\0'; } -/*static esp_err_t esp_core_dump_uart_write_prepare(void *priv, uint32_t *data_len) +static esp_err_t esp_core_dump_uart_write_start(void *priv, int verb) { esp_err_t err = ESP_OK; - return err; -}*/ - -static esp_err_t esp_core_dump_uart_write_start(void *priv) -{ -// core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; - esp_err_t err = ESP_OK; - esp_panicPutStr("================= CORE DUMP START =================\r\n"); + ets_printf("================= CORE DUMP START =================\r\n"); return err; } -static esp_err_t esp_core_dump_uart_write_end(void *priv) +static esp_err_t esp_core_dump_uart_write_end(void *priv, int verb) { -// core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; esp_err_t err = ESP_OK; - esp_panicPutStr("================= CORE DUMP END =================\r\n"); + ets_printf("================= CORE DUMP END =================\r\n"); return err; } -static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len) +static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len, int verb) { -// core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; esp_err_t err = ESP_OK; char buf[64 + 4], *addr = data; char *end = addr + data_len; -// esp_panicPutStr("CORE DUMP SEC: "); -// esp_panicPutDec(data_len); -// esp_panicPutStr("bytes\r\n"); while (addr < end) { size_t len = end - addr; @@ -659,15 +401,7 @@ static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t memcpy(tmp, addr, len); esp_core_dump_b64_encode((const uint8_t *)tmp, len, (uint8_t *)buf); addr += len; - esp_panicPutStr(buf); -// for (size_t i = 0; buf[i] != '\0'; i++) { -// panicPutChar(buf[i]); -// } - //if (addr % 96 == 0) - esp_panicPutStr("\r\n"); - /* Feed the Cerberus. */ -// TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; -// TIMERG0.wdt_feed = 1; + ets_printf("%s\r\n", buf); } return err; @@ -676,17 +410,16 @@ static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t void esp_core_dump_to_uart(XtExcFrame *frame) { core_dump_write_config_t wr_cfg; - //core_dump_write_flash_data_t wr_data; - wr_cfg.prepare = NULL;//esp_core_dump_uart_write_prepare; + wr_cfg.prepare = NULL; wr_cfg.start = esp_core_dump_uart_write_start; wr_cfg.end = esp_core_dump_uart_write_end; wr_cfg.write = esp_core_dump_uart_write_data; wr_cfg.priv = NULL; - esp_panicPutStr("Save core dump to flash...\r\n"); + ets_printf("Print core dump to uart...\r\n"); esp_core_dump_write(frame, &wr_cfg, 0); - esp_panicPutStr("Core dump has been written to uart.\r\n"); + ets_printf("Core dump has been written to uart.\r\n"); } #endif diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 1b8a95b75c..2ae4260bd5 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -203,6 +203,8 @@ void start_cpu0_default(void) #endif esp_ipc_init(); spi_flash_init(); + /* init default OS-aware flash access critical section */ + spi_flash_guard_set(&g_flash_guard_default_ops); #if CONFIG_ESP32_PHY_AUTO_INIT nvs_flash_init(); diff --git a/components/esp32/include/esp_panic.h b/components/esp32/include/esp_panic.h index 3dd5d4b185..aa83c6d381 100644 --- a/components/esp32/include/esp_panic.h +++ b/components/esp32/include/esp_panic.h @@ -24,10 +24,6 @@ */ void esp_set_breakpoint_if_jtag(void *fn); -void esp_panicPutChar(char c); -void esp_panicPutStr(const char *c); -void esp_panicPutHex(int a); -void esp_panicPutDec(int a); #define ESP_WATCHPOINT_LOAD 0x40000000 #define ESP_WATCHPOINT_STORE 0x80000000 diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 94aebfbac4..09ce520ded 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -47,61 +47,61 @@ #if !CONFIG_ESP32_PANIC_SILENT_REBOOT //printf may be broken, so we fix our own printing fns... -void esp_panicPutChar(char c) +static void panicPutChar(char c) { while (((READ_PERI_REG(UART_STATUS_REG(0)) >> UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT) >= 126) ; WRITE_PERI_REG(UART_FIFO_REG(0), c); } -void esp_panicPutStr(const char *c) +static void panicPutStr(const char *c) { int x = 0; while (c[x] != 0) { - esp_panicPutChar(c[x]); + panicPutChar(c[x]); x++; } } -void esp_panicPutHex(int a) +static void panicPutHex(int a) { int x; int c; for (x = 0; x < 8; x++) { c = (a >> 28) & 0xf; if (c < 10) { - esp_panicPutChar('0' + c); + panicPutChar('0' + c); } else { - esp_panicPutChar('a' + c - 10); + panicPutChar('a' + c - 10); } a <<= 4; } } -void esp_panicPutDec(int a) +static void panicPutDec(int a) { int n1, n2; n1 = a % 10; n2 = a / 10; if (n2 == 0) { - esp_panicPutChar(' '); + panicPutChar(' '); } else { - esp_panicPutChar(n2 + '0'); + panicPutChar(n2 + '0'); } - esp_panicPutChar(n1 + '0'); + panicPutChar(n1 + '0'); } #else //No printing wanted. Stub out these functions. -void esp_panicPutChar(char c) { } -void esp_panicPutStr(const char *c) { } -void esp_panicPutHex(int a) { } -void esp_panicPutDec(int a) { } +static void panicPutChar(char c) { } +static void panicPutStr(const char *c) { } +static void panicPutHex(int a) { } +static void panicPutDec(int a) { } #endif void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ) { - esp_panicPutStr("***ERROR*** A stack overflow in task "); - esp_panicPutStr((char *)pcTaskName); - esp_panicPutStr(" has been detected.\r\n"); + panicPutStr("***ERROR*** A stack overflow in task "); + panicPutStr((char *)pcTaskName); + panicPutStr(" has been detected.\r\n"); abort(); } @@ -220,25 +220,25 @@ void xt_unhandled_exception(XtExcFrame *frame) int x; haltOtherCore(); - esp_panicPutStr("Guru Meditation Error of type "); + panicPutStr("Guru Meditation Error of type "); x = regs[20]; if (x < 40) { - esp_panicPutStr(edesc[x]); + panicPutStr(edesc[x]); } else { - esp_panicPutStr("Unknown"); + panicPutStr("Unknown"); } - esp_panicPutStr(" occurred on core "); - esp_panicPutDec(xPortGetCoreID()); + panicPutStr(" occurred on core "); + panicPutDec(xPortGetCoreID()); if (esp_cpu_in_ocd_debug_mode()) { - esp_panicPutStr(" at pc="); - esp_panicPutHex(regs[1]); - esp_panicPutStr(". Setting bp and returning..\r\n"); + panicPutStr(" at pc="); + panicPutHex(regs[1]); + panicPutStr(". Setting bp and returning..\r\n"); //Stick a hardware breakpoint on the address the handler returns to. This way, the OCD debugger //will kick in exactly at the context the error happened. setFirstBreakpoint(regs[1]); return; } - esp_panicPutStr(". Exception was unhandled.\r\n"); + panicPutStr(". Exception was unhandled.\r\n"); commonErrorHandler(frame); } @@ -293,16 +293,16 @@ static void putEntry(uint32_t pc, uint32_t sp) if (pc & 0x80000000) { pc = (pc & 0x3fffffff) | 0x40000000; } - esp_panicPutStr(" 0x"); - esp_panicPutHex(pc); - esp_panicPutStr(":0x"); - esp_panicPutHex(sp); + panicPutStr(" 0x"); + panicPutHex(pc); + panicPutStr(":0x"); + panicPutHex(sp); } static void doBacktrace(XtExcFrame *frame) { uint32_t i = 0, pc = frame->pc, sp = frame->a1; - esp_panicPutStr("\r\nBacktrace:"); + panicPutStr("\r\nBacktrace:"); /* Do not check sanity on first entry, PC could be smashed. */ putEntry(pc, sp); pc = frame->a0; @@ -318,7 +318,7 @@ static void doBacktrace(XtExcFrame *frame) break; } } - esp_panicPutStr("\r\n\r\n"); + panicPutStr("\r\n\r\n"); } /* @@ -342,17 +342,17 @@ static void commonErrorHandler(XtExcFrame *frame) the register window is no longer useful. */ if (!abort_called) { - esp_panicPutStr("Register dump:\r\n"); + panicPutStr("Register dump:\r\n"); for (x = 0; x < 24; x += 4) { for (y = 0; y < 4; y++) { if (sdesc[x + y][0] != 0) { - esp_panicPutStr(sdesc[x + y]); - esp_panicPutStr(": 0x"); - esp_panicPutHex(regs[x + y + 1]); - esp_panicPutStr(" "); + panicPutStr(sdesc[x + y]); + panicPutStr(": 0x"); + panicPutHex(regs[x + y + 1]); + panicPutStr(" "); } - esp_panicPutStr("\r\n"); + panicPutStr("\r\n"); } } } @@ -362,7 +362,7 @@ static void commonErrorHandler(XtExcFrame *frame) #if CONFIG_ESP32_PANIC_GDBSTUB disableAllWdts(); - esp_panicPutStr("Entering gdb stub now.\r\n"); + panicPutStr("Entering gdb stub now.\r\n"); esp_gdbstub_panic_handler(frame); #else #if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH @@ -372,14 +372,14 @@ static void commonErrorHandler(XtExcFrame *frame) esp_core_dump_to_uart(frame); #endif #if CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT - esp_panicPutStr("Rebooting...\r\n"); + panicPutStr("Rebooting...\r\n"); for (x = 0; x < 100; x++) { ets_delay_us(1000); } software_reset(); #else disableAllWdts(); - esp_panicPutStr("CPU halted.\r\n"); + panicPutStr("CPU halted.\r\n"); while (1); #endif #endif diff --git a/components/espcoredump/espcoredump.py b/components/espcoredump/espcoredump.py index 2f31d3f292..2942c609f8 100755 --- a/components/espcoredump/espcoredump.py +++ b/components/espcoredump/espcoredump.py @@ -24,6 +24,11 @@ except ImportError: __version__ = "0.1-dev" +if os.name == 'nt': + CLOSE_FDS = False +else: + CLOSE_FDS = True + class Struct(object): def __init__(self, buf=None): @@ -492,6 +497,10 @@ class ESPCoreDumpLoader(object): print "get_registers_from_stack: pc %x ps %x a0 %x a1 %x a2 %x a3 %x" % ( regs[REG_PC_IDX], regs[REG_PS_IDX], regs[REG_AR_NUM + 0], regs[REG_AR_NUM + 1], regs[REG_AR_NUM + 2], regs[REG_AR_NUM + 3]) + # FIXME: crashed and some running tasks (e.g. prvIdleTask) have EXCM bit set + # and GDB can not unwind callstack properly (it implies not windowed call0) + if regs[REG_PS_IDX] & (1 << 5): + regs[REG_PS_IDX] &= ~(1 << 4) else: print "SOLSTACKFRAME %d" % rc regs[REG_PC_IDX] = stack[XT_SOL_PC] @@ -516,18 +525,18 @@ class ESPCoreDumpLoader(object): os.remove(fname) except OSError as e: if e.errno != errno.ENOENT: - print "Warning failed to remove temp file '%s'!" % fname + print "Warning failed to remove temp file '%s' (%d)!" % (fname, e.errno) def cleanup(self): if self.fcore: self.fcore.close() - self.remove_tmp_file(self.fcore.name) + if self.fcore_name: + self.remove_tmp_file(self.fcore_name) def create_corefile(self, core_fname=None, off=0): """ TBD """ core_off = off - print "Read core dump header" data = self.read_data(core_off, self.ESP32_COREDUMP_HDR_SZ) tot_len,task_num,tcbsz = struct.unpack_from(self.ESP32_COREDUMP_HDR_FMT, data) tcbsz_aligned = tcbsz @@ -538,7 +547,6 @@ class ESPCoreDumpLoader(object): core_elf = ESPCoreDumpElfFile() notes = b'' for i in range(task_num): - print "Read task[%d] header" % i data = self.read_data(core_off, self.ESP32_COREDUMP_TSK_HDR_SZ) tcb_addr,stack_top,stack_end = struct.unpack_from(self.ESP32_COREDUMP_TSK_HDR_FMT, data) if stack_end > stack_top: @@ -554,7 +562,6 @@ class ESPCoreDumpLoader(object): stack_len_aligned = 4*(stack_len_aligned/4 + 1) core_off += self.ESP32_COREDUMP_TSK_HDR_SZ - print "Read task[%d] TCB" % i data = self.read_data(core_off, tcbsz_aligned) if tcbsz != tcbsz_aligned: core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) @@ -562,15 +569,17 @@ class ESPCoreDumpLoader(object): core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) # print "tcb=%s" % data core_off += tcbsz_aligned - print "Read task[%d] stack %d bytes" % (i,stack_len) data = self.read_data(core_off, stack_len_aligned) # print "stk=%s" % data if stack_len != stack_len_aligned: data = data[:stack_len - stack_len_aligned] core_elf.add_program_segment(stack_base, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) core_off += stack_len_aligned - - task_regs = self._get_registers_from_stack(data, stack_end > stack_top) + try: + task_regs = self._get_registers_from_stack(data, stack_end > stack_top) + except Exception as e: + print e + return None prstatus = XtensaPrStatus() prstatus.pr_cursig = 0 # TODO: set sig only for current/failed task prstatus.pr_pid = i # TODO: use pid assigned by OS @@ -608,32 +617,38 @@ class ESPCoreDumpFileLoader(ESPCoreDumpLoader): self.fcore = self._load_coredump(path, b64) def _load_coredump(self, path, b64): + """Loads core dump from (raw binary or base64-encoded) file + """ + self.fcore_name = None if b64: - fhnd,fname = tempfile.mkstemp() - print "tmpname %s" % fname + fhnd,self.fcore_name = tempfile.mkstemp() fcore = os.fdopen(fhnd, 'wb') - fb64 = open(path, 'r') + fb64 = open(path, 'rb') try: while True: line = fb64.readline() if len(line) == 0: break - data = base64.b64decode(line.rstrip('\r\n'))#, validate=True) + data = base64.standard_b64decode(line.rstrip('\r\n')) fcore.write(data) fcore.close() - fcore = open(fname, 'r') + fcore = open(self.fcore_name, 'rb') + except Exception as e: + if self.fcore_name: + self.remove_tmp_file(self.fcore_name) + raise e finally: fb64.close() else: - fcore = open(path, 'r') + fcore = open(path, 'rb') return fcore class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): """ TBD """ - ESP32_COREDUMP_FLASH_MAGIC_START = 0xDEADBEEF - ESP32_COREDUMP_FLASH_MAGIC_END = 0xACDCFEED + ESP32_COREDUMP_FLASH_MAGIC_START = 0xE32C04ED + ESP32_COREDUMP_FLASH_MAGIC_END = 0xE32C04ED ESP32_COREDUMP_FLASH_MAGIC_FMT = 'start();}while(0) #define FLASH_GUARD_END(_gp_) do{if((_gp_)) (_gp_)->end();}while(0) static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc); -static esp_err_t spi_flash_erase_range_internal(uint32_t start_addr, uint32_t size, const spi_flash_guard_funcs_t *flash_guard); -static esp_err_t spi_flash_write_internal(size_t dst, const void *srcv, size_t size, const spi_flash_guard_funcs_t *flash_guard); -static esp_err_t spi_flash_read_internal(size_t src, void *dstv, size_t size, const spi_flash_guard_funcs_t *flash_guard); -const DRAM_ATTR spi_flash_guard_funcs_t s_flash_guard_ops = { +const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_default_ops = { .start = spi_flash_disable_interrupts_caches_and_other_cpu, .end = spi_flash_enable_interrupts_caches_and_other_cpu }; -const DRAM_ATTR spi_flash_guard_funcs_t s_flash_guard_panic_ops = { - .start = spi_flash_disable_interrupts_caches_and_other_cpu_panic, - .end = spi_flash_enable_interrupts_caches_panic +const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_no_os_ops = { + .start = spi_flash_disable_interrupts_caches_and_other_cpu_no_os, + .end = spi_flash_enable_interrupts_caches_no_os }; +static const spi_flash_guard_funcs_t *s_flash_guard_ops; + void spi_flash_init() { spi_flash_init_lock(); @@ -96,6 +83,11 @@ void spi_flash_init() #endif } +void spi_flash_guard_set(const spi_flash_guard_funcs_t* funcs) +{ + s_flash_guard_ops = funcs; +} + size_t spi_flash_get_chip_size() { return g_rom_flashchip.chip_size; @@ -120,16 +112,6 @@ esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec) } esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) -{ - return spi_flash_erase_range_internal(start_addr, size, &s_flash_guard_ops); -} - -esp_err_t IRAM_ATTR spi_flash_erase_range_panic(uint32_t start_addr, uint32_t size) -{ - return spi_flash_erase_range_internal(start_addr, size, &s_flash_guard_panic_ops); -} - -static esp_err_t IRAM_ATTR spi_flash_erase_range_internal(uint32_t start_addr, uint32_t size, const spi_flash_guard_funcs_t *flash_guard) { if (start_addr % SPI_FLASH_SEC_SIZE != 0) { return ESP_ERR_INVALID_ARG; @@ -144,7 +126,7 @@ static esp_err_t IRAM_ATTR spi_flash_erase_range_internal(uint32_t start_addr, u size_t end = start + size / SPI_FLASH_SEC_SIZE; const size_t sectors_per_block = BLOCK_ERASE_SIZE / SPI_FLASH_SEC_SIZE; COUNTER_START(); - FLASH_GUARD_START(flash_guard); + FLASH_GUARD_START(s_flash_guard_ops); SpiFlashOpResult rc; rc = spi_flash_unlock(); if (rc == SPI_FLASH_RESULT_OK) { @@ -160,22 +142,12 @@ static esp_err_t IRAM_ATTR spi_flash_erase_range_internal(uint32_t start_addr, u } } } - FLASH_GUARD_END(flash_guard); + FLASH_GUARD_END(s_flash_guard_ops); COUNTER_STOP(erase); return spi_flash_translate_rc(rc); } esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) -{ - return spi_flash_write_internal(dst, srcv, size, &s_flash_guard_ops); -} - -esp_err_t IRAM_ATTR spi_flash_write_panic(size_t dst, const void *srcv, size_t size) -{ - return spi_flash_write_internal(dst, srcv, size, &s_flash_guard_panic_ops); -} - -static esp_err_t IRAM_ATTR spi_flash_write_internal(size_t dst, const void *srcv, size_t size, const spi_flash_guard_funcs_t *flash_guard) { // Out of bound writes are checked in ROM code, but we can give better // error code here @@ -208,9 +180,9 @@ static esp_err_t IRAM_ATTR spi_flash_write_internal(size_t dst, const void *srcv if (left_size > 0) { uint32_t t = 0xffffffff; memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size); - FLASH_GUARD_START(flash_guard); + FLASH_GUARD_START(s_flash_guard_ops); rc = SPIWrite(left_off, &t, 4); - FLASH_GUARD_END(flash_guard); + FLASH_GUARD_END(s_flash_guard_ops); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -226,9 +198,9 @@ static esp_err_t IRAM_ATTR spi_flash_write_internal(size_t dst, const void *srcv bool in_dram = true; #endif if (in_dram && (((uintptr_t) srcc) + mid_off) % 4 == 0) { - FLASH_GUARD_START(flash_guard); + FLASH_GUARD_START(s_flash_guard_ops); rc = SPIWrite(dst + mid_off, (const uint32_t *) (srcc + mid_off), mid_size); - FLASH_GUARD_END(flash_guard); + FLASH_GUARD_END(s_flash_guard_ops); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -242,9 +214,9 @@ static esp_err_t IRAM_ATTR spi_flash_write_internal(size_t dst, const void *srcv uint32_t t[8]; uint32_t write_size = MIN(mid_size, sizeof(t)); memcpy(t, srcc + mid_off, write_size); - FLASH_GUARD_START(flash_guard); + FLASH_GUARD_START(s_flash_guard_ops); rc = SPIWrite(dst + mid_off, t, write_size); - FLASH_GUARD_END(flash_guard); + FLASH_GUARD_END(s_flash_guard_ops); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -257,9 +229,9 @@ static esp_err_t IRAM_ATTR spi_flash_write_internal(size_t dst, const void *srcv if (right_size > 0) { uint32_t t = 0xffffffff; memcpy(&t, srcc + right_off, right_size); - FLASH_GUARD_START(flash_guard); + FLASH_GUARD_START(s_flash_guard_ops); rc = SPIWrite(dst + right_off, &t, 4); - FLASH_GUARD_END(flash_guard); + FLASH_GUARD_END(s_flash_guard_ops); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -307,16 +279,6 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, } esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) -{ - return spi_flash_read_internal(src, dstv, size, &s_flash_guard_ops); -} - -esp_err_t IRAM_ATTR spi_flash_read_panic(size_t src, void *dstv, size_t size) -{ - return spi_flash_read_internal(src, dstv, size, &s_flash_guard_panic_ops); -} - -static esp_err_t IRAM_ATTR spi_flash_read_internal(size_t src, void *dstv, size_t size, const spi_flash_guard_funcs_t *flash_guard) { // Out of bound reads are checked in ROM code, but we can give better // error code here @@ -329,7 +291,7 @@ static esp_err_t IRAM_ATTR spi_flash_read_internal(size_t src, void *dstv, size_ SpiFlashOpResult rc = SPI_FLASH_RESULT_OK; COUNTER_START(); - FLASH_GUARD_START(flash_guard); + FLASH_GUARD_START(s_flash_guard_ops); /* To simplify boundary checks below, we handle small reads separately. */ if (size < 16) { uint32_t t[6]; /* Enough for 16 bytes + 4 on either side for padding. */ @@ -403,7 +365,7 @@ static esp_err_t IRAM_ATTR spi_flash_read_internal(size_t src, void *dstv, size_ memcpy(dstc + pad_right_off, t, pad_right_size); } out: - FLASH_GUARD_END(flash_guard); + FLASH_GUARD_END(s_flash_guard_ops); COUNTER_STOP(read); return spi_flash_translate_rc(rc); } diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index e78c389b4e..402cd6e6f7 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -173,12 +173,26 @@ void spi_flash_munmap(spi_flash_mmap_handle_t handle); */ void spi_flash_mmap_dump(); +/** + * @brief SPI flash critical section enter function. + */ +typedef void (*spi_flash_guard_start_func_t)(void); +/** + * @brief SPI flash critical section exit function. + */ +typedef void (*spi_flash_guard_end_func_t)(void); + +/** + * Structure holding SPI flash access critical section management functions + */ +typedef struct { + spi_flash_guard_start_func_t start; /**< critical section start func */ + spi_flash_guard_end_func_t end; /**< critical section end func */ +} spi_flash_guard_funcs_t; + /** * @brief Erase a range of flash sectors. * - * @note This version of function is to be called from panic handler. - * It does not use any OS primitives and IPC and implies that - * only calling CPU is active. * * @param start_address Address where erase operation has to start. * Must be 4kB-aligned @@ -186,42 +200,18 @@ void spi_flash_mmap_dump(); * * @return esp_err_t */ -esp_err_t spi_flash_erase_range_panic(size_t start_address, size_t size); +void spi_flash_guard_set(const spi_flash_guard_funcs_t* funcs); +/** Default OS-aware flash access critical section functions */ +extern const spi_flash_guard_funcs_t g_flash_guard_default_ops; -/** - * @brief Write data to Flash. +/** Non-OS flash access critical section functions * - * @note This version of function is to be called from panic handler. + * @note This version of functions is to be used when no OS is present or from panic handler. * It does not use any OS primitives and IPC and implies that * only calling CPU is active. - - * @note If source address is in DROM, this function will return - * ESP_ERR_INVALID_ARG. - * - * @param dest destination address in Flash. Must be a multiple of 4 bytes. - * @param src pointer to the source buffer. - * @param size length of data, in bytes. Must be a multiple of 4 bytes. - * - * @return esp_err_t */ -esp_err_t spi_flash_write_panic(size_t dest, const void *src, size_t size); - - -/** - * @brief Read data from Flash. - * - * @note This version of function is to be called from panic handler. - * It does not use any OS primitives and IPC and implies that - * only calling CPU is active. - * - * @param src source address of the data in Flash. - * @param dest pointer to the destination buffer - * @param size length of data - * - * @return esp_err_t - */ -esp_err_t spi_flash_read_panic(size_t src, void *dest, size_t size); +extern const spi_flash_guard_funcs_t g_flash_guard_no_os_ops; #if CONFIG_SPI_FLASH_ENABLE_COUNTERS From 50b3ce616f5132ccb8b25162932129e2f6d33aa0 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Fri, 6 Jan 2017 13:06:43 +0300 Subject: [PATCH 4/5] esp32: Adds documentation and comments to core dump feature files --- components/esp32/core_dump.c | 10 - components/esp32/include/esp_core_dump.h | 43 +++ components/espcoredump/espcoredump.py | 344 ++++++++++--------- components/freertos/include/freertos/task.h | 10 +- components/spi_flash/cache_utils.h | 10 +- components/spi_flash/include/esp_spi_flash.h | 25 +- docs/Doxyfile | 3 +- docs/core_dump.rst | 127 +++++++ 8 files changed, 391 insertions(+), 181 deletions(-) create mode 100644 docs/core_dump.rst diff --git a/components/esp32/core_dump.c b/components/esp32/core_dump.c index e12fc1d71c..13519badb7 100644 --- a/components/esp32/core_dump.c +++ b/components/esp32/core_dump.c @@ -316,15 +316,6 @@ static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_ return err; } -/* - * | MAGIC1 | - * | TOTAL_LEN | TASKS_NUM | TCB_SIZE | - * | TCB_ADDR_1 | STACK_TOP_1 | STACK_END_1 | TCB_1 | STACK_1 | - * . . . . - * . . . . - * | TCB_ADDR_N | STACK_TOP_N | STACK_END_N | TCB_N | STACK_N | - * | MAGIC2 | - */ void esp_core_dump_to_flash(XtExcFrame *frame) { core_dump_write_config_t wr_cfg; @@ -347,7 +338,6 @@ void esp_core_dump_to_flash(XtExcFrame *frame) #if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8_t *dst) { -// BASE64_ENCODE_BODY(src, src_len, dst); static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int i, j, a, b, c; diff --git a/components/esp32/include/esp_core_dump.h b/components/esp32/include/esp_core_dump.h index d130aa237b..c6634364c5 100644 --- a/components/esp32/include/esp_core_dump.h +++ b/components/esp32/include/esp_core_dump.h @@ -14,8 +14,51 @@ #ifndef ESP_CORE_DUMP_H_ #define ESP_CORE_DUMP_H_ +/** + * @brief Initializes core dump module internal data. + * + * @note Should be called at system startup. + */ void esp_core_dump_init(); + +/** + * @brief Saves core dump to flash. + * + * The structure of data stored in flash is as follows: + * | MAGIC1 | + * | TOTAL_LEN | TASKS_NUM | TCB_SIZE | + * | TCB_ADDR_1 | STACK_TOP_1 | STACK_END_1 | TCB_1 | STACK_1 | + * . . . . + * . . . . + * | TCB_ADDR_N | STACK_TOP_N | STACK_END_N | TCB_N | STACK_N | + * | MAGIC2 | + * Core dump in flash consists of header and data for every task in the system at the moment of crash. + * For flash data integrity control two magic numbers are used at the beginning and the end of core dump. + * The structure of core dump data is described below in details. + * 1) MAGIC1 and MAGIC2 are special numbers stored at the beginning and the end of core dump. + * They are used to control core dump data integrity. Size of every number is 4 bytes. + * 2) Core dump starts with header: + * 2.1) TOTAL_LEN is total length of core dump data in flash including magic numbers. Size is 4 bytes. + * 2.2) TASKS_NUM is the number of tasks for which data are stored. Size is 4 bytes. + * 2.3) TCB_SIZE is the size of task's TCB structure. Size is 4 bytes. + * 3) Core dump header is followed by the data for every task in the system. + * Task data are started with task header: + * 3.1) TCB_ADDR is the address of TCB in memory. Size is 4 bytes. + * 3.2) STACK_TOP is the top of task's stack (address of the topmost stack item). Size is 4 bytes. + * 3.2) STACK_END is the end of task's stack (address from which task's stack starts). Size is 4 bytes. + * 4) Task header is followed by TCB data. Size is TCB_SIZE bytes. + * 5) Task's stack is placed after TCB data. Size is (STACK_END - STACK_TOP) bytes. + */ void esp_core_dump_to_flash(); + +/** + * @brief Print base64-encoded core dump to UART. + * + * The structure of core dump data is the same as for data stored in flash (@see esp_core_dump_to_flash) with some notes: + * 1) Magic numbers are not present in core dump printed to UART. + * 2) Since magic numbers are omitted TOTAL_LEN does not include their size. + * 3) Printed base64 data are surrounded with special messages to help user recognize the start and end of actual data. + */ void esp_core_dump_to_uart(); #endif diff --git a/components/espcoredump/espcoredump.py b/components/espcoredump/espcoredump.py index 2942c609f8..1589a362b9 100755 --- a/components/espcoredump/espcoredump.py +++ b/components/espcoredump/espcoredump.py @@ -30,34 +30,63 @@ else: CLOSE_FDS = True -class Struct(object): +class ESPCoreDumpError(RuntimeError): + """Core dump runtime error class + """ + def __init__(self, message): + """Constructor for core dump error + """ + super(ESPCoreDumpError, self).__init__(message) + +class BinStruct(object): + """Binary structure representation + + Subclasses must specify actual structure layout using 'fields' and 'format' members. + For example, the following subclass represents structure with two fields: + f1 of size 2 bytes and 4 bytes f2. Little endian. + class SomeStruct(BinStruct): + fields = ("f1", + "f2") + format = " (3, 0): - # Convert strings into bytearrays if this is Python 3 - for k in keys: - if type(self.__dict__[k]) is str: - self.__dict__[k] = bytearray(self.__dict__[k], encoding='ascii') - return struct.pack(self.__class__.fmt, *(self.__dict__[k] for k in keys)) + return struct.pack(self.__class__.format, *(self.__dict__[k] for k in keys)) - def __str__(self): - keys = self.__class__.fields - return (self.__class__.__name__ + "({" + - ", ".join("%s:%r" % (k, self.__dict__[k]) for k in keys) + - "})") +# def __str__(self): +# keys = self.__class__.fields +# return (self.__class__.__name__ + "({" + +# ", ".join("%s:%r" % (k, self.__dict__[k]) for k in keys) + +# "})") -class Elf32FileHeader(Struct): - """ELF32 File header""" +class Elf32FileHeader(BinStruct): + """ELF32 file header + """ fields = ("e_ident", "e_type", "e_machine", @@ -72,9 +101,11 @@ class Elf32FileHeader(Struct): "e_shentsize", "e_shnum", "e_shstrndx") - fmt = "<16sHHLLLLLHHHHHH" + format = "<16sHHLLLLLHHHHHH" def __init__(self, buf=None): + """Constructor for ELF32 file header structure + """ super(Elf32FileHeader, self).__init__(buf) if buf is None: # Fill in sane ELF header for LSB32 @@ -83,8 +114,9 @@ class Elf32FileHeader(Struct): self.e_ehsize = self.sizeof() -class Elf32ProgramHeader(Struct): - """ELF32 Program Header""" +class Elf32ProgramHeader(BinStruct): + """ELF32 program header + """ fields = ("p_type", "p_offset", "p_vaddr", @@ -93,39 +125,37 @@ class Elf32ProgramHeader(Struct): "p_memsz", "p_flags", "p_align") - fmt = " 0: @@ -265,11 +314,13 @@ class ESPCoreDumpElfFile(esptool.ELFFile): self.program_segments = [] def _read_sections(self, f, section_header_offs, shstrndx): + """Reads core dump sections from ELF file + """ f.seek(section_header_offs) section_header = f.read() LEN_SEC_HEADER = 0x28 if len(section_header) == 0: - raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs) + raise ESPCoreDumpError("No section header found at offset %04x in ELF file." % section_header_offs) if len(section_header) % LEN_SEC_HEADER != 0: print 'WARNING: Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header),LEN_SEC_HEADER) @@ -284,7 +335,7 @@ class ESPCoreDumpElfFile(esptool.ELFFile): # search for the string table section if not shstrndx * LEN_SEC_HEADER in section_header_offsets: - raise FatalError("ELF file has no STRTAB section at shstrndx %d" % shstrndx) + raise ESPCoreDumpError("ELF file has no STRTAB section at shstrndx %d" % shstrndx) _,sec_type,_,_,sec_size,sec_offs = read_section_header(shstrndx * LEN_SEC_HEADER) if sec_type != esptool.ELFFile.SEC_TYPE_STRTAB: print 'WARNING: ELF file has incorrect STRTAB section type 0x%02x' % sec_type @@ -306,11 +357,13 @@ class ESPCoreDumpElfFile(esptool.ELFFile): self.sections = prog_sections def _read_program_segments(self, f, seg_table_offs, entsz, num): + """Reads core dump program segments from ELF file + """ f.seek(seg_table_offs) seg_table = f.read(entsz*num) LEN_SEG_HEADER = 0x20 if len(seg_table) == 0: - raise FatalError("No program header table found at offset %04x in ELF file." % seg_table_offs) + raise ESPCoreDumpError("No program header table found at offset %04x in ELF file." % seg_table_offs) if len(seg_table) % LEN_SEG_HEADER != 0: print 'WARNING: Unexpected ELF program header table length %04x is not mod-%02x' % (len(seg_table),LEN_SEG_HEADER) @@ -318,8 +371,8 @@ class ESPCoreDumpElfFile(esptool.ELFFile): seg_table_offs = range(0, len(seg_table), LEN_SEG_HEADER) def read_program_header(offs): - type,offset,vaddr,_paddr,filesz,_memsz,_flags,_align = struct.unpack_from("= ps.addr and addr < (ps.addr + seg_len): - raise FatalError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % + raise ESPCoreDumpError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1)) if (addr + data_sz) > ps.addr and (addr + data_sz) <= (ps.addr + seg_len): - raise FatalError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % + raise ESPCoreDumpError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1)) # append self.program_segments.append(ESPCoreDumpSegment(addr, data, type, flags)) - # currently dumps only program segments. - # dumping sections is not supported yet def dump(self, f): - print "dump to '%s'" % f + """Write core dump contents to file + """ + # TODO: currently dumps only program segments. + # dumping sections is not supported yet # write ELF header ehdr = Elf32FileHeader() ehdr.e_type = self.e_type @@ -370,9 +424,7 @@ class ESPCoreDumpElfFile(esptool.ELFFile): f.write(ehdr.dump()) # write program header table cur_off = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize -# print "" % (ehdr.e_ehsize, ehdr.e_phnum, ehdr.e_phentsize) for i in range(len(self.program_segments)): - print "dump header for seg '%s'" % self.program_segments[i] phdr = Elf32ProgramHeader() phdr.p_type = self.program_segments[i].type phdr.p_offset = cur_off @@ -382,57 +434,52 @@ class ESPCoreDumpElfFile(esptool.ELFFile): phdr.p_memsz = phdr.p_filesz # TODO phdr.p_flags = self.program_segments[i].flags phdr.p_align = 0 # TODO -# print "header '%s'" % phdr f.write(phdr.dump()) cur_off += phdr.p_filesz # write program segments for i in range(len(self.program_segments)): - print "dump seg '%s'" % self.program_segments[i] f.write(self.program_segments[i].data) -class ESPCoreDumpError(RuntimeError): - """ - TBD - """ - def __init__(self, message): - super(ESPCoreDumpError, self).__init__(message) - - class ESPCoreDumpLoaderError(ESPCoreDumpError): - """ - TBD + """Core dump loader error class """ def __init__(self, message): + """Constructor for core dump loader error + """ super(ESPCoreDumpLoaderError, self).__init__(message) class ESPCoreDumpLoader(object): - """ - TBD + """Core dump loader base class """ ESP32_COREDUMP_HDR_FMT = '<3L' ESP32_COREDUMP_HDR_SZ = struct.calcsize(ESP32_COREDUMP_HDR_FMT) ESP32_COREDUMP_TSK_HDR_FMT = '<3L' ESP32_COREDUMP_TSK_HDR_SZ = struct.calcsize(ESP32_COREDUMP_TSK_HDR_FMT) + def __init__(self): + """Base constructor for core dump loader + """ self.fcore = None def _get_registers_from_stack(self, data, grows_down): - # from "gdb/xtensa-tdep.h" - # typedef struct - # { - #0 xtensa_elf_greg_t pc; - #1 xtensa_elf_greg_t ps; - #2 xtensa_elf_greg_t lbeg; - #3 xtensa_elf_greg_t lend; - #4 xtensa_elf_greg_t lcount; - #5 xtensa_elf_greg_t sar; - #6 xtensa_elf_greg_t windowstart; - #7 xtensa_elf_greg_t windowbase; - #8..63 xtensa_elf_greg_t reserved[8+48]; - #64 xtensa_elf_greg_t ar[64]; - # } xtensa_elf_gregset_t; + """Returns list of registers (in GDB format) from xtensa stack frame + """ + # from "gdb/xtensa-tdep.h" + # typedef struct + # { + #0 xtensa_elf_greg_t pc; + #1 xtensa_elf_greg_t ps; + #2 xtensa_elf_greg_t lbeg; + #3 xtensa_elf_greg_t lend; + #4 xtensa_elf_greg_t lcount; + #5 xtensa_elf_greg_t sar; + #6 xtensa_elf_greg_t windowstart; + #7 xtensa_elf_greg_t windowbase; + #8..63 xtensa_elf_greg_t reserved[8+48]; + #64 xtensa_elf_greg_t ar[64]; + # } xtensa_elf_gregset_t; REG_PC_IDX=0 REG_PS_IDX=1 REG_LB_IDX=2 @@ -446,7 +493,7 @@ class ESPCoreDumpLoader(object): # FIXME: acc to xtensa_elf_gregset_t number of regs must be 128, # but gdb complanis when it less then 129 REG_NUM=129 - + XT_SOL_EXIT=0 XT_SOL_PC=1 XT_SOL_PS=2 @@ -454,7 +501,7 @@ class ESPCoreDumpLoader(object): XT_SOL_AR_START=4 XT_SOL_AR_NUM=4 XT_SOL_FRMSZ=8 - + XT_STK_EXIT=0 XT_STK_PC=1 XT_STK_PS=2 @@ -467,25 +514,19 @@ class ESPCoreDumpLoader(object): XT_STK_LEND=23 XT_STK_LCOUNT=24 XT_STK_FRMSZ=25 - + regs = [0] * REG_NUM # TODO: support for growing up stacks if not grows_down: - print "Growing up stacks are not supported for now!" - return regs - # for i in range(REG_NUM): - # regs[i] = i - # return regs + raise ESPCoreDumpLoaderError("Growing up stacks are not supported for now!") ex_struct = "<%dL" % XT_STK_FRMSZ if len(data) < struct.calcsize(ex_struct): - print "Too small stack to keep frame: %d bytes!" % len(data) - return regs - + raise ESPCoreDumpLoaderError("Too small stack to keep frame: %d bytes!" % len(data)) + stack = struct.unpack(ex_struct, data[:struct.calcsize(ex_struct)]) # Stack frame type indicator is always the first item rc = stack[XT_STK_EXIT] if rc != 0: - print "EXCSTACKFRAME %d" % rc regs[REG_PC_IDX] = stack[XT_STK_PC] regs[REG_PS_IDX] = stack[XT_STK_PS] for i in range(XT_STK_AR_NUM): @@ -494,23 +535,16 @@ class ESPCoreDumpLoader(object): regs[REG_LB_IDX] = stack[XT_STK_LBEG] regs[REG_LE_IDX] = stack[XT_STK_LEND] regs[REG_LC_IDX] = stack[XT_STK_LCOUNT] - print "get_registers_from_stack: pc %x ps %x a0 %x a1 %x a2 %x a3 %x" % ( - regs[REG_PC_IDX], regs[REG_PS_IDX], regs[REG_AR_NUM + 0], - regs[REG_AR_NUM + 1], regs[REG_AR_NUM + 2], regs[REG_AR_NUM + 3]) # FIXME: crashed and some running tasks (e.g. prvIdleTask) have EXCM bit set # and GDB can not unwind callstack properly (it implies not windowed call0) if regs[REG_PS_IDX] & (1 << 5): regs[REG_PS_IDX] &= ~(1 << 4) else: - print "SOLSTACKFRAME %d" % rc regs[REG_PC_IDX] = stack[XT_SOL_PC] regs[REG_PS_IDX] = stack[XT_SOL_PS] for i in range(XT_SOL_AR_NUM): regs[REG_AR_START_IDX + i] = stack[XT_SOL_AR_START + i] nxt = stack[XT_SOL_NEXT] - print "get_registers_from_stack: pc %x ps %x a0 %x a1 %x a2 %x a3 %x" % ( - regs[REG_PC_IDX], regs[REG_PS_IDX], regs[REG_AR_NUM + 0], - regs[REG_AR_NUM + 1], regs[REG_AR_NUM + 2], regs[REG_AR_NUM + 3]) # TODO: remove magic hack with saved PC to get proper value regs[REG_PC_IDX] = ((regs[REG_PC_IDX] & 0x3FFFFFFF) | 0x40000000) @@ -521,6 +555,8 @@ class ESPCoreDumpLoader(object): return regs def remove_tmp_file(self, fname): + """Silently removes temporary file + """ try: os.remove(fname) except OSError as e: @@ -528,13 +564,15 @@ class ESPCoreDumpLoader(object): print "Warning failed to remove temp file '%s' (%d)!" % (fname, e.errno) def cleanup(self): + """Cleans up loader resources + """ if self.fcore: self.fcore.close() if self.fcore_name: self.remove_tmp_file(self.fcore_name) def create_corefile(self, core_fname=None, off=0): - """ TBD + """Creates core dump ELF file """ core_off = off data = self.read_data(core_off, self.ESP32_COREDUMP_HDR_SZ) @@ -542,7 +580,6 @@ class ESPCoreDumpLoader(object): tcbsz_aligned = tcbsz if tcbsz_aligned % 4: tcbsz_aligned = 4*(tcbsz_aligned/4 + 1) - print "tot_len=%d, task_num=%d, tcbsz=%d" % (tot_len,task_num,tcbsz) core_off += self.ESP32_COREDUMP_HDR_SZ core_elf = ESPCoreDumpElfFile() notes = b'' @@ -555,7 +592,6 @@ class ESPCoreDumpLoader(object): else: stack_len = stack_top - stack_end stack_base = stack_end - print "tcb_addr=%x, stack_top=%x, stack_end=%x, stack_len=%d" % (tcb_addr,stack_top,stack_end,stack_len) stack_len_aligned = stack_len if stack_len_aligned % 4: @@ -567,10 +603,8 @@ class ESPCoreDumpLoader(object): core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) else: core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) - # print "tcb=%s" % data core_off += tcbsz_aligned data = self.read_data(core_off, stack_len_aligned) - # print "stk=%s" % data if stack_len != stack_len_aligned: data = data[:stack_len - stack_len_aligned] core_elf.add_program_segment(stack_base, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) @@ -584,7 +618,6 @@ class ESPCoreDumpLoader(object): prstatus.pr_cursig = 0 # TODO: set sig only for current/failed task prstatus.pr_pid = i # TODO: use pid assigned by OS note = Elf32NoteDesc("CORE", 1, prstatus.dump() + struct.pack("<%dL" % len(task_regs), *task_regs)).dump() - print "NOTE_LEN %d" % len(note) notes += note # add notes @@ -602,20 +635,22 @@ class ESPCoreDumpLoader(object): return core_fname def read_data(self, off, sz): -# print "read_data: %x %d" % (off, sz) + """Reads data from raw core dump got from flash or UART + """ self.fcore.seek(off) data = self.fcore.read(sz) -# print "data1: %s" % data return data class ESPCoreDumpFileLoader(ESPCoreDumpLoader): - """ TBD + """Core dump file loader class """ def __init__(self, path, b64 = False): + """Constructor for core dump file loader + """ super(ESPCoreDumpFileLoader, self).__init__() self.fcore = self._load_coredump(path, b64) - + def _load_coredump(self, path, b64): """Loads core dump from (raw binary or base64-encoded) file """ @@ -645,7 +680,7 @@ class ESPCoreDumpFileLoader(ESPCoreDumpLoader): class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): - """ TBD + """Core dump flash loader class """ ESP32_COREDUMP_FLASH_MAGIC_START = 0xE32C04ED ESP32_COREDUMP_FLASH_MAGIC_END = 0xE32C04ED @@ -655,7 +690,8 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): ESP32_COREDUMP_FLASH_HDR_SZ = struct.calcsize(ESP32_COREDUMP_FLASH_HDR_FMT) def __init__(self, off, tool_path=None, chip='esp32', port=None, baud=None): -# print "esptool.__file__ %s" % esptool.__file__ + """Constructor for core dump flash loader + """ super(ESPCoreDumpFlashLoader, self).__init__() if not tool_path: self.path = esptool.__file__ @@ -667,7 +703,7 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): self.chip = chip self.dump_sz = 0 self.fcore = self._load_coredump(off) - + def _load_coredump(self, off): """Loads core dump from flash """ @@ -702,7 +738,8 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): return f def _read_core_dump_length(self, f): - print "Read core dump header from '%s'" % f.name + """Reads core dump length + """ data = f.read(4*4) mag1,tot_len,task_num,tcbsz = struct.unpack_from(self.ESP32_COREDUMP_FLASH_HDR_FMT, data) if mag1 != self.ESP32_COREDUMP_FLASH_MAGIC_START: @@ -710,44 +747,50 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): return tot_len def create_corefile(self, core_fname=None): - """ TBD + """Checks flash coredump data integrity and creates ELF file """ data = self.read_data(0, self.ESP32_COREDUMP_FLASH_MAGIC_SZ) mag1, = struct.unpack_from(self.ESP32_COREDUMP_FLASH_MAGIC_FMT, data) if mag1 != self.ESP32_COREDUMP_FLASH_MAGIC_START: - print "Invalid start marker %x" % mag1 - return None + raise ESPCoreDumpLoaderError("Invalid start marker %x" % mag1) data = self.read_data(self.dump_sz-self.ESP32_COREDUMP_FLASH_MAGIC_SZ, self.ESP32_COREDUMP_FLASH_MAGIC_SZ) mag2, = struct.unpack_from(self.ESP32_COREDUMP_FLASH_MAGIC_FMT, data) if mag2 != self.ESP32_COREDUMP_FLASH_MAGIC_END: - print "Invalid end marker %x" % mag2 - return None + raise ESPCoreDumpLoaderError("Invalid end marker %x" % mag2) return super(ESPCoreDumpFlashLoader, self).create_corefile(core_fname, off=self.ESP32_COREDUMP_FLASH_MAGIC_SZ) class GDBMIOutRecordHandler(object): - """ TBD + """GDB/MI output record handler base class """ TAG = '' def __init__(self, f, verbose=False): + """Base constructor for GDB/MI output record handler + """ self.verbose = verbose def execute(self, ln): + """Base method to execute GDB/MI output record handler function + """ if self.verbose: print "%s.execute '%s'" % (self.__class__.__name__, ln) class GDBMIOutStreamHandler(GDBMIOutRecordHandler): - """ TBD + """GDB/MI output stream handler class """ def __init__(self, f, verbose=False): + """Constructor for GDB/MI output stream handler + """ super(GDBMIOutStreamHandler, self).__init__(None, verbose) self.func = f def execute(self, ln): + """Executes GDB/MI output stream handler function + """ GDBMIOutRecordHandler.execute(self, ln) if self.func: # remove TAG / quotes and replace c-string \n with actual NL @@ -755,7 +798,7 @@ class GDBMIOutStreamHandler(GDBMIOutRecordHandler): class GDBMIResultHandler(GDBMIOutRecordHandler): - """ TBD + """GDB/MI result handler class """ TAG = '^' RC_DONE = 'done' @@ -765,11 +808,15 @@ class GDBMIResultHandler(GDBMIOutRecordHandler): RC_EXIT = 'exit' def __init__(self, verbose=False): + """Constructor for GDB/MI result handler + """ super(GDBMIResultHandler, self).__init__(None, verbose) self.result_class = None self.result_str = None def _parse_rc(self, ln, rc): + """Parses result code + """ rc_str = "{0}{1}".format(self.TAG, rc) if ln.startswith(rc_str): self.result_class = rc @@ -786,6 +833,8 @@ class GDBMIResultHandler(GDBMIOutRecordHandler): return False def execute(self, ln): + """Executes GDB/MI result handler function + """ GDBMIOutRecordHandler.execute(self, ln) if self._parse_rc(ln, self.RC_DONE): return @@ -797,17 +846,17 @@ class GDBMIResultHandler(GDBMIOutRecordHandler): return if self._parse_rc(ln, self.RC_EXIT): return - print "Unknown result: '%s'" % ln + print "Unknown GDB/MI result: '%s'" % ln class GDBMIStreamConsoleHandler(GDBMIOutStreamHandler): - """ TBD + """GDB/MI console stream handler class """ TAG = '~' def dbg_corefile(args): - """ TBD + """ Command to load core dump from file or flash and run GDB debug session with it """ global CLOSE_FDS loader = None @@ -847,19 +896,14 @@ def dbg_corefile(args): def info_corefile(args): -# def info_corefile(args): - """ TBD + """ Command to load core dump from file or flash and print it's data in user friendly form """ global CLOSE_FDS def gdbmi_console_stream_handler(ln): - # print ln sys.stdout.write(ln) sys.stdout.flush() - def gdbmi_read2prompt(f, out_handlers=None): - """ TBD - """ while True: ln = f.readline().rstrip(' \r\n') if ln == '(gdb)': @@ -908,10 +952,11 @@ def info_corefile(args): gdbmi_read2prompt(p.stdout, handlers) exe_elf = ESPCoreDumpElfFile(args.prog) core_elf = ESPCoreDumpElfFile(core_fname) - merged_segs = []#[(s, 0) for s in exe_elf.sections if s.flags & (esptool.ELFSection.SHF_ALLOC | esptool.ELFSection.SHF_WRITE)] + merged_segs = [] + core_segs = core_elf.program_segments for s in exe_elf.sections: merged = False - for ps in core_elf.program_segments: + for ps in core_segs: if ps.addr <= s.addr and ps.addr + len(ps.data) >= s.addr: # sec: |XXXXXXXXXX| # seg: |...XXX.............| @@ -927,6 +972,7 @@ def info_corefile(args): # merged: |XXXXXXXXXXXXXXXXX| seg_len = len(ps.data) merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True)) + core_segs.remove(ps) merged = True elif ps.addr >= s.addr and ps.addr <= s.addr + len(s.data): # sec: |XXXXXXXXXX| @@ -943,11 +989,11 @@ def info_corefile(args): # merged: |XXXXXXXXXX| seg_len = len(s.data) merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True)) + core_segs.remove(ps) merged = True if not merged: merged_segs.append((s.name, s.addr, len(s.data), s.attr_str(), False)) -# merged_segs.append(('None', ps.addr, len(ps.data), 'None')) print "===============================================================" print "==================== ESP32 CORE DUMP START ====================" @@ -969,7 +1015,7 @@ def info_corefile(args): gdbmi_read2prompt(p.stdout, handlers) if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) - print "\n======================= MEMORY REGIONS ========================" + print "\n======================= ALL MEMORY REGIONS ========================" print "Name Address Size Attrs" for ms in merged_segs: print "%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3]) @@ -1018,11 +1064,6 @@ def main(): type=int, default=os.environ.get('ESPTOOL_BAUD', esptool.ESPLoader.ESP_ROM_BAUD)) -# parser.add_argument( -# '--no-stub', -# help="Disable launching the flasher stub, only talk to ROM bootloader. Some features will not be available.", -# action='store_true') - subparsers = parser.add_subparsers( dest='operation', help='Run coredumper {command} -h for additional help') @@ -1054,10 +1095,7 @@ def main(): args = parser.parse_args() - print 'coredumper.py v%s' % __version__ - - # operation function can take 1 arg (args), 2 args (esp, arg) - # or be a member function of the ESPLoader class. + print 'espcoredump.py v%s' % __version__ operation_func = globals()[args.operation] operation_func(args) diff --git a/components/freertos/include/freertos/task.h b/components/freertos/include/freertos/task.h index c6896e5386..590b07a2a4 100644 --- a/components/freertos/include/freertos/task.h +++ b/components/freertos/include/freertos/task.h @@ -2185,7 +2185,15 @@ eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION; */ void *pvTaskIncrementMutexHeldCount( void ); -/* Used by core dump facility to get list of task handles. */ +/* + * This function fills array with TaskSnapshot_t structures for every task in the system. + * Used by core dump facility to get snapshots of all tasks in the system. + * Only available when configENABLE_TASK_SNAPSHOT is set to 1. + * @param pxTaskSnapshotArray Pointer to array of TaskSnapshot_t structures to store tasks snapshot data. + * @param uxArraySize Size of tasks snapshots array. + * @param pxTcbSz Pointer to store size of TCB. + * @return Number of elements stored in array. + */ UBaseType_t uxTaskGetSnapshotAll( TaskSnapshot_t * const pxTaskSnapshotArray, const UBaseType_t uxArraySize, UBaseType_t * const pxTcbSz ); #ifdef __cplusplus diff --git a/components/spi_flash/cache_utils.h b/components/spi_flash/cache_utils.h index 4ec1f3203a..a5442d1af7 100644 --- a/components/spi_flash/cache_utils.h +++ b/components/spi_flash/cache_utils.h @@ -41,13 +41,13 @@ void spi_flash_disable_interrupts_caches_and_other_cpu(); void spi_flash_enable_interrupts_caches_and_other_cpu(); // Disables non-IRAM interrupt handlers on current CPU and caches on both CPUs. -// This function is implied to be called from panic handler or when no OS is present -// when non-current CPU is halted and can not execute code from flash. +// This function is implied to be called from panic handler +// (when non-current CPU is halted and can not execute code from flash) or when no OS is present. void spi_flash_disable_interrupts_caches_and_other_cpu_no_os(); -// Enable cache, enable interrupts (to be added in future) on current CPU. -// This function is implied to be called from panic handler or when no OS is present -// when non-current CPU is halted and can not execute code from flash. +// Enable cache, enable interrupts on current CPU. +// This function is implied to be called from panic handler +// (when non-current CPU is halted and can not execute code from flash) or when no OS is present. void spi_flash_enable_interrupts_caches_no_os(); #endif //ESP_SPI_FLASH_CACHE_UTILS_H diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 402cd6e6f7..bf897e8995 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -184,6 +184,9 @@ typedef void (*spi_flash_guard_end_func_t)(void); /** * Structure holding SPI flash access critical section management functions + * + * @note Structure and corresponding guard functions should not reside in flash. + * For example structure can be placed in DRAM and functions in IRAM sections. */ typedef struct { spi_flash_guard_start_func_t start; /**< critical section start func */ @@ -191,25 +194,25 @@ typedef struct { } spi_flash_guard_funcs_t; /** - * @brief Erase a range of flash sectors. + * @brief Sets guard functions to access flash. * + * @note Pointed structure and corresponding guard functions should not reside in flash. + * For example structure can be placed in DRAM and functions in IRAM sections. * - * @param start_address Address where erase operation has to start. - * Must be 4kB-aligned - * @param size Size of erased range, in bytes. Must be divisible by 4kB. - * - * @return esp_err_t + * @param funcs pointer to structure holding flash access guard functions. */ void spi_flash_guard_set(const spi_flash_guard_funcs_t* funcs); -/** Default OS-aware flash access critical section functions */ +/** + * @brief Default OS-aware flash access guard functions + */ extern const spi_flash_guard_funcs_t g_flash_guard_default_ops; -/** Non-OS flash access critical section functions +/** + * @brief Non-OS flash access guard functions * - * @note This version of functions is to be used when no OS is present or from panic handler. - * It does not use any OS primitives and IPC and implies that - * only calling CPU is active. + * @note This version of flash guard functions is to be used when no OS is present or from panic handler. + * It does not use any OS primitives and IPC and implies that only calling CPU is active. */ extern const spi_flash_guard_funcs_t g_flash_guard_no_os_ops; diff --git a/docs/Doxyfile b/docs/Doxyfile index 7dbd1cc8d1..a3fc5faa12 100755 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -35,7 +35,8 @@ INPUT = ../components/esp32/include/esp_wifi.h \ ../components/esp32/include/esp_deep_sleep.h \ ../components/sdmmc/include/sdmmc_cmd.h \ ../components/fatfs/src/esp_vfs_fat.h \ - ../components/fatfs/src/diskio.h + ../components/fatfs/src/diskio.h \ + ../components/esp32/include/esp_core_dump.h ## Get warnings for functions that have no documentation for their parameters or return value ## diff --git a/docs/core_dump.rst b/docs/core_dump.rst new file mode 100644 index 0000000000..0d0624cc89 --- /dev/null +++ b/docs/core_dump.rst @@ -0,0 +1,127 @@ +ESP32 Core Dump +================ + +Overview +-------- + +ESP-IDF provides support to generate core dumps on unrecoverable software errors. This useful technique allows post-mortem analisys of software state at the moment of failure. +Upon the crash system enters panic state, prints some information and halts or reboots depending configuration. User can choose to generate core dump in order to analyse +the reason of failure on PC later on. Core dump contains snapshots of all tasks in the system at the moment of failure. Snapshots include tasks control blocks (TCB) and stacks. +So it is possible to find out what task, at what instruction (line of code) and what callstack of that task lead to the crash. +ESP-IDF provides special script `espcoredump.py` to help users to retrieve and analyse core dumps. This tool provides two commands for core dumps analysis: + +* info_corefile - prints crashed task's registers, callstack, list of available tasks in the system, memory regions and contents of memory stored in core dump (TCBs and stacks) +* dbg_corefile - creates core dump ELF file and runs GDB debug session with this file. User can examine memory, variables and tasks states manually. Note that since not all memory is saved in core dump only values of variables allocated on stack will be meaningfull + +Configuration +------------- + +Currently there are three options related to core dump generation which user can choose in configuration menu of the application (`make menuconfig`): + +* Disable core dump generation +* Save core dump to flash +* Print core dump to UART + +These options can be choosen in Components -> ESP32-specific config -> Core dump destination menu item. + +Save core dump to flash +----------------------- + +When this option is selected core dumps are saved to special partition on flash. When using default partition table files which are provided with ESP-IDF it automatically +allocates necessary space on flash, But if user wants to use its own layout file together with core dump feature it should define separate partition for core dump +as it is shown below:: + + # Name, Type, SubType, Offset, Size + # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild + nvs, data, nvs, 0x9000, 0x6000 + phy_init, data, phy, 0xf000, 0x1000 + factory, app, factory, 0x10000, 1M + coredump, data, 3, , 64K + +There are no special requrements for partition name. It can be choosen according to the user application needs, but partition type should be 'data' and +sub-type should be 3. Also when choosing partition size note that core dump data structure introduces constant overhead of 20 bytes and per-task overhead of 12 bytes. +This overhead does not include size of TCB and stack for every task. So partirion size should be at least 20 + max tasks number x (12 + TCB size + max task stack size) bytes. + +The example of generic command to analyze core dump from flash is: `espcoredump.py -p info_corefile ` +or `espcoredump.py -p dbg_corefile ` + +Print core dump to UART +----------------------- + +When this option is selected base64-encoded core dumps are printed on UART upon system panic. In this case user should save core dump text body to some file manually and +then run the following command: `espcoredump.py -p info_corefile -t b64 -c ` +or `espcoredump.py -p dbg_corefile -t b64 -c ` + +Base64-encoded body of core dump will be between the following header and footer:: + + ================= CORE DUMP START ================= + + ================= CORE DUMP END =================== + +Command Options For 'espcoredump.py' +-------------------------------------------- + +usage: coredumper [-h] [--chip {auto,esp32}] [--port PORT] [--baud BAUD] + {dbg_corefile,info_corefile} ... + +espcoredump.py v0.1-dev - ESP32 Core Dump Utility + +positional arguments: + {dbg_corefile,info_corefile} + Run coredumper {command} -h for additional help + dbg_corefile Starts GDB debugging session with specified corefile + info_corefile Print core dump info from file + +optional arguments: + -h, --help show this help message and exit + --chip {auto,esp32}, -c {auto,esp32} + Target chip type + --port PORT, -p PORT Serial port device + --baud BAUD, -b BAUD Serial port baud rate used when flashing/reading + + +usage: coredumper info_corefile [-h] [--gdb GDB] [--core CORE] + [--core-format CORE_FORMAT] [--off OFF] + [--save-core SAVE_CORE] [--print-mem] + prog + +positional arguments: + prog Path to program's ELF binary + +optional arguments: + -h, --help show this help message and exit + --gdb GDB, -g GDB Path to gdb + --core CORE, -c CORE Path to core dump file (if skipped core dump will be + read from flash) + --core-format CORE_FORMAT, -t CORE_FORMAT + (elf, raw or b64). File specified with "-c" is an ELF + ("elf"), raw (raw) or base64-encoded (b64) binary + --off OFF, -o OFF Ofsset of coredump partition in flash (type "make + partition_table" to see). + --save-core SAVE_CORE, -s SAVE_CORE + Save core to file. Othwerwise temporary core file will + be deleted. Does not work with "-c" + --print-mem, -m Print memory dump + + +usage: coredumper dbg_corefile [-h] [--gdb GDB] [--core CORE] + [--core-format CORE_FORMAT] [--off OFF] + [--save-core SAVE_CORE] + prog + +positional arguments: + prog Path to program's ELF binary + +optional arguments: + -h, --help show this help message and exit + --gdb GDB, -g GDB Path to gdb + --core CORE, -c CORE Path to core dump file (if skipped core dump will be + read from flash) + --core-format CORE_FORMAT, -t CORE_FORMAT + (elf, raw or b64). File specified with "-c" is an ELF + ("elf"), raw (raw) or base64-encoded (b64) binary + --off OFF, -o OFF Ofsset of coredump partition in flash (type "make + partition_table" to see). + --save-core SAVE_CORE, -s SAVE_CORE + Save core to file. Othwerwise temporary core file will + be deleted. Ignored with "-c" From ad66fbe5ada66eb2ce8c7f6dee2421560fbf8d89 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Tue, 10 Jan 2017 14:48:47 +0300 Subject: [PATCH 5/5] esp32: Fixes issues discussed during code review of MR!341 The following issues mentioned during MR!341 review were fixed: 1) Core dump test application description 2) Usage of CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH and CONFIG_ESP32_ENABLE_COREDUMP_TO_UART 3) FLASH_GUARD_START macro usage is fixed in flash API 4) Core dump module logging facility 5) cache util functions doc updated 6) interactive delay before print core dump to uart 7) core dump partion support in build system --- components/esp32/Kconfig | 23 +++ components/esp32/core_dump.c | 189 +++++++++++------- components/esp32/cpu_start.c | 2 +- components/esp32/panic.c | 32 +-- components/espcoredump/test/component.mk | 0 components/espcoredump/test/test_core_dump.c | 105 ++++++++++ .../include/freertos/FreeRTOSConfig.h | 2 +- components/log/include/esp_log.h | 10 + components/partition_table/gen_esp32part.py | 1 + .../partitions_singleapp_coredump.csv | 2 +- .../partitions_two_ota_coredump.csv | 2 +- components/spi_flash/cache_utils.h | 6 +- components/spi_flash/flash_ops.c | 39 ++-- docs/core_dump.rst | 96 +++------ docs/index.rst | 1 + 15 files changed, 324 insertions(+), 186 deletions(-) create mode 100644 components/espcoredump/test/component.mk create mode 100644 components/espcoredump/test/test_core_dump.c diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 9811585140..3bdfd230cb 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -66,12 +66,35 @@ choice ESP32_COREDUMP_TO_FLASH_OR_UART config ESP32_ENABLE_COREDUMP_TO_FLASH bool "Flash" + select ESP32_ENABLE_COREDUMP config ESP32_ENABLE_COREDUMP_TO_UART bool "UART" + select ESP32_ENABLE_COREDUMP config ESP32_ENABLE_COREDUMP_TO_NONE bool "None" endchoice +config ESP32_ENABLE_COREDUMP + bool + default F + help + Enables/disable core dump module. + +config ESP32_CORE_DUMP_UART_DELAY + int "Core dump print to UART delay" + depends on ESP32_ENABLE_COREDUMP_TO_UART + default 0 + help + Config delay (in ms) before printing core dump to UART. + Delay can be interrupted by pressing Enter key. + +config ESP32_CORE_DUMP_LOG_LEVEL + int "Core dump module logging level" + depends on ESP32_ENABLE_COREDUMP + default 1 + help + Config core dump module logging level (0-5). + # Not implemented and/or needs new silicon rev to work config MEMMAP_SPISRAM bool "Use external SPI SRAM chip as main memory" diff --git a/components/esp32/core_dump.c b/components/esp32/core_dump.c index 13519badb7..85301f4dd6 100644 --- a/components/esp32/core_dump.c +++ b/components/esp32/core_dump.c @@ -14,21 +14,40 @@ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "soc/uart_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/gpio.h" #include "esp_panic.h" #include "esp_partition.h" -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH || CONFIG_ESP32_ENABLE_COREDUMP_TO_UART +#if CONFIG_ESP32_ENABLE_COREDUMP +#define LOG_LOCAL_LEVEL CONFIG_ESP32_CORE_DUMP_LOG_LEVEL #include "esp_log.h" const static char *TAG = "esp_core_dump"; +#define ESP_COREDUMP_LOGE( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_ERROR) { ets_printf(LOG_FORMAT(E, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#define ESP_COREDUMP_LOGW( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_WARN) { ets_printf(LOG_FORMAT(W, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#define ESP_COREDUMP_LOGI( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) { ets_printf(LOG_FORMAT(I, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#define ESP_COREDUMP_LOGD( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) { ets_printf(LOG_FORMAT(D, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#define ESP_COREDUMP_LOGV( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_VERBOSE) { ets_printf(LOG_FORMAT(V, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH +#define ESP_COREDUMP_LOG_PROCESS( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) { ets_printf(LOG_FORMAT(D, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#else +#define ESP_COREDUMP_LOG_PROCESS( format, ... ) do{/*(__VA_ARGS__);*/}while(0) +#endif + + // TODO: allow user to set this in menuconfig or get tasks iteratively #define COREDUMP_MAX_TASKS_NUM 32 -typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len, int verb); -typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv, int verb); -typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv, int verb); -typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len, int verb); +typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len); +typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv); +typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv); +typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len); typedef struct _core_dump_write_config_t { @@ -40,19 +59,17 @@ typedef struct _core_dump_write_config_t } core_dump_write_config_t; -static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *write_cfg, int verb) +static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *write_cfg) { union { uint8_t data8[12]; uint32_t data32[3]; } rom_data; - //const esp_partition_t *core_part; esp_err_t err; TaskSnapshot_t tasks[COREDUMP_MAX_TASKS_NUM]; UBaseType_t tcb_sz, task_num; uint32_t data_len = 0, i, len; - //size_t off; task_num = uxTaskGetSnapshotAll(tasks, COREDUMP_MAX_TASKS_NUM, &tcb_sz); // take TCB padding into account, actual TCB size will be stored in header @@ -63,22 +80,21 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri // header + tasknum*(tcb + stack start/end + tcb addr) data_len = 3*sizeof(uint32_t) + task_num*(len + 2*sizeof(uint32_t) + sizeof(uint32_t *)); for (i = 0; i < task_num; i++) { - if (tasks[i].pxTCB == xTaskGetCurrentTaskHandle()) { + if (tasks[i].pxTCB == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) { // set correct stack top for current task tasks[i].pxTopOfStack = (StackType_t *)frame; - if (verb) - ets_printf("Current task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", frame->exit, frame->pc, frame->ps, frame->a0, frame->a1); + ESP_COREDUMP_LOG_PROCESS("Current task EXIT/PC/PS/A0/SP %x %x %x %x %x", frame->exit, frame->pc, frame->ps, frame->a0, frame->a1); } else { - if (verb) { - XtSolFrame *task_frame = (XtSolFrame *)tasks[i].pxTopOfStack; - if (task_frame->exit == 0) { - ets_printf("Task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", task_frame->exit, task_frame->pc, task_frame->ps, task_frame->a0, task_frame->a1); - } - else { - XtExcFrame *task_frame2 = (XtExcFrame *)tasks[i].pxTopOfStack; - ets_printf("Task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1); - } + XtSolFrame *task_frame = (XtSolFrame *)tasks[i].pxTopOfStack; + if (task_frame->exit == 0) { + ESP_COREDUMP_LOG_PROCESS("Task EXIT/PC/PS/A0/SP %x %x %x %x %x", task_frame->exit, task_frame->pc, task_frame->ps, task_frame->a0, task_frame->a1); + } + else { +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + XtExcFrame *task_frame2 = (XtExcFrame *)tasks[i].pxTopOfStack; +#endif + ESP_COREDUMP_LOG_PROCESS("Task EXIT/PC/PS/A0/SP %x %x %x %x %x", task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1); } } #if( portSTACK_GROWTH < 0 ) @@ -86,9 +102,7 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri #else len = (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack; #endif - if (verb) { - ets_printf("Stack len = %lu (%x %x)\r\n", len, tasks[i].pxTopOfStack, tasks[i].pxEndOfStack); - } + ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", len, tasks[i].pxTopOfStack, tasks[i].pxEndOfStack); // take stack padding into account if (len % sizeof(uint32_t)) len = (len / sizeof(uint32_t) + 1) * sizeof(uint32_t); @@ -97,22 +111,20 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri // prepare write if (write_cfg->prepare) { - err = write_cfg->prepare(write_cfg->priv, &data_len, verb); + err = write_cfg->prepare(write_cfg->priv, &data_len); if (err != ESP_OK) { - ets_printf("ERROR: Failed to prepare core dump (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to prepare core dump (%d)!", err); return; } } - if (verb) { - ets_printf("Core dump len = %lu\r\n", data_len); - } + ESP_COREDUMP_LOG_PROCESS("Core dump len = %lu", data_len); // write start if (write_cfg->start) { - err = write_cfg->start(write_cfg->priv, verb); + err = write_cfg->start(write_cfg->priv); if (err != ESP_OK) { - ets_printf("ERROR: Failed to start core dump (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to start core dump (%d)!", err); return; } } @@ -121,30 +133,28 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri rom_data.data32[0] = data_len; rom_data.data32[1] = task_num; rom_data.data32[2] = tcb_sz; - err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t), verb); + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); if (err != ESP_OK) { - ets_printf("ERROR: Failed to write core dump header (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to write core dump header (%d)!", err); return; } // write tasks for (i = 0; i < task_num; i++) { - if (verb) { - ets_printf("Dump task %x\r\n", tasks[i].pxTCB); - } + ESP_COREDUMP_LOG_PROCESS("Dump task %x", tasks[i].pxTCB); // save TCB address, stack base and stack top addr rom_data.data32[0] = (uint32_t)tasks[i].pxTCB; rom_data.data32[1] = (uint32_t)tasks[i].pxTopOfStack; rom_data.data32[2] = (uint32_t)tasks[i].pxEndOfStack; - err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t), verb); + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); if (err != ESP_OK) { - ets_printf("ERROR: Failed to write task header (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to write task header (%d)!", err); return; } // save TCB - err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz, verb); + err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz); if (err != ESP_OK) { - ets_printf("ERROR: Failed to write TCB (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to write TCB (%d)!", err); return; } // save task stack @@ -156,18 +166,18 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri tasks[i].pxEndOfStack, (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack #endif - , verb); + ); if (err != ESP_OK) { - ets_printf("ERROR: Failed to write task stack (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to write task stack (%d)!", err); return; } } // write end if (write_cfg->end) { - err = write_cfg->end(write_cfg->priv, verb); + err = write_cfg->end(write_cfg->priv); if (err != ESP_OK) { - ets_printf("ERROR: Failed to end core dump (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to end core dump (%d)!", err); return; } } @@ -202,7 +212,7 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint data_len = (data_size / sizeof(uint32_t)) * sizeof(uint32_t); err = spi_flash_write(off, data, data_len); if (err != ESP_OK) { - ets_printf("ERROR: Failed to write data to flash (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to write data to flash (%d)!", err); return 0; } @@ -214,7 +224,7 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint rom_data.data8[k] = *(data + data_len + k); err = spi_flash_write(off + data_len, &rom_data, sizeof(uint32_t)); if (err != ESP_OK) { - ets_printf("ERROR: Failed to finish write data to flash (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to finish write data to flash (%d)!", err); return 0; } data_len += sizeof(uint32_t); @@ -223,7 +233,7 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint return data_len; } -static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len, int verb) +static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len) { esp_err_t err; uint32_t sec_num; @@ -231,7 +241,7 @@ static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_le // add space for 2 magics. TODO: change to CRC if ((*data_len + 2*sizeof(uint32_t)) > s_core_part_size) { - ets_printf("ERROR: Not enough space to save core dump!\r\n"); + ESP_COREDUMP_LOGE("Not enough space to save core dump!"); return ESP_ERR_NO_MEM; } *data_len += 2*sizeof(uint32_t); @@ -243,7 +253,7 @@ static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_le sec_num++; err = spi_flash_erase_range(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE); if (err != ESP_OK) { - ets_printf("ERROR: Failed to erase flash (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to erase flash (%d)!", err); return err; } @@ -257,7 +267,7 @@ static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr err = spi_flash_write(s_core_part_start + wr_data->off, &data32, sizeof(uint32_t)); if (err != ESP_OK) { - ets_printf("ERROR: Failed to write to flash (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to write to flash (%d)!", err); return err; } wr_data->off += sizeof(uint32_t); @@ -265,16 +275,17 @@ static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr return err; } -static esp_err_t esp_core_dump_flash_write_start(void *priv, int verb) +static esp_err_t esp_core_dump_flash_write_start(void *priv) { core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; // save magic 1 return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_START); } -static esp_err_t esp_core_dump_flash_write_end(void *priv, int verb) +static esp_err_t esp_core_dump_flash_write_end(void *priv) { core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; +#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG uint32_t i; union { @@ -282,27 +293,24 @@ static esp_err_t esp_core_dump_flash_write_end(void *priv, int verb) uint32_t data32[4]; } rom_data; - if (verb) { - // TEST READ START - esp_err_t err = spi_flash_read(s_core_part_start + 0, &rom_data, sizeof(rom_data)); - if (err != ESP_OK) { - ets_printf("ERROR: Failed to read flash (%d)!\r\n", err); - return err; - } - else { - ets_printf("Data from flash:\r\n"); - for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { - ets_printf("%x\r\n", rom_data.data32[i]); - } - } - // TEST READ END + esp_err_t err = spi_flash_read(s_core_part_start + 0, &rom_data, sizeof(rom_data)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to read flash (%d)!", err); + return err; } + else { + ESP_COREDUMP_LOG_PROCESS("Data from flash:"); + for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { + ESP_COREDUMP_LOG_PROCESS("%x", rom_data.data32[i]); + } + } +#endif // save magic 2 return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_END); } -static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len, int verb) +static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len) { esp_err_t err = ESP_OK; core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; @@ -330,9 +338,9 @@ void esp_core_dump_to_flash(XtExcFrame *frame) wr_cfg.write = esp_core_dump_flash_write_data; wr_cfg.priv = &wr_data; - ets_printf("Save core dump to flash...\r\n"); - esp_core_dump_write(frame, &wr_cfg, 0); - ets_printf("Core dump has been saved to flash.\r\n"); + ESP_COREDUMP_LOGI("Save core dump to flash..."); + esp_core_dump_write(frame, &wr_cfg); + ESP_COREDUMP_LOGI("Core dump has been saved to flash."); } #endif @@ -362,27 +370,26 @@ static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8 dst[j++] = '\0'; } -static esp_err_t esp_core_dump_uart_write_start(void *priv, int verb) +static esp_err_t esp_core_dump_uart_write_start(void *priv) { esp_err_t err = ESP_OK; ets_printf("================= CORE DUMP START =================\r\n"); return err; } -static esp_err_t esp_core_dump_uart_write_end(void *priv, int verb) +static esp_err_t esp_core_dump_uart_write_end(void *priv) { esp_err_t err = ESP_OK; ets_printf("================= CORE DUMP END =================\r\n"); return err; } -static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len, int verb) +static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len) { esp_err_t err = ESP_OK; char buf[64 + 4], *addr = data; char *end = addr + data_len; - while (addr < end) { size_t len = end - addr; if (len > 48) len = 48; @@ -397,9 +404,21 @@ static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t return err; } +static int esp_core_dump_uart_get_char() { + int i; + uint32_t reg = (READ_PERI_REG(UART_STATUS_REG(0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT; + if (reg) + i = READ_PERI_REG(UART_FIFO_REG(0)); + else + i = -1; + return i; +} + void esp_core_dump_to_uart(XtExcFrame *frame) { core_dump_write_config_t wr_cfg; + uint32_t tm_end, tm_cur; + int ch; wr_cfg.prepare = NULL; wr_cfg.start = esp_core_dump_uart_write_start; @@ -407,9 +426,27 @@ void esp_core_dump_to_uart(XtExcFrame *frame) wr_cfg.write = esp_core_dump_uart_write_data; wr_cfg.priv = NULL; - ets_printf("Print core dump to uart...\r\n"); - esp_core_dump_write(frame, &wr_cfg, 0); - ets_printf("Core dump has been written to uart.\r\n"); + //Make sure txd/rxd are enabled + gpio_pullup_dis(1); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD); + + ESP_COREDUMP_LOGI("Press Enter to print core dump to UART..."); + tm_end = xthal_get_ccount() / (XT_CLOCK_FREQ / 1000) + CONFIG_ESP32_CORE_DUMP_UART_DELAY; + ch = esp_core_dump_uart_get_char(); + while (!(ch == '\n' || ch == '\r')) { + tm_cur = xthal_get_ccount() / (XT_CLOCK_FREQ / 1000); + if (tm_cur >= tm_end) + break; + /* Feed the Cerberus. */ + TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; + TIMERG0.wdt_feed = 1; + TIMERG0.wdt_wprotect = 0; + ch = esp_core_dump_uart_get_char(); + } + ESP_COREDUMP_LOGI("Print core dump to uart..."); + esp_core_dump_write(frame, &wr_cfg); + ESP_COREDUMP_LOGI("Core dump has been written to uart."); } #endif diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 2ae4260bd5..5ae68fc643 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -217,7 +217,7 @@ void start_cpu0_default(void) } #endif -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH || CONFIG_ESP32_ENABLE_COREDUMP_TO_UART +#if CONFIG_ESP32_ENABLE_COREDUMP esp_core_dump_init(); #endif diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 09ce520ded..3db09370ac 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -162,39 +162,39 @@ void panicHandler(XtExcFrame *frame) reason = reasons[regs[20]]; } haltOtherCore(); - esp_panicPutStr("Guru Meditation Error: Core "); - esp_panicPutDec(xPortGetCoreID()); - esp_panicPutStr(" panic'ed ("); + panicPutStr("Guru Meditation Error: Core "); + panicPutDec(xPortGetCoreID()); + panicPutStr(" panic'ed ("); if (!abort_called) { - esp_panicPutStr(reason); - esp_panicPutStr(")\r\n"); + panicPutStr(reason); + panicPutStr(")\r\n"); if (regs[20]==PANIC_RSN_DEBUGEXCEPTION) { int debugRsn; asm("rsr.debugcause %0":"=r"(debugRsn)); - esp_panicPutStr("Debug exception reason: "); - if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) esp_panicPutStr("SingleStep "); - if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) esp_panicPutStr("HwBreakpoint "); + panicPutStr("Debug exception reason: "); + if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) panicPutStr("SingleStep "); + if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) panicPutStr("HwBreakpoint "); if (debugRsn&XCHAL_DEBUGCAUSE_DBREAK_MASK) { //Unlike what the ISA manual says, this core seemingly distinguishes from a DBREAK //reason caused by watchdog 0 and one caused by watchdog 1 by setting bit 8 of the //debugcause if the cause is watchdog 1 and clearing it if it's watchdog 0. if (debugRsn&(1<<8)) { #if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK - esp_panicPutStr("Stack canary watchpoint triggered "); + panicPutStr("Stack canary watchpoint triggered "); #else - esp_panicPutStr("Watchpoint 1 triggered "); + panicPutStr("Watchpoint 1 triggered "); #endif } else { - esp_panicPutStr("Watchpoint 0 triggered "); + panicPutStr("Watchpoint 0 triggered "); } } - if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) esp_panicPutStr("BREAK instr "); - if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) esp_panicPutStr("BREAKN instr "); - if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) esp_panicPutStr("DebugIntr "); - esp_panicPutStr("\r\n"); + if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) panicPutStr("BREAK instr "); + if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) panicPutStr("BREAKN instr "); + if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) panicPutStr("DebugIntr "); + panicPutStr("\r\n"); } } else { - esp_panicPutStr("abort)\r\n"); + panicPutStr("abort)\r\n"); } if (esp_cpu_in_ocd_debug_mode()) { diff --git a/components/espcoredump/test/component.mk b/components/espcoredump/test/component.mk new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/espcoredump/test/test_core_dump.c b/components/espcoredump/test/test_core_dump.c new file mode 100644 index 0000000000..41137f9302 --- /dev/null +++ b/components/espcoredump/test/test_core_dump.c @@ -0,0 +1,105 @@ +/* Application For Core Dumps Generation + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" + + +// task crash indicators +#define TCI_NULL_PTR 0x1 +#define TCI_UNALIGN_PTR 0x2 +#define TCI_FAIL_ASSERT 0x4 + +volatile unsigned long crash_flags = TCI_UNALIGN_PTR; + +void bad_ptr_func() +{ + unsigned long *ptr = (unsigned long *)0; + volatile int cnt; + int i = 0; + + for (i = 0; i < 1000; i++) { + cnt++; + } + + if(crash_flags & TCI_NULL_PTR) { + printf("Write to bad address 0x%lx.\n", (unsigned long)ptr); + *ptr = 0xDEADBEEF; + } +} + +void bad_ptr_task(void *pvParameter) +{ + printf("Task 'bad_ptr_task' start.\n"); + while (1) { + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Task 'bad_ptr_task' run.\n"); + bad_ptr_func(); + } + fflush(stdout); +} + +void recur_func() +{ + static int rec_cnt; + unsigned short *ptr = (unsigned short *)0x5; + volatile int cnt; + int i = 0; + + if (rec_cnt++ > 2) { + return; + } + for (i = 0; i < 4; i++) { + cnt++; + if(i == 2) { + recur_func(); + break; + } + } + + if(crash_flags & TCI_UNALIGN_PTR) { + printf("Write to unaligned address 0x%lx.\n", (unsigned long)ptr); + *ptr = 0xDEAD; + } +} + +void unaligned_ptr_task(void *pvParameter) +{ + printf("Task 'unaligned_ptr_task' start.\n"); + while (1) { + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Task 'unaligned_ptr_task' run.\n"); + recur_func(); + } + fflush(stdout); +} + +void failed_assert_task(void *pvParameter) +{ + printf("Task 'failed_assert_task' start.\n"); + while (1) { + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Task 'failed_assert_task' run.\n"); + if(crash_flags & TCI_FAIL_ASSERT) { + printf("Assert.\n"); + assert(0); + } + } + fflush(stdout); +} + +void app_main() +{ + nvs_flash_init(); + xTaskCreate(&bad_ptr_task, "bad_ptr_task", 2048, NULL, 5, NULL); + xTaskCreatePinnedToCore(&unaligned_ptr_task, "unaligned_ptr_task", 2048, NULL, 7, NULL, 1); + xTaskCreatePinnedToCore(&failed_assert_task, "failed_assert_task", 2048, NULL, 10, NULL, 0); +} diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index 7c9d8fd972..b2fc077bc3 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -268,7 +268,7 @@ #define configXT_BOARD 1 /* Board mode */ #define configXT_SIMULATOR 0 -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH | CONFIG_ESP32_ENABLE_COREDUMP_TO_UART +#if CONFIG_ESP32_ENABLE_COREDUMP #define configENABLE_TASK_SNAPSHOT 1 #endif diff --git a/components/log/include/esp_log.h b/components/log/include/esp_log.h index 33bc10b42a..6fda8d6054 100644 --- a/components/log/include/esp_log.h +++ b/components/log/include/esp_log.h @@ -75,6 +75,16 @@ void esp_log_set_vprintf(vprintf_like_t func); */ uint32_t esp_log_timestamp(void); +/** + * @brief Function which returns timestamp to be used in log output + * + * This function uses HW cycle counter and does not depend on OS, + * so it can be safely used after application crash. + * + * @return timestamp, in milliseconds + */ +uint32_t esp_log_early_timestamp(void); + /** * @brief Write message into the log * diff --git a/components/partition_table/gen_esp32part.py b/components/partition_table/gen_esp32part.py index a4412ad45b..0491204885 100755 --- a/components/partition_table/gen_esp32part.py +++ b/components/partition_table/gen_esp32part.py @@ -127,6 +127,7 @@ class PartitionDefinition(object): "ota" : 0x00, "phy" : 0x01, "nvs" : 0x02, + "coredump" : 0x03, "esphttpd" : 0x80, "fat" : 0x81, "spiffs" : 0x82, diff --git a/components/partition_table/partitions_singleapp_coredump.csv b/components/partition_table/partitions_singleapp_coredump.csv index 96719a425a..a9f12c0fd3 100644 --- a/components/partition_table/partitions_singleapp_coredump.csv +++ b/components/partition_table/partitions_singleapp_coredump.csv @@ -3,4 +3,4 @@ nvs, data, nvs, 0x9000, 0x6000 phy_init, data, phy, 0xf000, 0x1000 factory, app, factory, 0x10000, 1M -coredump, data, 3, , 64K +coredump, data, coredump,, 64K diff --git a/components/partition_table/partitions_two_ota_coredump.csv b/components/partition_table/partitions_two_ota_coredump.csv index 3d2b34ec9a..64d70b0d8e 100644 --- a/components/partition_table/partitions_two_ota_coredump.csv +++ b/components/partition_table/partitions_two_ota_coredump.csv @@ -4,6 +4,6 @@ nvs, data, nvs, 0x9000, 0x4000 otadata, data, ota, 0xd000, 0x2000 phy_init, data, phy, 0xf000, 0x1000 factory, 0, 0, 0x10000, 1M -coredump, data, 3, , 64K +coredump, data, coredump,, 64K ota_0, 0, ota_0, , 1M ota_1, 0, ota_1, , 1M diff --git a/components/spi_flash/cache_utils.h b/components/spi_flash/cache_utils.h index a5442d1af7..598b8fba77 100644 --- a/components/spi_flash/cache_utils.h +++ b/components/spi_flash/cache_utils.h @@ -41,13 +41,11 @@ void spi_flash_disable_interrupts_caches_and_other_cpu(); void spi_flash_enable_interrupts_caches_and_other_cpu(); // Disables non-IRAM interrupt handlers on current CPU and caches on both CPUs. -// This function is implied to be called from panic handler -// (when non-current CPU is halted and can not execute code from flash) or when no OS is present. +// This function is implied to be called when other CPU is not running or running code from IRAM. void spi_flash_disable_interrupts_caches_and_other_cpu_no_os(); // Enable cache, enable interrupts on current CPU. -// This function is implied to be called from panic handler -// (when non-current CPU is halted and can not execute code from flash) or when no OS is present. +// This function is implied to be called when other CPU is not running or running code from IRAM. void spi_flash_enable_interrupts_caches_no_os(); #endif //ESP_SPI_FLASH_CACHE_UTILS_H diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 831679f437..fffe487bd1 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -58,9 +58,6 @@ static spi_flash_counters_t s_flash_stats; #endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS -#define FLASH_GUARD_START(_gp_) do{if((_gp_)) (_gp_)->start();}while(0) -#define FLASH_GUARD_END(_gp_) do{if((_gp_)) (_gp_)->end();}while(0) - static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc); const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_default_ops = { @@ -106,6 +103,18 @@ SpiFlashOpResult IRAM_ATTR spi_flash_unlock() return SPI_FLASH_RESULT_OK; } +static inline void spi_flash_guard_start() +{ + if (s_flash_guard_ops) + s_flash_guard_ops->start(); +} + +static inline void spi_flash_guard_end() +{ + if (s_flash_guard_ops) + s_flash_guard_ops->end(); +} + esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec) { return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE); @@ -126,7 +135,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) size_t end = start + size / SPI_FLASH_SEC_SIZE; const size_t sectors_per_block = BLOCK_ERASE_SIZE / SPI_FLASH_SEC_SIZE; COUNTER_START(); - FLASH_GUARD_START(s_flash_guard_ops); + spi_flash_guard_start(); SpiFlashOpResult rc; rc = spi_flash_unlock(); if (rc == SPI_FLASH_RESULT_OK) { @@ -142,7 +151,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) } } } - FLASH_GUARD_END(s_flash_guard_ops); + spi_flash_guard_end(); COUNTER_STOP(erase); return spi_flash_translate_rc(rc); } @@ -180,9 +189,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) if (left_size > 0) { uint32_t t = 0xffffffff; memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size); - FLASH_GUARD_START(s_flash_guard_ops); + spi_flash_guard_start(); rc = SPIWrite(left_off, &t, 4); - FLASH_GUARD_END(s_flash_guard_ops); + spi_flash_guard_end(); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -198,9 +207,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) bool in_dram = true; #endif if (in_dram && (((uintptr_t) srcc) + mid_off) % 4 == 0) { - FLASH_GUARD_START(s_flash_guard_ops); + spi_flash_guard_start(); rc = SPIWrite(dst + mid_off, (const uint32_t *) (srcc + mid_off), mid_size); - FLASH_GUARD_END(s_flash_guard_ops); + spi_flash_guard_end(); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -214,9 +223,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) uint32_t t[8]; uint32_t write_size = MIN(mid_size, sizeof(t)); memcpy(t, srcc + mid_off, write_size); - FLASH_GUARD_START(s_flash_guard_ops); + spi_flash_guard_start(); rc = SPIWrite(dst + mid_off, t, write_size); - FLASH_GUARD_END(s_flash_guard_ops); + spi_flash_guard_end(); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -229,9 +238,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) if (right_size > 0) { uint32_t t = 0xffffffff; memcpy(&t, srcc + right_off, right_size); - FLASH_GUARD_START(s_flash_guard_ops); + spi_flash_guard_start(); rc = SPIWrite(dst + right_off, &t, 4); - FLASH_GUARD_END(s_flash_guard_ops); + spi_flash_guard_end(); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -291,7 +300,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) SpiFlashOpResult rc = SPI_FLASH_RESULT_OK; COUNTER_START(); - FLASH_GUARD_START(s_flash_guard_ops); + spi_flash_guard_start(); /* To simplify boundary checks below, we handle small reads separately. */ if (size < 16) { uint32_t t[6]; /* Enough for 16 bytes + 4 on either side for padding. */ @@ -365,7 +374,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) memcpy(dstc + pad_right_off, t, pad_right_size); } out: - FLASH_GUARD_END(s_flash_guard_ops); + spi_flash_guard_end(); COUNTER_STOP(read); return spi_flash_translate_rc(rc); } diff --git a/docs/core_dump.rst b/docs/core_dump.rst index 0d0624cc89..a8e328996f 100644 --- a/docs/core_dump.rst +++ b/docs/core_dump.rst @@ -1,10 +1,10 @@ ESP32 Core Dump -================ +=============== Overview -------- -ESP-IDF provides support to generate core dumps on unrecoverable software errors. This useful technique allows post-mortem analisys of software state at the moment of failure. +ESP-IDF provides support to generate core dumps on unrecoverable software errors. This useful technique allows post-mortem analysis of software state at the moment of failure. Upon the crash system enters panic state, prints some information and halts or reboots depending configuration. User can choose to generate core dump in order to analyse the reason of failure on PC later on. Core dump contains snapshots of all tasks in the system at the moment of failure. Snapshots include tasks control blocks (TCB) and stacks. So it is possible to find out what task, at what instruction (line of code) and what callstack of that task lead to the crash. @@ -30,16 +30,16 @@ Save core dump to flash When this option is selected core dumps are saved to special partition on flash. When using default partition table files which are provided with ESP-IDF it automatically allocates necessary space on flash, But if user wants to use its own layout file together with core dump feature it should define separate partition for core dump as it is shown below:: - + # Name, Type, SubType, Offset, Size # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild nvs, data, nvs, 0x9000, 0x6000 phy_init, data, phy, 0xf000, 0x1000 factory, app, factory, 0x10000, 1M - coredump, data, 3, , 64K - + coredump, data, coredump,, 64K + There are no special requrements for partition name. It can be choosen according to the user application needs, but partition type should be 'data' and -sub-type should be 3. Also when choosing partition size note that core dump data structure introduces constant overhead of 20 bytes and per-task overhead of 12 bytes. +sub-type should be 'coredump'. Also when choosing partition size note that core dump data structure introduces constant overhead of 20 bytes and per-task overhead of 12 bytes. This overhead does not include size of TCB and stack for every task. So partirion size should be at least 20 + max tasks number x (12 + TCB size + max task stack size) bytes. The example of generic command to analyze core dump from flash is: `espcoredump.py -p info_corefile ` @@ -55,73 +55,27 @@ or `espcoredump.py -p dbg_corefile -t b64 -c + ================= CORE DUMP END =================== -Command Options For 'espcoredump.py' --------------------------------------------- +Running 'espcoredump.py' +------------------------------------ -usage: coredumper [-h] [--chip {auto,esp32}] [--port PORT] [--baud BAUD] - {dbg_corefile,info_corefile} ... +Generic command syntax: -espcoredump.py v0.1-dev - ESP32 Core Dump Utility +`espcoredump.py [options] command [args]` -positional arguments: - {dbg_corefile,info_corefile} - Run coredumper {command} -h for additional help - dbg_corefile Starts GDB debugging session with specified corefile - info_corefile Print core dump info from file - -optional arguments: - -h, --help show this help message and exit - --chip {auto,esp32}, -c {auto,esp32} - Target chip type - --port PORT, -p PORT Serial port device - --baud BAUD, -b BAUD Serial port baud rate used when flashing/reading - - -usage: coredumper info_corefile [-h] [--gdb GDB] [--core CORE] - [--core-format CORE_FORMAT] [--off OFF] - [--save-core SAVE_CORE] [--print-mem] - prog - -positional arguments: - prog Path to program's ELF binary - -optional arguments: - -h, --help show this help message and exit - --gdb GDB, -g GDB Path to gdb - --core CORE, -c CORE Path to core dump file (if skipped core dump will be - read from flash) - --core-format CORE_FORMAT, -t CORE_FORMAT - (elf, raw or b64). File specified with "-c" is an ELF - ("elf"), raw (raw) or base64-encoded (b64) binary - --off OFF, -o OFF Ofsset of coredump partition in flash (type "make - partition_table" to see). - --save-core SAVE_CORE, -s SAVE_CORE - Save core to file. Othwerwise temporary core file will - be deleted. Does not work with "-c" - --print-mem, -m Print memory dump - - -usage: coredumper dbg_corefile [-h] [--gdb GDB] [--core CORE] - [--core-format CORE_FORMAT] [--off OFF] - [--save-core SAVE_CORE] - prog - -positional arguments: - prog Path to program's ELF binary - -optional arguments: - -h, --help show this help message and exit - --gdb GDB, -g GDB Path to gdb - --core CORE, -c CORE Path to core dump file (if skipped core dump will be - read from flash) - --core-format CORE_FORMAT, -t CORE_FORMAT - (elf, raw or b64). File specified with "-c" is an ELF - ("elf"), raw (raw) or base64-encoded (b64) binary - --off OFF, -o OFF Ofsset of coredump partition in flash (type "make - partition_table" to see). - --save-core SAVE_CORE, -s SAVE_CORE - Save core to file. Othwerwise temporary core file will - be deleted. Ignored with "-c" +:Script Options: + * --chip,-c {auto,esp32}. Target chip type. Supported values are `auto` and `esp32`. + * --port,-p PORT. Serial port device. + * --baud,-b BAUD. Serial port baud rate used when flashing/reading. +:Commands: + * info_corefile. Retrieve core dump and print useful info. + * dbg_corefile. Retrieve core dump and start GDB session with it. +:Command Arguments: + * --gdb,-g GDB. Path to gdb to use for data retrieval. + * --core,-c CORE. Path to core dump file to use (if skipped core dump will be read from flash). + * --core-format,-t CORE_FORMAT. Specifies that file passed with "-c" is an ELF ("elf"), dumped raw binary ("raw") or base64-encoded ("b64") format. + * --off,-o OFF. Ofsset of coredump partition in flash (type "make partition_table" to see it). + * --save-core,-s SAVE_CORE. Save core to file. Othwerwise temporary core file will be deleted. Ignored with "-c". + * --print-mem,-m Print memory dump. Used only with "info_corefile". diff --git a/docs/index.rst b/docs/index.rst index 2d9a62f14b..b994648848 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -35,6 +35,7 @@ Contents: partition-tables build_system openocd + core_dump Flash encryption Secure Boot ULP coprocessor