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
This commit is contained in:
Alexey Gerenkov
2016-12-22 02:56:23 +03:00
parent 5fbea86a9e
commit 4a3e160888
19 changed files with 1715 additions and 76 deletions
+18
View File
@@ -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"
+276
View File
@@ -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
}
+5
View File
@@ -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);
+21
View File
@@ -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
+4
View File
@@ -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
+67 -58
View File
@@ -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
}