coredump: improvements refactoring

Move existing core dump files into espcoredump component folder
Add KConfig, linker.lf, make and CMakeList.txt for new component
Existing functionality separated into core_dump_common, core_dump_flash, core_dump_uart
Update test_core_dump.c and make files to link it as unit test
Update according to review:
Move target and RTOS related functionality into separated file (core_dump_port.c).
This commit is contained in:
aleks 2019-01-29 15:49:56 +01:00 committed by bot
parent 4fd418dfe7
commit 10fe158f67
18 changed files with 961 additions and 687 deletions

View File

@ -14,7 +14,6 @@ else()
"cache_sram_mmu.c"
"clk.c"
"coexist.c"
"core_dump.c"
"cpu_start.c"
"crosscore_int.c"
"dbg_stubs.c"

View File

@ -285,51 +285,6 @@ menu "ESP32-specific"
default 0x4000 if MEMMAP_TRACEMEM && !MEMMAP_TRACEMEM_TWOBANKS
default 0x0
menu "Core dump"
choice ESP32_COREDUMP_TO_FLASH_OR_UART
prompt "Data 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"
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_MAX_TASKS_NUM
int "Maximum number of tasks"
depends on ESP32_ENABLE_COREDUMP
default 64
help
Maximum number of tasks snapshots in core dump.
config ESP32_CORE_DUMP_UART_DELAY
int "Delay before print to UART"
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.
endmenu
choice NUMBER_OF_UNIVERSAL_MAC_ADDRESS
bool "Number of universally administered (by IEEE) MAC address"
default FOUR_UNIVERSAL_MAC_ADDRESS

View File

@ -1,632 +0,0 @@
// 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 <string.h>
#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 "rom/crc.h"
#include "esp_panic.h"
#include "esp_partition.h"
#include "esp_clk.h"
#include "esp_core_dump.h"
#include "esp_log.h"
const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump";
typedef uint32_t core_dump_crc_t;
#if CONFIG_ESP32_ENABLE_COREDUMP
#define ESP_COREDUMP_LOG( level, format, ... ) if (LOG_LOCAL_LEVEL >= level) { ets_printf(DRAM_STR(format), esp_log_early_timestamp(), (const char *)TAG, ##__VA_ARGS__); }
#define ESP_COREDUMP_LOGE( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_ERROR, LOG_FORMAT(E, format), ##__VA_ARGS__)
#define ESP_COREDUMP_LOGW( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_WARN, LOG_FORMAT(W, format), ##__VA_ARGS__)
#define ESP_COREDUMP_LOGI( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_INFO, LOG_FORMAT(I, format), ##__VA_ARGS__)
#define ESP_COREDUMP_LOGD( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_DEBUG, LOG_FORMAT(D, format), ##__VA_ARGS__)
#define ESP_COREDUMP_LOGV( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_VERBOSE, LOG_FORMAT(V, format), ##__VA_ARGS__)
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
#define ESP_COREDUMP_LOG_PROCESS( format, ... ) ESP_COREDUMP_LOGD(format, ##__VA_ARGS__)
#else
#define ESP_COREDUMP_LOG_PROCESS( format, ... ) do{/*(__VA_ARGS__);*/}while(0)
#endif
#define COREDUMP_MAX_TASK_STACK_SIZE (64*1024)
#define COREDUMP_VERSION 1
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);
/** core dump emitter control structure */
typedef struct _core_dump_write_config_t
{
// this function is called before core dump data writing
// used for sanity checks
esp_core_dump_write_prepare_t prepare;
// this function is called at the beginning of data writing
esp_core_dump_write_start_t start;
// this function is called when all dump data are written
esp_core_dump_write_end_t end;
// this function is called to write data chunk
esp_core_dump_flash_write_data_t write;
// number of tasks with corrupted TCBs
uint32_t bad_tasks_num;
// pointer to data which are specific for particular core dump emitter
void * priv;
} core_dump_write_config_t;
/** core dump data header */
typedef struct _core_dump_header_t
{
uint32_t data_len; // data length
uint32_t version; // core dump struct version
uint32_t tasks_num; // number of tasks
uint32_t tcb_sz; // size of TCB
} core_dump_header_t;
/** core dump task data header */
typedef struct _core_dump_task_header_t
{
void * tcb_addr; // TCB address
uint32_t stack_start; // stack start address
uint32_t stack_end; // stack end address
} core_dump_task_header_t;
static inline bool esp_task_stack_start_is_sane(uint32_t sp)
{
return !(sp < 0x3ffae010UL || sp > 0x3fffffffUL);
}
static inline bool esp_tcb_addr_is_sane(uint32_t addr, uint32_t sz)
{
//TODO: currently core dump supports TCBs in DRAM only, external SRAM not supported yet
return !(addr < 0x3ffae000UL || (addr + sz) > 0x40000000UL);
}
static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *write_cfg)
{
int cur_task_bad = 0;
esp_err_t err;
TaskSnapshot_t tasks[CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM];
UBaseType_t tcb_sz, tcb_sz_padded, task_num;
uint32_t data_len = 0, i, len;
union
{
core_dump_header_t hdr;
core_dump_task_header_t task_hdr;
} dump_data;
task_num = uxTaskGetSnapshotAll(tasks, CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM, &tcb_sz);
// take TCB padding into account, actual TCB size will be stored in header
if (tcb_sz % sizeof(uint32_t))
tcb_sz_padded = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t);
else
tcb_sz_padded = tcb_sz;
// header + tasknum*(tcb + stack start/end + tcb addr)
data_len = sizeof(core_dump_header_t) + task_num*(tcb_sz_padded + sizeof(core_dump_task_header_t));
for (i = 0; i < task_num; i++) {
if (!esp_tcb_addr_is_sane((uint32_t)tasks[i].pxTCB, tcb_sz)) {
ESP_COREDUMP_LOG_PROCESS("Bad TCB addr %x!", tasks[i].pxTCB);
write_cfg->bad_tasks_num++;
continue;
}
if (tasks[i].pxTCB == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) {
// set correct stack top for current task
tasks[i].pxTopOfStack = (StackType_t *)frame;
// This field is not initialized for crashed task, but stack frame has the structure of interrupt one,
// so make workaround to allow espcoredump to parse it properly.
if (frame->exit == 0)
frame->exit = -1;
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 {
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;
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);
#endif
}
}
len = (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack;
// check task's stack
if (!esp_stack_ptr_is_sane((uint32_t)tasks[i].pxTopOfStack) || !esp_task_stack_start_is_sane((uint32_t)tasks[i].pxEndOfStack)
|| len > COREDUMP_MAX_TASK_STACK_SIZE) {
if (tasks[i].pxTCB == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) {
cur_task_bad = 1;
}
ESP_COREDUMP_LOG_PROCESS("Corrupted TCB %x: stack len %lu, top %x, end %x!",
tasks[i].pxTCB, len, tasks[i].pxTopOfStack, tasks[i].pxEndOfStack);
tasks[i].pxTCB = 0; // make TCB addr invalid to skip it in dump
write_cfg->bad_tasks_num++;
} else {
ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", len, tasks[i].pxTopOfStack, tasks[i].pxEndOfStack);
// take stack padding into account
len = (len + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);
data_len += len;
}
}
data_len -= write_cfg->bad_tasks_num*(tcb_sz_padded + sizeof(core_dump_task_header_t));
ESP_COREDUMP_LOG_PROCESS("Core dump len = %lu (%d %d)", data_len, task_num, write_cfg->bad_tasks_num);
// prepare write
if (write_cfg->prepare) {
err = write_cfg->prepare(write_cfg->priv, &data_len);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to prepare core dump (%d)!", err);
return;
}
}
// write start
if (write_cfg->start) {
err = write_cfg->start(write_cfg->priv);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to start core dump (%d)!", err);
return;
}
}
// write header
dump_data.hdr.data_len = data_len;
dump_data.hdr.version = COREDUMP_VERSION;
dump_data.hdr.tasks_num = task_num - write_cfg->bad_tasks_num;
dump_data.hdr.tcb_sz = tcb_sz;
err = write_cfg->write(write_cfg->priv, &dump_data, sizeof(core_dump_header_t));
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to write core dump header (%d)!", err);
return;
}
// write tasks
for (i = 0; i < task_num; i++) {
if (!esp_tcb_addr_is_sane((uint32_t)tasks[i].pxTCB, tcb_sz)) {
ESP_COREDUMP_LOG_PROCESS("Skip TCB with bad addr %x!", tasks[i].pxTCB);
continue;
}
ESP_COREDUMP_LOG_PROCESS("Dump task %x", tasks[i].pxTCB);
// save TCB address, stack base and stack top addr
dump_data.task_hdr.tcb_addr = tasks[i].pxTCB;
dump_data.task_hdr.stack_start = (uint32_t)tasks[i].pxTopOfStack;
dump_data.task_hdr.stack_end = (uint32_t)tasks[i].pxEndOfStack;
err = write_cfg->write(write_cfg->priv, &dump_data, sizeof(core_dump_task_header_t));
if (err != ESP_OK) {
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);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to write TCB (%d)!", err);
return;
}
// save task stack
if (tasks[i].pxTopOfStack != 0 && tasks[i].pxEndOfStack != 0) {
err = write_cfg->write(write_cfg->priv, tasks[i].pxTopOfStack,
(uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to write task stack (%d)!", err);
return;
}
} else {
ESP_COREDUMP_LOG_PROCESS("Skip corrupted task %x stack!", tasks[i].pxTCB);
}
}
// write end
if (write_cfg->end) {
err = write_cfg->end(write_cfg->priv);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to end core dump (%d)!", err);
return;
}
}
if (write_cfg->bad_tasks_num) {
ESP_COREDUMP_LOGE("Skipped %d tasks with bad TCB!", write_cfg->bad_tasks_num);
if (cur_task_bad) {
ESP_COREDUMP_LOGE("Crashed task has been skipped!");
}
}
}
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
typedef struct _core_dump_write_flash_data_t
{
uint32_t off; // current offset in partition
core_dump_crc_t crc; // CRC of dumped data
} core_dump_write_flash_data_t;
typedef struct _core_dump_partition_t
{
// core dump partition start
uint32_t start;
// core dump partition size
uint32_t size;
} core_dump_partition_t;
typedef struct _core_dump_flash_config_t
{
// core dump partition config
core_dump_partition_t partition;
// CRC of core dump partition config
core_dump_crc_t partition_config_crc;
} core_dump_flash_config_t;
// core dump flash data
static core_dump_flash_config_t s_core_flash_config;
static inline core_dump_crc_t esp_core_dump_calc_flash_config_crc(void)
{
return crc32_le(0, (uint8_t const *)&s_core_flash_config.partition, sizeof(s_core_flash_config.partition));
}
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);
assert(off >= s_core_flash_config.partition.start);
assert((off + data_len + (data_size % sizeof(uint32_t) ? sizeof(uint32_t) : 0)) <=
s_core_flash_config.partition.start + s_core_flash_config.partition.size);
err = spi_flash_write(off, data, data_len);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to write data to flash (%d)!", err);
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(off + data_len, &rom_data, sizeof(uint32_t));
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to finish write data to flash (%d)!", err);
return 0;
}
data_len += sizeof(uint32_t);
}
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;
// check for available space in partition
if ((*data_len + sizeof(uint32_t)) > s_core_flash_config.partition.size) {
ESP_COREDUMP_LOGE("Not enough space to save core dump!");
return ESP_ERR_NO_MEM;
}
// add space for CRC
*data_len += sizeof(core_dump_crc_t);
memset(wr_data, 0, sizeof(*wr_data));
sec_num = *data_len / SPI_FLASH_SEC_SIZE;
if (*data_len % SPI_FLASH_SEC_SIZE) {
sec_num++;
}
assert(sec_num * SPI_FLASH_SEC_SIZE <= s_core_flash_config.partition.size);
err = spi_flash_erase_range(s_core_flash_config.partition.start + 0, sec_num * SPI_FLASH_SEC_SIZE);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to erase flash (%d)!", err);
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;
assert(wr_data->off + sizeof(uint32_t) <= s_core_flash_config.partition.size);
err = spi_flash_write(s_core_flash_config.partition.start + wr_data->off, &data32, sizeof(uint32_t));
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to write to flash (%d)!", err);
return err;
}
wr_data->off += sizeof(uint32_t);
return err;
}
static esp_err_t esp_core_dump_flash_write_start(void *priv)
{
return ESP_OK;
}
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
union
{
uint8_t data8[16];
uint32_t data32[4];
} rom_data;
esp_err_t err = spi_flash_read(s_core_flash_config.partition.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 (uint32_t i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) {
ESP_COREDUMP_LOG_PROCESS("%x", rom_data.data32[i]);
}
}
#endif
// write core dump CRC
ESP_COREDUMP_LOG_PROCESS("Dump data CRC = 0x%x", wr_data->crc);
return esp_core_dump_flash_write_word(wr_data, wr_data->crc);
}
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_flash_config.partition.start + wr_data->off, data, data_len);
if (len != data_len) {
return ESP_FAIL;
}
wr_data->off += len;
wr_data->crc = crc32_le(wr_data->crc, data, data_len);
return err;
}
void esp_core_dump_to_flash(XtExcFrame *frame)
{
core_dump_write_config_t wr_cfg;
core_dump_write_flash_data_t wr_data;
core_dump_crc_t crc = esp_core_dump_calc_flash_config_crc();
if (s_core_flash_config.partition_config_crc != crc) {
ESP_COREDUMP_LOGE("Core dump flash config is corrupted! CRC=0x%x instead of 0x%x", crc, s_core_flash_config.partition_config_crc);
return;
}
// check that partition can hold at least core dump data length
if (s_core_flash_config.partition.start == 0 || s_core_flash_config.partition.size < sizeof(uint32_t)) {
ESP_COREDUMP_LOGE("Invalid flash partition config!");
return;
}
/* init non-OS flash access critical section */
spi_flash_guard_set(&g_flash_guard_no_os_ops);
memset(&wr_cfg, 0, sizeof(wr_cfg));
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_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
#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) {
const static DRAM_ATTR 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_start(void *priv)
{
esp_err_t err = ESP_OK;
ets_printf(DRAM_STR("================= CORE DUMP START =================\r\n"));
return err;
}
static esp_err_t esp_core_dump_uart_write_end(void *priv)
{
esp_err_t err = ESP_OK;
ets_printf(DRAM_STR("================= 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)
{
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;
/* 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;
ets_printf(DRAM_STR("%s\r\n"), buf);
}
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;
memset(&wr_cfg, 0, sizeof(wr_cfg));
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;
//Make sure txd/rxd are enabled
// use direct reg access instead of gpio_pullup_dis which can cause exception when flash cache is disabled
REG_CLR_BIT(GPIO_PIN_REG_1, FUN_PU);
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...");
const int cpu_ticks_per_ms = esp_clk_cpu_freq() / 1000;
tm_end = xthal_get_ccount() / cpu_ticks_per_ms + CONFIG_ESP32_CORE_DUMP_UART_DELAY;
ch = esp_core_dump_uart_get_char();
while (!(ch == '\n' || ch == '\r')) {
tm_cur = xthal_get_ccount() / cpu_ticks_per_ms;
if (tm_cur >= tm_end){
break;
}
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
void esp_core_dump_init()
{
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
const esp_partition_t *core_part;
ESP_COREDUMP_LOGI("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_COREDUMP_LOGE("No core dump partition found!");
return;
}
ESP_COREDUMP_LOGI("Found partition '%s' @ %x %d bytes", core_part->label, core_part->address, core_part->size);
s_core_flash_config.partition.start = core_part->address;
s_core_flash_config.partition.size = core_part->size;
s_core_flash_config.partition_config_crc = esp_core_dump_calc_flash_config_crc();
#endif
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART
ESP_COREDUMP_LOGI("Init core dump to UART");
#endif
}
esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size)
{
esp_err_t err;
const void *core_data;
spi_flash_mmap_handle_t core_data_handle;
if (out_addr == NULL || out_size == NULL) {
return ESP_ERR_INVALID_ARG;
}
const esp_partition_t *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_FAIL;
}
if (core_part->size < sizeof(uint32_t)) {
ESP_LOGE(TAG, "Too small core dump partition!");
return ESP_FAIL;
}
err = esp_partition_mmap(core_part, 0, sizeof(uint32_t),
SPI_FLASH_MMAP_DATA, &core_data, &core_data_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err);
return err;
}
uint32_t *dw = (uint32_t *)core_data;
*out_size = *dw;
spi_flash_munmap(core_data_handle);
// remap full core dump with CRC
err = esp_partition_mmap(core_part, 0, *out_size,
SPI_FLASH_MMAP_DATA, &core_data, &core_data_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err);
return err;
}
uint32_t *crc = (uint32_t *)(((uint8_t *)core_data) + *out_size);
crc--; // Point to CRC field
// Calc CRC over core dump data except for CRC field
core_dump_crc_t cur_crc = crc32_le(0, (uint8_t const *)core_data, *out_size - sizeof(core_dump_crc_t));
if (*crc != cur_crc) {
ESP_LOGE(TAG, "Core dump data CRC check failed: 0x%x -> 0x%x!", *crc, cur_crc);
spi_flash_munmap(core_data_handle);
return ESP_FAIL;
}
spi_flash_munmap(core_data_handle);
*out_addr = core_part->address;
return ESP_OK;
}
#endif

View File

@ -1,7 +1,6 @@
[mapping]
archive: libesp32.a
entries:
core_dump (noflash_text)
panic (noflash)
[mapping]

View File

@ -0,0 +1,11 @@
set(COMPONENT_PRIV_INCLUDEDIRS "include_core_dump")
set(COMPONENT_ADD_INCLUDEDIRS "include")
set(COMPONENT_REQUIRES)
set(COMPONENT_PRIV_REQUIRES spi_flash)
set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
set(COMPONENT_SRCS "src/core_dump_common.c"
"src/core_dump_flash.c"
"src/core_dump_port.c"
"src/core_dump_uart.c")
register_component()

View File

@ -0,0 +1,45 @@
menu "Core dump"
choice ESP32_COREDUMP_TO_FLASH_OR_UART
prompt "Data 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"
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_MAX_TASKS_NUM
int "Maximum number of tasks"
depends on ESP32_ENABLE_COREDUMP
default 64
help
Maximum number of tasks snapshots in core dump.
config ESP32_CORE_DUMP_UART_DELAY
int "Delay before print to UART"
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.
endmenu

View File

@ -0,0 +1,4 @@
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_SRCDIRS := src
COMPONENT_PRIV_INCLUDEDIRS := include_core_dump
COMPONENT_ADD_LDFRAGMENTS += linker.lf

View File

@ -628,6 +628,7 @@ class ESPCoreDumpLoader(object):
logging.warning("Skip task's (%x) stack %d bytes @ 0x%x. (Reason: %s)" % (tcb_addr, stack_len_aligned, stack_base, e))
core_off += stack_len_aligned
try:
logging.info("Stack start_end: 0x%x @ 0x%x" % (stack_top, stack_end))
task_regs = self._get_registers_from_stack(data, stack_end > stack_top)
except Exception as e:
print(e)

View File

@ -0,0 +1,85 @@
// 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_
/**************************************************************************************/
/******************************** EXCEPTION MODE API **********************************/
/**************************************************************************************/
/**
* @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:
*
* | TOTAL_LEN | VERSION | 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 |
* | CRC32 |
*
* 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 CRC is used at the end of core the dump data.
* The structure of core dump data is described below in details.
* 1) Core dump starts with header:
* 1.1) TOTAL_LEN is total length of core dump data in flash including CRC. Size is 4 bytes.
* 1.2) VERSION field keeps 4 byte version of core dump.
* 1.2) TASKS_NUM is the number of tasks for which data are stored. Size is 4 bytes.
* 1.3) TCB_SIZE is the size of task's TCB structure. Size is 4 bytes.
* 2) Core dump header is followed by the data for every task in the system.
* Task data are started with task header:
* 2.1) TCB_ADDR is the address of TCB in memory. Size is 4 bytes.
* 2.2) STACK_TOP is the top of task's stack (address of the topmost stack item). Size is 4 bytes.
* 2.2) STACK_END is the end of task's stack (address from which task's stack starts). Size is 4 bytes.
* 3) Task header is followed by TCB data. Size is TCB_SIZE bytes.
* 4) Task's stack is placed after TCB data. Size is (STACK_END - STACK_TOP) bytes.
* 5) CRC is placed at the end of the data.
*/
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) CRC is not present in core dump printed to UART.
* 2) Since CRC is omitted TOTAL_LEN does not include its 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();
/**************************************************************************************/
/*********************************** USER MODE API ************************************/
/**************************************************************************************/
/**
* @brief Retrieves address and size of coredump data in flash.
* This function is always available, even when core dump is disabled in menuconfig.
*
* @param out_addr pointer to store image address in flash.
* @param out_size pointer to store image size in flash (including CRC). In bytes.
*
* @return ESP_OK on success, otherwise \see esp_err_t
*/
esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size);
#endif

View File

@ -0,0 +1,105 @@
// 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_
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
#include "esp_log.h"
#define ESP_COREDUMP_LOG( level, format, ... ) if (LOG_LOCAL_LEVEL >= level) { ets_printf(DRAM_STR(format), esp_log_early_timestamp(), (const char *)TAG, ##__VA_ARGS__); }
#define ESP_COREDUMP_LOGE( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_ERROR, LOG_FORMAT(E, format), ##__VA_ARGS__)
#define ESP_COREDUMP_LOGW( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_WARN, LOG_FORMAT(W, format), ##__VA_ARGS__)
#define ESP_COREDUMP_LOGI( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_INFO, LOG_FORMAT(I, format), ##__VA_ARGS__)
#define ESP_COREDUMP_LOGD( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_DEBUG, LOG_FORMAT(D, format), ##__VA_ARGS__)
#define ESP_COREDUMP_LOGV( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_VERBOSE, LOG_FORMAT(V, format), ##__VA_ARGS__)
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
#define ESP_COREDUMP_LOG_PROCESS( format, ... ) ESP_COREDUMP_LOGD(format, ##__VA_ARGS__)
#else
#define ESP_COREDUMP_LOG_PROCESS( format, ... ) do{/*(__VA_ARGS__);*/}while(0)
#endif
#define COREDUMP_MAX_TASK_STACK_SIZE (64*1024)
#define COREDUMP_VERSION 1
typedef uint32_t core_dump_crc_t;
#if CONFIG_ESP32_ENABLE_COREDUMP
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);
/** core dump emitter control structure */
typedef struct _core_dump_write_config_t
{
// this function is called before core dump data writing
// used for sanity checks
esp_core_dump_write_prepare_t prepare;
// this function is called at the beginning of data writing
esp_core_dump_write_start_t start;
// this function is called when all dump data are written
esp_core_dump_write_end_t end;
// this function is called to write data chunk
esp_core_dump_flash_write_data_t write;
// number of tasks with corrupted TCBs
uint32_t bad_tasks_num;
// pointer to data which are specific for particular core dump emitter
void * priv;
} core_dump_write_config_t;
/** core dump data header */
typedef struct _core_dump_header_t
{
uint32_t data_len; // data length
uint32_t version; // core dump struct version
uint32_t tasks_num; // number of tasks
uint32_t tcb_sz; // size of TCB
} core_dump_header_t;
/** core dump task data header */
typedef struct _core_dump_task_header_t
{
void * tcb_addr; // TCB address
uint32_t stack_start; // stack start address
uint32_t stack_end; // stack end address
} core_dump_task_header_t;
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
// Core dump flash init function
void esp_core_dump_flash_init();
#endif
// Common core dump write function
void esp_core_dump_write(void *frame, core_dump_write_config_t *write_cfg);
// Gets RTOS tasks snapshot
uint32_t esp_core_dump_get_tasks_snapshot(core_dump_task_header_t* const tasks,
const uint32_t snapshot_size, uint32_t* const tcb_sz);
// Checks TCB consistency
bool esp_tcb_addr_is_sane(uint32_t addr, uint32_t sz);
bool esp_core_dump_process_tcb(void *frame, core_dump_task_header_t *task_snaphort, uint32_t tcb_sz);
bool esp_core_dump_process_stack(core_dump_task_header_t* task_snaphort, uint32_t *length);
#endif
#endif

View File

@ -0,0 +1,6 @@
[mapping]
archive: libespcoredump.a
entries:
core_dump_uart (noflash_text)
core_dump_flash (noflash_text)
core_dump_common (noflash_text)

View File

@ -0,0 +1,217 @@
// 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 <string.h>
#include <stdbool.h>
#include "rom/crc.h"
#include "esp_panic.h"
#include "esp_partition.h"
#include "esp_core_dump_priv.h"
const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_common";
#if CONFIG_ESP32_ENABLE_COREDUMP
static esp_err_t esp_core_dump_write_binary(void *frame, core_dump_write_config_t *write_cfg)
{
esp_err_t err;
core_dump_task_header_t tasks[CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM];
uint32_t tcb_sz, task_num, tcb_sz_padded;
bool task_is_valid = false;
uint32_t data_len = 0, i;
union
{
core_dump_header_t hdr;
core_dump_task_header_t task_hdr;
} dump_data;
task_num = esp_core_dump_get_tasks_snapshot(tasks, CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM, &tcb_sz);
ESP_COREDUMP_LOGI("Found tasks: (%d)!", task_num);
// Take TCB padding into account, actual TCB size will be stored in header
if (tcb_sz % sizeof(uint32_t))
tcb_sz_padded = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t);
else
tcb_sz_padded = tcb_sz;
// Verifies all tasks in the snapshot
for (i = 0; i < task_num; i++) {
task_is_valid = esp_core_dump_process_tcb(frame, &tasks[i], tcb_sz);
// Check if task tcb is corrupted
if (!task_is_valid) {
write_cfg->bad_tasks_num++;
continue;
} else {
data_len += (tcb_sz_padded + sizeof(core_dump_task_header_t));
}
uint32_t len = 0;
task_is_valid = esp_core_dump_process_stack(&tasks[i], &len);
if (task_is_valid) {
// Increase core dump size by task stack size
data_len += len;
} else {
// If task tcb is ok but stack is corrupted
write_cfg->bad_tasks_num++;
}
}
// Add core dump header size
data_len += sizeof(core_dump_header_t);
ESP_COREDUMP_LOG_PROCESS("Core dump len = %lu (%d %d)", data_len, task_num, write_cfg->bad_tasks_num);
// Prepare write
if (write_cfg->prepare) {
err = write_cfg->prepare(write_cfg->priv, &data_len);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to prepare core dump (%d)!", err);
return err;
}
}
// Write start
if (write_cfg->start) {
err = write_cfg->start(write_cfg->priv);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to start core dump (%d)!", err);
return err;
}
}
// Write header
dump_data.hdr.data_len = data_len;
dump_data.hdr.version = COREDUMP_VERSION;
dump_data.hdr.tasks_num = task_num - write_cfg->bad_tasks_num;
dump_data.hdr.tcb_sz = tcb_sz;
err = write_cfg->write(write_cfg->priv, &dump_data, sizeof(core_dump_header_t));
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to write core dump header (%d)!", err);
return err;
}
// Write tasks
for (i = 0; i < task_num; i++) {
if (!esp_tcb_addr_is_sane((uint32_t)tasks[i].tcb_addr, tcb_sz)) {
ESP_COREDUMP_LOG_PROCESS("Skip TCB with bad addr %x!", tasks[i].tcb_addr);
continue;
}
ESP_COREDUMP_LOG_PROCESS("Dump task %x", tasks[i].tcb_addr);
// Save TCB address, stack base and stack top addr
dump_data.task_hdr.tcb_addr = tasks[i].tcb_addr;
dump_data.task_hdr.stack_start = tasks[i].stack_start;
dump_data.task_hdr.stack_end = tasks[i].stack_end;
err = write_cfg->write(write_cfg->priv, (void*)&dump_data, sizeof(core_dump_task_header_t));
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to write task header (%d)!", err);
return err;
}
// Save TCB
err = write_cfg->write(write_cfg->priv, tasks[i].tcb_addr, tcb_sz);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to write TCB (%d)!", err);
return err;
}
// Save task stack
if (tasks[i].stack_start != 0 && tasks[i].stack_end != 0) {
err = write_cfg->write(write_cfg->priv, (void*)tasks[i].stack_start,
tasks[i].stack_end - tasks[i].stack_start);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to write task stack (%d)!", err);
return err;
}
} else {
ESP_COREDUMP_LOG_PROCESS("Skip corrupted task %x stack!", tasks[i].tcb_addr);
}
}
// write end
if (write_cfg->end) {
err = write_cfg->end(write_cfg->priv);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to end core dump (%d)!", err);
return err;
}
}
if (write_cfg->bad_tasks_num) {
ESP_COREDUMP_LOGE("Skipped %d tasks with bad TCB!", write_cfg->bad_tasks_num);
}
return err;
}
inline void esp_core_dump_write(void *frame, core_dump_write_config_t *write_cfg)
{
esp_err_t err = esp_core_dump_write_binary(frame, write_cfg);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Core dump write binary failed with error: %d", err);
}
}
#endif
void esp_core_dump_init()
{
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
esp_core_dump_flash_init();
#endif
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART
ESP_COREDUMP_LOGI("Init core dump to UART");
#endif
}
esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size)
{
esp_err_t err;
const void *core_data;
spi_flash_mmap_handle_t core_data_handle;
if (out_addr == NULL || out_size == NULL) {
return ESP_ERR_INVALID_ARG;
}
const esp_partition_t *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_FAIL;
}
if (core_part->size < sizeof(uint32_t)) {
ESP_LOGE(TAG, "Too small core dump partition!");
return ESP_FAIL;
}
err = esp_partition_mmap(core_part, 0, sizeof(uint32_t),
SPI_FLASH_MMAP_DATA, &core_data, &core_data_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err);
return err;
}
uint32_t *dw = (uint32_t *)core_data;
*out_size = *dw;
spi_flash_munmap(core_data_handle);
// remap full core dump with CRC
err = esp_partition_mmap(core_part, 0, *out_size,
SPI_FLASH_MMAP_DATA, &core_data, &core_data_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err);
return err;
}
uint32_t *crc = (uint32_t *)(((uint8_t *)core_data) + *out_size);
crc--; // Point to CRC field
// Calc CRC over core dump data except for CRC field
core_dump_crc_t cur_crc = crc32_le(0, (uint8_t const *)core_data, *out_size - sizeof(core_dump_crc_t));
if (*crc != cur_crc) {
ESP_LOGE(TAG, "Core dump data CRC check failed: 0x%x -> 0x%x!", *crc, cur_crc);
spi_flash_munmap(core_data_handle);
return ESP_FAIL;
}
spi_flash_munmap(core_data_handle);
*out_addr = core_part->address;
return ESP_OK;
}

View File

@ -0,0 +1,232 @@
// 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 <string.h>
#include "rom/crc.h"
#include "esp_partition.h"
#include "esp_core_dump_priv.h"
const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_flash";
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
typedef struct _core_dump_write_flash_data_t
{
uint32_t off; // current offset in partition
core_dump_crc_t crc; // CRC of dumped data
} core_dump_write_flash_data_t;
typedef struct _core_dump_partition_t
{
// core dump partition start
uint32_t start;
// core dump partition size
uint32_t size;
} core_dump_partition_t;
typedef struct _core_dump_flash_config_t
{
// core dump partition config
core_dump_partition_t partition;
// CRC of core dump partition config
core_dump_crc_t partition_config_crc;
} core_dump_flash_config_t;
// core dump flash data
static core_dump_flash_config_t s_core_flash_config;
static inline core_dump_crc_t esp_core_dump_calc_flash_config_crc(void)
{
return crc32_le(0, (uint8_t const *)&s_core_flash_config.partition, sizeof(s_core_flash_config.partition));
}
void esp_core_dump_flash_init()
{
const esp_partition_t *core_part;
ESP_COREDUMP_LOGI("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_COREDUMP_LOGE("No core dump partition found!");
return;
}
ESP_COREDUMP_LOGI("Found partition '%s' @ %x %d bytes", core_part->label, core_part->address, core_part->size);
s_core_flash_config.partition.start = core_part->address;
s_core_flash_config.partition.size = core_part->size;
s_core_flash_config.partition_config_crc = esp_core_dump_calc_flash_config_crc();
}
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);
assert(off >= s_core_flash_config.partition.start);
assert((off + data_len + (data_size % sizeof(uint32_t) ? sizeof(uint32_t) : 0)) <=
s_core_flash_config.partition.start + s_core_flash_config.partition.size);
err = spi_flash_write(off, data, data_len);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to write data to flash (%d)!", err);
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(off + data_len, &rom_data, sizeof(uint32_t));
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to finish write data to flash (%d)!", err);
return 0;
}
data_len += sizeof(uint32_t);
}
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;
// check for available space in partition
if ((*data_len + sizeof(uint32_t)) > s_core_flash_config.partition.size) {
ESP_COREDUMP_LOGE("Not enough space to save core dump!");
return ESP_ERR_NO_MEM;
}
// add space for CRC
*data_len += sizeof(core_dump_crc_t);
memset(wr_data, 0, sizeof(*wr_data));
sec_num = *data_len / SPI_FLASH_SEC_SIZE;
if (*data_len % SPI_FLASH_SEC_SIZE) {
sec_num++;
}
assert(sec_num * SPI_FLASH_SEC_SIZE <= s_core_flash_config.partition.size);
err = spi_flash_erase_range(s_core_flash_config.partition.start + 0, sec_num * SPI_FLASH_SEC_SIZE);
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to erase flash (%d)!", err);
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;
assert(wr_data->off + sizeof(uint32_t) <= s_core_flash_config.partition.size);
err = spi_flash_write(s_core_flash_config.partition.start + wr_data->off, &data32, sizeof(uint32_t));
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to write to flash (%d)!", err);
return err;
}
wr_data->off += sizeof(uint32_t);
return err;
}
static esp_err_t esp_core_dump_flash_write_start(void *priv)
{
return ESP_OK;
}
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
union
{
uint8_t data8[16];
uint32_t data32[4];
} rom_data;
esp_err_t err = spi_flash_read(s_core_flash_config.partition.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 (uint32_t i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) {
ESP_COREDUMP_LOG_PROCESS("%x", rom_data.data32[i]);
}
}
#endif
// write core dump CRC
ESP_COREDUMP_LOG_PROCESS("Dump data CRC = 0x%x", wr_data->crc);
return esp_core_dump_flash_write_word(wr_data, wr_data->crc);
}
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_flash_config.partition.start + wr_data->off, data, data_len);
if (len != data_len) {
return ESP_FAIL;
}
wr_data->off += len;
wr_data->crc = crc32_le(wr_data->crc, data, data_len);
return err;
}
void esp_core_dump_to_flash(XtExcFrame *frame)
{
core_dump_write_config_t wr_cfg;
core_dump_write_flash_data_t wr_data;
core_dump_crc_t crc = esp_core_dump_calc_flash_config_crc();
if (s_core_flash_config.partition_config_crc != crc) {
ESP_COREDUMP_LOGE("Core dump flash config is corrupted! CRC=0x%x instead of 0x%x", crc, s_core_flash_config.partition_config_crc);
return;
}
// check that partition can hold at least core dump data length
if (s_core_flash_config.partition.start == 0 || s_core_flash_config.partition.size < sizeof(uint32_t)) {
ESP_COREDUMP_LOGE("Invalid flash partition config!");
return;
}
/* init non-OS flash access critical section */
spi_flash_guard_set(&g_flash_guard_no_os_ops);
memset(&wr_cfg, 0, sizeof(wr_cfg));
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_COREDUMP_LOGI("Save core dump to flash...");
esp_core_dump_write((void*)frame, &wr_cfg);
ESP_COREDUMP_LOGI("Core dump has been saved to flash.");
}
#endif

View File

@ -0,0 +1,103 @@
// 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 <string.h>
#include <stdbool.h>
#include "esp_panic.h"
#include "esp_core_dump_priv.h"
const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_port";
#if CONFIG_ESP32_ENABLE_COREDUMP
inline bool esp_task_stack_start_is_sane(uint32_t sp)
{
return !(sp < 0x3ffae010UL || sp > 0x3fffffffUL);
}
inline bool esp_tcb_addr_is_sane(uint32_t addr, uint32_t sz)
{
//TODO: currently core dump supports TCBs in DRAM only, external SRAM not supported yet
return !(addr < 0x3ffae000UL || (addr + sz) > 0x40000000UL);
}
uint32_t esp_core_dump_get_tasks_snapshot(core_dump_task_header_t* const tasks,
const uint32_t snapshot_size, uint32_t* const tcb_sz)
{
uint32_t task_num = (uint32_t)uxTaskGetSnapshotAll((TaskSnapshot_t*)tasks, (UBaseType_t)snapshot_size, (UBaseType_t*)tcb_sz);
return task_num;
}
bool esp_core_dump_process_tcb(void *frame, core_dump_task_header_t *task_snaphort, uint32_t tcb_sz)
{
XtExcFrame *exc_frame = (XtExcFrame*)frame;
if (!esp_tcb_addr_is_sane((uint32_t)task_snaphort->tcb_addr, tcb_sz)) {
ESP_COREDUMP_LOG_PROCESS("Bad TCB addr %x!", task_snaphort->tcb_addr);
return false;
}
if (task_snaphort->tcb_addr == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) {
// Set correct stack top for current task
task_snaphort->stack_start = (uint32_t)exc_frame;
// This field is not initialized for crashed task, but stack frame has the structure of interrupt one,
// so make workaround to allow espcoredump to parse it properly.
if (exc_frame->exit == 0)
exc_frame->exit = -1;
ESP_COREDUMP_LOG_PROCESS("Current task %x EXIT/PC/PS/A0/SP %x %x %x %x %x",
task_snaphort->tcb_addr, exc_frame->exit, exc_frame->pc, exc_frame->ps, exc_frame->a0, exc_frame->a1);
}
else {
XtSolFrame *task_frame = (XtSolFrame *)task_snaphort->stack_start;
if (task_frame->exit == 0) {
ESP_COREDUMP_LOG_PROCESS("Task %x EXIT/PC/PS/A0/SP %x %x %x %x %x",
task_snaphort->tcb_addr, 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 *)task_snaphort->stack_start;
ESP_COREDUMP_LOG_PROCESS("Task %x EXIT/PC/PS/A0/SP %x %x %x %x %x",
task_snaphort->tcb_addr, task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1);
#endif
}
}
return true;
}
bool esp_core_dump_process_stack(core_dump_task_header_t* task_snaphort, uint32_t *length)
{
uint32_t len = 0;
bool task_is_valid = false;
len = (uint32_t)task_snaphort->stack_end - (uint32_t)task_snaphort->stack_start;
// Check task's stack
if (!esp_stack_ptr_is_sane(task_snaphort->stack_start) ||
!esp_task_stack_start_is_sane((uint32_t)task_snaphort->stack_end) ||
(len > COREDUMP_MAX_TASK_STACK_SIZE)) {
// Check if current task stack corrupted
if (task_snaphort->tcb_addr == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) {
ESP_COREDUMP_LOG_PROCESS("Crashed task will be skipped!");
}
ESP_COREDUMP_LOG_PROCESS("Corrupted TCB %x: stack len %lu, top %x, end %x!",
task_snaphort->tcb_addr, len, task_snaphort->stack_start, task_snaphort->stack_end);
task_snaphort->tcb_addr = 0; // make TCB addr invalid to skip it in dump
task_is_valid = false;
} else {
ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", len,
task_snaphort->stack_start, task_snaphort->stack_end);
// Take stack padding into account
*length = (len + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);
task_is_valid = true;
}
return task_is_valid;
}
#endif

View File

@ -0,0 +1,129 @@
// 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 <string.h>
#include "soc/uart_reg.h"
#include "soc/io_mux_reg.h"
#include "driver/gpio.h"
#include "esp_clk.h"
#include "esp_core_dump_priv.h"
const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_uart";
#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) {
const static DRAM_ATTR 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_start(void *priv)
{
esp_err_t err = ESP_OK;
ets_printf(DRAM_STR("================= CORE DUMP START =================\r\n"));
return err;
}
static esp_err_t esp_core_dump_uart_write_end(void *priv)
{
esp_err_t err = ESP_OK;
ets_printf(DRAM_STR("================= 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)
{
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;
/* 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;
ets_printf(DRAM_STR("%s\r\n"), buf);
}
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;
memset(&wr_cfg, 0, sizeof(wr_cfg));
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;
//Make sure txd/rxd are enabled
// use direct reg access instead of gpio_pullup_dis which can cause exception when flash cache is disabled
REG_CLR_BIT(GPIO_PIN_REG_1, FUN_PU);
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...");
const int cpu_ticks_per_ms = esp_clk_cpu_freq() / 1000;
tm_end = xthal_get_ccount() / cpu_ticks_per_ms + CONFIG_ESP32_CORE_DUMP_UART_DELAY;
ch = esp_core_dump_uart_get_char();
while (!(ch == '\n' || ch == '\r')) {
tm_cur = xthal_get_ccount() / cpu_ticks_per_ms;
if (tm_cur >= tm_end){
break;
}
ch = esp_core_dump_uart_get_char();
}
ESP_COREDUMP_LOGI("Print core dump to uart...");
esp_core_dump_write((void*)frame, &wr_cfg);
ESP_COREDUMP_LOGI("Core dump has been written to uart.");
}
#endif

View File

@ -1,6 +1,8 @@
set(COMPONENT_SRCDIRS ".")
set(COMPONENT_ADD_INCLUDEDIRS ".")
set(COMPONENT_REQUIRES unity)
register_component()
if(TESTS_ALL EQUAL 1)
message("not linking coredump test from CI.")
else()
set(COMPONENT_SRCDIRS ".")
set(COMPONENT_ADD_INCLUDEDIRS ".")
set(COMPONENT_REQUIRES unity nvs_flash)
register_component()
endif()

View File

@ -0,0 +1,13 @@
ifeq ($(TESTS_ALL),1)
$(info not linking coredump tests from CI)
else
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
COMPONENT_SRCDIRS := .
COMPONENT_ADD_INCLUDEDIRS := .
COMPONENT_PRIV_INCLUDEDIRS := .
COMPONENT_REQUIRES := unity nvs_flash
endif # TESTS_ALL

View File

@ -11,7 +11,7 @@
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "unity.h"
// task crash indicators
#define TCI_NULL_PTR 0x1
@ -96,7 +96,7 @@ void failed_assert_task(void *pvParameter)
fflush(stdout);
}
void app_main()
TEST_CASE("verify coredump functionality", "[coredump][ignore]")
{
nvs_flash_init();
xTaskCreate(&bad_ptr_task, "bad_ptr_task", 2048, NULL, 5, NULL);