mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(coredump): save .bss, .data and .heap sections to the elf file
This commit is contained in:
parent
9a24782dd2
commit
3b8191cf5d
@ -47,6 +47,18 @@ menu "Core dump"
|
||||
depends on ESP_COREDUMP_DATA_FORMAT_ELF
|
||||
endchoice
|
||||
|
||||
config ESP_COREDUMP_CAPTURE_DRAM
|
||||
bool "Include whole .bss and .data sections and heap data into core dump file"
|
||||
default n
|
||||
#TODO: Heap walker api is not ready for the esp32c5 (IDF-9641)
|
||||
depends on ESP_COREDUMP_DATA_FORMAT_ELF && !IDF_TARGET_ESP32C5
|
||||
help
|
||||
Storing these sections can help with easier debugging and troubleshooting.
|
||||
However, additional storage space will be required in the core dump partition.
|
||||
At least 128KB should be reserved, but the actual amount required may vary based
|
||||
on the application's DRAM usage.
|
||||
Note that sections located in external RAM will not be stored.
|
||||
|
||||
config ESP_COREDUMP_CHECK_BOOT
|
||||
bool "Check core dump data integrity on boot"
|
||||
default y
|
||||
@ -112,7 +124,8 @@ menu "Core dump"
|
||||
help
|
||||
Size of the memory to be reserved for core dump stack. If 0 core dump process will run on
|
||||
the stack of crashed task/ISR, otherwise special stack will be allocated.
|
||||
To ensure that core dump itself will not overflow task/ISR stack set this to the value above 800.
|
||||
To ensure that core dump itself will not overflow task/ISR stack set this to the value around 1300-1800
|
||||
depending on the chosen checksum calculation method. SHA256 method needs more stack space than CRC32.
|
||||
NOTE: It eats DRAM.
|
||||
|
||||
config ESP_COREDUMP_SUMMARY_STACKDUMP_SIZE
|
||||
|
@ -20,15 +20,25 @@ extern "C" {
|
||||
* One can use these definitions to retrieve the start address and/or the size
|
||||
* of a specific region using the functions below.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
COREDUMP_MEMORY_DRAM,
|
||||
COREDUMP_MEMORY_IRAM,
|
||||
#if CONFIG_ESP_COREDUMP_CAPTURE_DRAM
|
||||
COREDUMP_MEMORY_DRAM_BSS,
|
||||
COREDUMP_MEMORY_DRAM_DATA,
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
COREDUMP_MEMORY_DRAM_BSS_HIGH,
|
||||
COREDUMP_MEMORY_DRAM_DATA_HIGH,
|
||||
#endif
|
||||
#else
|
||||
COREDUMP_MEMORY_DRAM,
|
||||
#endif
|
||||
#if SOC_RTC_MEM_SUPPORTED
|
||||
COREDUMP_MEMORY_RTC,
|
||||
COREDUMP_MEMORY_RTC_FAST,
|
||||
#endif
|
||||
COREDUMP_MEMORY_MAX,
|
||||
COREDUMP_MEMORY_START = COREDUMP_MEMORY_DRAM
|
||||
COREDUMP_MEMORY_START = COREDUMP_MEMORY_IRAM
|
||||
} coredump_region_t;
|
||||
|
||||
/**
|
||||
@ -126,6 +136,12 @@ esp_err_t esp_core_dump_write_data(core_dump_write_data_t *wr_data, void *data,
|
||||
*/
|
||||
esp_err_t esp_core_dump_write_end(core_dump_write_data_t *wr_data);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the stack information which will be used from the coredump module itself.
|
||||
* It will show the whole stack boundaries in case the stack is shared with the crashed task.
|
||||
*/
|
||||
void esp_core_dump_get_own_stack_info(uint32_t *addr, uint32_t *size);
|
||||
|
||||
/**
|
||||
* @brief Stores the core dump in either binary or ELF format.
|
||||
*/
|
||||
@ -157,7 +173,7 @@ static inline core_dump_task_handle_t esp_core_dump_get_current_task_handle(void
|
||||
* @brief Get the length, in bytes, of a given memory location. Padding is
|
||||
* taken into account in this calculation.
|
||||
*
|
||||
* @param start Start address of the momery location.
|
||||
* @param start Start address of the memory location.
|
||||
* @param end End address of the memory location.
|
||||
*
|
||||
* @return Size of the memory location, multiple of sizeof(uint32_t).
|
||||
|
@ -25,6 +25,25 @@ const static char TAG[] __attribute__((unused)) = "esp_core_dump_common";
|
||||
/**
|
||||
* @brief Memory regions to dump, defined at compile time.
|
||||
*/
|
||||
#if CONFIG_ESP_COREDUMP_CAPTURE_DRAM
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
extern int _bss_start;
|
||||
extern int _bss_end;
|
||||
extern int _data_start;
|
||||
extern int _data_end;
|
||||
#else
|
||||
extern int _bss_start_low;
|
||||
extern int _bss_end_low;
|
||||
extern int _data_start_low;
|
||||
extern int _data_end_low;
|
||||
extern int _bss_start_high;
|
||||
extern int _bss_end_high;
|
||||
extern int _data_start_high;
|
||||
extern int _data_end_high;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Regions for the user defined variable locations */
|
||||
extern int _coredump_dram_start;
|
||||
extern int _coredump_dram_end;
|
||||
extern int _coredump_iram_start;
|
||||
@ -160,6 +179,7 @@ FORCE_INLINE_ATTR void esp_core_dump_setup_stack(void)
|
||||
FORCE_INLINE_ATTR void esp_core_dump_report_stack_usage(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif // CONFIG_ESP_COREDUMP_STACK_SIZE > 0
|
||||
|
||||
static void* s_exc_frame = NULL;
|
||||
@ -254,19 +274,29 @@ uint32_t esp_core_dump_get_user_ram_segments(void)
|
||||
return total_sz;
|
||||
}
|
||||
|
||||
uint32_t esp_core_dump_get_user_ram_size(void)
|
||||
{
|
||||
uint32_t total_sz = 0;
|
||||
|
||||
total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_dram_end, &_coredump_dram_start);
|
||||
#if SOC_RTC_MEM_SUPPORTED
|
||||
total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_end, &_coredump_rtc_start);
|
||||
total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_fast_end, &_coredump_rtc_fast_start);
|
||||
static const struct {
|
||||
int *start;
|
||||
int *end;
|
||||
} s_memory_sections[COREDUMP_MEMORY_MAX] = {
|
||||
[COREDUMP_MEMORY_IRAM] = { &_coredump_iram_start, &_coredump_iram_end },
|
||||
#if CONFIG_ESP_COREDUMP_CAPTURE_DRAM
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
[COREDUMP_MEMORY_DRAM_BSS] = { &_bss_start, &_bss_end },
|
||||
[COREDUMP_MEMORY_DRAM_DATA] = { &_data_start, &_data_end },
|
||||
#else
|
||||
[COREDUMP_MEMORY_DRAM_BSS] = { &_bss_start_low, &_bss_end_low },
|
||||
[COREDUMP_MEMORY_DRAM_DATA] = { &_data_start_low, &_data_end_low },
|
||||
[COREDUMP_MEMORY_DRAM_BSS_HIGH] = { &_bss_start_high, &_bss_end_high },
|
||||
[COREDUMP_MEMORY_DRAM_DATA_HIGH] = { &_data_start_high, &_data_end_high },
|
||||
#endif
|
||||
total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_iram_end, &_coredump_iram_start);
|
||||
|
||||
return total_sz;
|
||||
}
|
||||
#else
|
||||
[COREDUMP_MEMORY_DRAM] = { &_coredump_dram_start, &_coredump_dram_end },
|
||||
#endif
|
||||
#if SOC_RTC_MEM_SUPPORTED
|
||||
[COREDUMP_MEMORY_RTC] = { &_coredump_rtc_start, &_coredump_rtc_end },
|
||||
[COREDUMP_MEMORY_RTC_FAST] = { &_coredump_rtc_fast_start, &_coredump_rtc_fast_end },
|
||||
#endif
|
||||
};
|
||||
|
||||
int esp_core_dump_get_user_ram_info(coredump_region_t region, uint32_t *start)
|
||||
{
|
||||
@ -274,36 +304,34 @@ int esp_core_dump_get_user_ram_info(coredump_region_t region, uint32_t *start)
|
||||
|
||||
ESP_COREDUMP_DEBUG_ASSERT(start != NULL);
|
||||
|
||||
switch (region) {
|
||||
case COREDUMP_MEMORY_DRAM:
|
||||
*start = (uint32_t)&_coredump_dram_start;
|
||||
total_sz = (uint8_t *)&_coredump_dram_end - (uint8_t *)&_coredump_dram_start;
|
||||
break;
|
||||
|
||||
case COREDUMP_MEMORY_IRAM:
|
||||
*start = (uint32_t)&_coredump_iram_start;
|
||||
total_sz = (uint8_t *)&_coredump_iram_end - (uint8_t *)&_coredump_iram_start;
|
||||
break;
|
||||
|
||||
#if SOC_RTC_MEM_SUPPORTED
|
||||
case COREDUMP_MEMORY_RTC:
|
||||
*start = (uint32_t)&_coredump_rtc_start;
|
||||
total_sz = (uint8_t *)&_coredump_rtc_end - (uint8_t *)&_coredump_rtc_start;
|
||||
break;
|
||||
|
||||
case COREDUMP_MEMORY_RTC_FAST:
|
||||
*start = (uint32_t)&_coredump_rtc_fast_start;
|
||||
total_sz = (uint8_t *)&_coredump_rtc_fast_end - (uint8_t *)&_coredump_rtc_fast_start;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
if (region >= COREDUMP_MEMORY_START && region < COREDUMP_MEMORY_MAX) {
|
||||
total_sz = (uint8_t *)s_memory_sections[region].end - (uint8_t *)s_memory_sections[region].start;
|
||||
*start = (uint32_t)s_memory_sections[region].start;
|
||||
}
|
||||
|
||||
return total_sz;
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_COREDUMP_CAPTURE_DRAM
|
||||
void esp_core_dump_get_own_stack_info(uint32_t *addr, uint32_t *size)
|
||||
{
|
||||
#if CONFIG_ESP_COREDUMP_STACK_SIZE > 0
|
||||
/* Custom stack reserved for the coredump */
|
||||
*addr = (uint32_t)s_coredump_stack;
|
||||
*size = sizeof(s_coredump_stack);
|
||||
#else
|
||||
/* Shared stack with the crashed task */
|
||||
core_dump_task_handle_t handle = esp_core_dump_get_current_task_handle();
|
||||
TaskSnapshot_t rtos_snapshot = { 0 };
|
||||
vTaskGetSnapshot(handle, &rtos_snapshot);
|
||||
StaticTask_t *current = (StaticTask_t *)handle;
|
||||
*addr = (uint32_t)current->pxDummy6; //pxStack
|
||||
*size = (uint32_t)rtos_snapshot.pxTopOfStack - (uint32_t)current->pxDummy6; /* free */
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ESP_COREDUMP_CAPTURE_DRAM */
|
||||
|
||||
inline bool esp_core_dump_tcb_addr_is_sane(uint32_t addr)
|
||||
{
|
||||
return esp_core_dump_mem_seg_is_sane(addr, esp_core_dump_get_tcb_len());
|
||||
|
@ -80,6 +80,11 @@ typedef struct _core_dump_elf_t {
|
||||
uint16_t segs_count;
|
||||
core_dump_write_data_t write_data;
|
||||
uint32_t note_data_size; /* can be used where static storage needed */
|
||||
#if CONFIG_ESP_COREDUMP_CAPTURE_DRAM
|
||||
/* To avoid checksum failure, coredump stack region will be excluded while storing the sections. */
|
||||
uint32_t coredump_stack_start;
|
||||
uint32_t coredump_stack_size;
|
||||
#endif
|
||||
} core_dump_elf_t;
|
||||
|
||||
typedef struct {
|
||||
@ -474,10 +479,17 @@ static int elf_write_tasks_data(core_dump_elf_t *self)
|
||||
bad_tasks_num++;
|
||||
continue;
|
||||
}
|
||||
ret = elf_save_task(self, &task_hdr);
|
||||
ELF_CHECK_ERR((ret > 0), ret,
|
||||
"Task %x, TCB write failed, return (%d).", task_iter.pxTaskHandle, ret);
|
||||
elf_len += ret;
|
||||
|
||||
#if CONFIG_ESP_COREDUMP_CAPTURE_DRAM
|
||||
/* Only crashed task data will be saved here. The other task's data will be automatically saved within the sections */
|
||||
if (esp_core_dump_get_current_task_handle() != task_iter.pxTaskHandle)
|
||||
#endif
|
||||
{
|
||||
ret = elf_save_task(self, &task_hdr);
|
||||
ELF_CHECK_ERR((ret > 0), ret,
|
||||
"Task %x, TCB write failed, return (%d).", task_iter.pxTaskHandle, ret);
|
||||
elf_len += ret;
|
||||
}
|
||||
if (interrupted_stack.size > 0) {
|
||||
ESP_COREDUMP_LOG_PROCESS("Add interrupted task stack %lu bytes @ %x",
|
||||
interrupted_stack.size, interrupted_stack.start);
|
||||
@ -493,28 +505,148 @@ static int elf_write_tasks_data(core_dump_elf_t *self)
|
||||
return elf_len;
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_COREDUMP_CAPTURE_DRAM
|
||||
|
||||
/* Coredump stack will also be used by the checksum functions while saving sections.
|
||||
* There is a potential for inconsistency when writing coredump stack to the flash and calculating checksum simultaneously.
|
||||
* This is because, coredump stack will be modified during the process, leading to incorrect checksum calculations.
|
||||
* To mitigate this issue, it's important to ensure that the coredump stack excluded from checksum calculation by
|
||||
* filter out from the written regions.
|
||||
* Typically, the coredump stack can be located in two different sections.
|
||||
* 1. In the bss section;
|
||||
* 1.a if `CONFIG_ESP_COREDUMP_STACK_SIZE` set to a nonzero value
|
||||
* 1.b if the crashed task is created with a static task buffer using the xTaskCreateStatic() api
|
||||
* 2. In the heap section, if custom stack is not defined and the crashed task buffer is allocated in the heap
|
||||
* with the xTaskCreate() api
|
||||
*
|
||||
* esp_core_dump_store_section() will check if the coredump stack is located inside the section.
|
||||
* If it is, this part will be skipped.
|
||||
* |+++++++++| xxxxxxxxxxxxxx |++++++++|
|
||||
* |+++++++++| coredump stack |++++++++|
|
||||
*/
|
||||
static int esp_core_dump_store_section(core_dump_elf_t *self, uint32_t start, uint32_t data_len)
|
||||
{
|
||||
uint32_t end = start + data_len;
|
||||
int total_sz = 0;
|
||||
int ret;
|
||||
|
||||
if (self->coredump_stack_start > start && self->coredump_stack_start < end) {
|
||||
/* write until the coredump stack. */
|
||||
data_len = self->coredump_stack_start - start;
|
||||
ret = elf_add_segment(self, PT_LOAD,
|
||||
start,
|
||||
(void*)start,
|
||||
data_len);
|
||||
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
}
|
||||
total_sz += ret;
|
||||
|
||||
/* Skip coredump stack and set offset for the rest of the section */
|
||||
start = self->coredump_stack_start + self->coredump_stack_size;
|
||||
data_len = end - start;
|
||||
}
|
||||
|
||||
if (data_len > 0) {
|
||||
ret = elf_add_segment(self, PT_LOAD,
|
||||
(uint32_t)start,
|
||||
(void*)start,
|
||||
(uint32_t)data_len);
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
}
|
||||
total_sz += ret;
|
||||
}
|
||||
|
||||
return total_sz;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
core_dump_elf_t *self;
|
||||
int *total_sz;
|
||||
int ret;
|
||||
} heap_block_data_t;
|
||||
|
||||
bool esp_core_dump_write_heap_blocks(walker_heap_into_t heap_info, walker_block_info_t block_info, void* user_data)
|
||||
{
|
||||
heap_block_data_t *param = user_data;
|
||||
int *total_sz = param->total_sz;
|
||||
core_dump_elf_t *self = param->self;
|
||||
int *ret = ¶m->ret;
|
||||
|
||||
if (*ret <= 0) {
|
||||
/* There was a flash write failure at the previous write attempt */
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((intptr_t)heap_info.end - (intptr_t)block_info.ptr < block_info.size) {
|
||||
ESP_COREDUMP_LOGE("Block corruption detected in the heap (%p-%p)", heap_info.start, heap_info.end);
|
||||
ESP_COREDUMP_LOGE("Corrupted block addr:%p size:%x)", block_info.ptr, block_info.size);
|
||||
/* Heap walker will skip the next block in the same heap region and it will continue from the next heap region's block. */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (block_info.used && block_info.size > 0) {
|
||||
ESP_COREDUMP_LOG_PROCESS("heap block @%p sz:(%x)", (void *)block_info.ptr, block_info.size);
|
||||
|
||||
if (!esp_core_dump_mem_seg_is_sane((uint32_t)block_info.ptr, block_info.size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (self->coredump_stack_start == (uint32_t)block_info.ptr) {
|
||||
/* skip writing coredump stack block */
|
||||
return true;
|
||||
}
|
||||
|
||||
*ret = elf_add_segment(self, PT_LOAD,
|
||||
(uint32_t)block_info.ptr,
|
||||
(void*)block_info.ptr,
|
||||
block_info.size);
|
||||
if (*ret <= 0) {
|
||||
return false;
|
||||
}
|
||||
*total_sz += *ret;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int esp_core_dump_store_section(core_dump_elf_t *self, uint32_t start, uint32_t data_len)
|
||||
{
|
||||
return elf_add_segment(self, PT_LOAD,
|
||||
start,
|
||||
(void*)start,
|
||||
data_len);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int elf_write_core_dump_user_data(core_dump_elf_t *self)
|
||||
{
|
||||
int data_len = 0;
|
||||
int total_sz = 0;
|
||||
uint32_t start = 0;
|
||||
|
||||
for (coredump_region_t i = COREDUMP_MEMORY_START; i < COREDUMP_MEMORY_MAX; i++) {
|
||||
data_len = esp_core_dump_get_user_ram_info(i, &start);
|
||||
int data_len = esp_core_dump_get_user_ram_info(i, &start);
|
||||
|
||||
ELF_CHECK_ERR((data_len >= 0), ELF_PROC_ERR_OTHER, "invalid memory region");
|
||||
|
||||
if (data_len > 0) {
|
||||
int ret = elf_add_segment(self, PT_LOAD,
|
||||
(uint32_t)start,
|
||||
(void*)start,
|
||||
(uint32_t) data_len);
|
||||
|
||||
int ret = esp_core_dump_store_section(self, start, data_len);
|
||||
ELF_CHECK_ERR((ret > 0), ret, "memory region write failed. Returned (%d).", ret);
|
||||
total_sz += ret;
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_COREDUMP_CAPTURE_DRAM
|
||||
heap_block_data_t user_data = {.self = self, .total_sz = &total_sz, .ret = 1};
|
||||
heap_caps_walk(MALLOC_CAP_8BIT, esp_core_dump_write_heap_blocks, &user_data);
|
||||
ELF_CHECK_ERR((user_data.ret > 0), user_data.ret, "Heap memory write failed. Returned (%d).", user_data.ret);
|
||||
#endif
|
||||
|
||||
return total_sz;
|
||||
}
|
||||
|
||||
@ -676,6 +808,12 @@ static esp_err_t esp_core_dump_write_elf(void)
|
||||
int tot_len = sizeof(dump_hdr);
|
||||
int write_len = sizeof(dump_hdr);
|
||||
|
||||
#if CONFIG_ESP_COREDUMP_CAPTURE_DRAM
|
||||
esp_core_dump_get_own_stack_info(&self.coredump_stack_start, &self.coredump_stack_size);
|
||||
ESP_COREDUMP_LOG_PROCESS("Core dump stack start=%p size = %d",
|
||||
(void *)self.coredump_stack_start, self.coredump_stack_size);
|
||||
#endif
|
||||
|
||||
esp_err_t err = esp_core_dump_write_init();
|
||||
if (err != ESP_OK) {
|
||||
ESP_COREDUMP_LOGE("Elf write init failed!");
|
||||
@ -794,7 +932,7 @@ static esp_err_t elf_core_dump_image_mmap(esp_partition_mmap_handle_t* core_data
|
||||
ESP_COREDUMP_LOGE("Failed to read core dump data size");
|
||||
return ret;
|
||||
}
|
||||
/* map the full core dump parition, including the checksum. */
|
||||
/* map the full core dump partition, including the checksum. */
|
||||
return esp_partition_mmap(core_part, 0, out_size, ESP_PARTITION_MMAP_DATA,
|
||||
map_addr, core_data_handle);
|
||||
}
|
||||
|
@ -56,9 +56,26 @@ Setting this option to 0 bytes will cause the core dump routines to run from the
|
||||
|
||||
.. note::
|
||||
|
||||
If a separate stack is used, the recommended stack size should be larger than 800 bytes to ensure that the core dump routines themselves do not cause a stack overflow.
|
||||
If a separate stack is used, the recommended stack size should be larger than 1300 bytes to ensure that the core dump routines themselves do not cause a stack overflow.
|
||||
|
||||
|
||||
Core Dump Memory Regions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
By default, core dumps typically save CPU registers, tasks data and summary of the panic reason. When the :ref:`CONFIG_ESP_COREDUMP_CAPTURE_DRAM` option is selected, ``.bss`` and ``.data`` sections and ``heap`` data will also be part of the dump.
|
||||
|
||||
For a better debugging experience, it is recommended to dump these sections. However, this will result in a larger coredump file. The required additional storage space may vary based on the amount of DRAM the application uses.
|
||||
|
||||
.. note::
|
||||
|
||||
.. only:: SOC_SPIRAM_SUPPORTED
|
||||
|
||||
Apart from the crashed task's TCB and stack, data located in the external RAM will not be stored in the core dump file, this include variables defined with ``EXT_RAM_BSS_ATTR`` or ``EXT_RAM_NOINIT_ATTR`` attributes, as well as any data stored in the ``extram_bss`` section.
|
||||
|
||||
.. note::
|
||||
|
||||
This feature is only enabled when using the ELF file format.
|
||||
|
||||
Core Dump to Flash
|
||||
------------------
|
||||
|
||||
|
@ -56,7 +56,25 @@ ELF 格式具备扩展特性,支持在发生崩溃时保存更多关于错误
|
||||
|
||||
.. note::
|
||||
|
||||
如果使用了独立的栈,建议栈大小应大于 800 字节,确保核心转储例程本身不会导致栈溢出。
|
||||
如果使用了独立的栈,建议栈大小应大于 1300 字节,确保核心转储例程本身不会导致栈溢出。
|
||||
|
||||
|
||||
Core Dump Memory Regions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
By default, core dumps typically save CPU registers, tasks data and summary of the panic reason. When the :ref:`CONFIG_ESP_COREDUMP_CAPTURE_DRAM` option is selected, ``.bss`` and ``.data`` sections and ``heap`` data will also be part of the dump.
|
||||
|
||||
For a better debugging experience, it is recommended to dump these sections. However, this will result in a larger coredump file. The required additional storage space may vary based on the amount of DRAM the application uses.
|
||||
|
||||
.. note::
|
||||
|
||||
.. only:: SOC_SPIRAM_SUPPORTED
|
||||
|
||||
Apart from the crashed task's TCB and stack, data located in the external RAM will not be stored in the core dump file, this include variables defined with ``EXT_RAM_BSS_ATTR`` or ``EXT_RAM_NOINIT_ATTR`` attributes, as well as any data stored in the ``extram_bss`` section.
|
||||
|
||||
.. note::
|
||||
|
||||
This feature is only enabled when using the ELF file format.
|
||||
|
||||
|
||||
将核心转储保存到 flash
|
||||
|
@ -21,7 +21,7 @@ void foo(void)
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("tested app is runnig.\n");
|
||||
printf("tested app is running.\n");
|
||||
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import os.path as path
|
||||
import sys
|
||||
|
||||
@ -23,9 +22,9 @@ def get_line_number(lookup: str, offset: int = 0) -> int:
|
||||
@pytest.mark.supported_targets
|
||||
@pytest.mark.generic
|
||||
def test_gdbstub_runtime(dut: PanicTestDut) -> None:
|
||||
dut.expect_exact('tested app is runnig.')
|
||||
dut.expect_exact('tested app is running.')
|
||||
dut.write(b'\x03') # send Ctrl-C
|
||||
dut.start_gdb()
|
||||
dut.start_gdb_for_gdbstub()
|
||||
|
||||
# Test breakpoint
|
||||
cmd = '-break-insert --source test_app_main.c --function app_main --label label_1'
|
||||
@ -162,9 +161,9 @@ def test_gdbstub_runtime(dut: PanicTestDut) -> None:
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32', 'esp32s2', 'esp32s3'], reason='fix IDF-7927')
|
||||
def test_gdbstub_runtime_xtensa_stepping_bug(dut: PanicTestDut) -> None:
|
||||
dut.expect_exact('tested app is runnig.')
|
||||
dut.expect_exact('tested app is running.')
|
||||
dut.write(b'\x03') # send Ctrl-C
|
||||
dut.start_gdb()
|
||||
dut.start_gdb_for_gdbstub()
|
||||
|
||||
# Test breakpoint
|
||||
cmd = '-break-insert --source test_app_main.c --function app_main --label label_1'
|
||||
|
@ -61,6 +61,8 @@ void test_assert_cache_disabled(void);
|
||||
|
||||
void test_illegal_access(void);
|
||||
|
||||
void test_capture_dram(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -98,6 +98,9 @@ void app_main(void)
|
||||
#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
|
||||
HANDLE_TEST(test_name, test_panic_extram_stack);
|
||||
#endif
|
||||
#if CONFIG_ESP_COREDUMP_CAPTURE_DRAM
|
||||
HANDLE_TEST(test_name, test_capture_dram);
|
||||
#endif
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
HANDLE_TEST(test_name, test_task_wdt_cpu1);
|
||||
#endif
|
||||
|
@ -252,3 +252,30 @@ void test_illegal_access(void)
|
||||
printf("[2] val: %d at %p\n", val, (void *)addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_ESP_COREDUMP_CAPTURE_DRAM
|
||||
int g_data_var = 42;
|
||||
int g_bss_var;
|
||||
char *g_heap_ptr;
|
||||
COREDUMP_IRAM_DATA_ATTR uint32_t g_cd_iram = 0x4242;
|
||||
COREDUMP_DRAM_ATTR uint32_t g_cd_dram = 0x4343;
|
||||
#if SOC_RTC_MEM_SUPPORTED
|
||||
COREDUMP_RTC_FAST_ATTR uint32_t g_rtc_fast_var;
|
||||
COREDUMP_RTC_DATA_ATTR uint32_t g_rtc_data_var = 0x55A9;
|
||||
#endif
|
||||
|
||||
void test_capture_dram(void)
|
||||
{
|
||||
g_data_var++;
|
||||
g_bss_var = 55;
|
||||
g_heap_ptr = strdup("Coredump Test");
|
||||
assert(g_heap_ptr);
|
||||
g_cd_iram++;
|
||||
g_cd_dram++;
|
||||
#if SOC_RTC_MEM_SUPPORTED
|
||||
g_rtc_fast_var = 0xAABBCCDD;
|
||||
g_rtc_data_var++;
|
||||
#endif
|
||||
assert(0);
|
||||
}
|
||||
#endif
|
||||
|
6
tools/test_apps/system/panic/partitions_capture_dram.csv
Normal file
6
tools/test_apps/system/panic/partitions_capture_dram.csv
Normal file
@ -0,0 +1,6 @@
|
||||
# ESP-IDF Partition Table
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs,data,nvs,0x9000,24K,
|
||||
phy_init,data,phy,0xf000,4K,
|
||||
factory,app,factory,0x10000,1M,
|
||||
coredump,data,coredump,0x110000,128K,
|
|
@ -89,6 +89,8 @@ CONFIGS_HW_STACK_GUARD_DUAL_CORE = [
|
||||
pytest.param('panic', marks=TARGETS_RISCV_DUAL_CORE),
|
||||
]
|
||||
|
||||
CONFIG_CAPTURE_DRAM = [pytest.param('coredump_flash_capture_dram', marks=TARGETS_ALL)]
|
||||
|
||||
# Panic abort information will start with this string.
|
||||
PANIC_ABORT_PREFIX = 'Panic reason: '
|
||||
|
||||
@ -101,7 +103,7 @@ def common_test(dut: PanicTestDut, config: str, expected_backtrace: Optional[Lis
|
||||
expected_coredump: Optional[List[Union[str, re.Pattern]]] = None) -> None:
|
||||
if 'gdbstub' in config:
|
||||
dut.expect_exact('Entering gdb stub now.')
|
||||
dut.start_gdb()
|
||||
dut.start_gdb_for_gdbstub()
|
||||
frames = dut.gdb_backtrace()
|
||||
if expected_backtrace is not None:
|
||||
dut.verify_gdb_backtrace(frames, expected_backtrace)
|
||||
@ -861,7 +863,7 @@ def test_gdbstub_coredump(dut: PanicTestDut) -> None:
|
||||
dut.process_coredump_uart()
|
||||
|
||||
dut.expect_exact('Entering gdb stub now.')
|
||||
dut.start_gdb()
|
||||
dut.start_gdb_for_gdbstub()
|
||||
frames = dut.gdb_backtrace()
|
||||
dut.verify_gdb_backtrace(frames, get_default_backtrace(test_func_name))
|
||||
dut.revert_log_level()
|
||||
@ -913,3 +915,25 @@ def test_illegal_access(dut: PanicTestDut, config: str, test_func_name: str) ->
|
||||
dut.expect_backtrace()
|
||||
dut.expect_elf_sha256()
|
||||
dut.expect_none('Guru Meditation')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('config', CONFIG_CAPTURE_DRAM, indirect=True)
|
||||
@pytest.mark.generic
|
||||
def test_capture_dram(dut: PanicTestDut, config: str, test_func_name: str) -> None:
|
||||
dut.run_test_func(test_func_name)
|
||||
|
||||
dut.expect_elf_sha256()
|
||||
dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
|
||||
|
||||
core_elf_file = dut.process_coredump_flash()
|
||||
dut.start_gdb_for_coredump(core_elf_file)
|
||||
|
||||
assert dut.gdb_data_eval_expr('g_data_var') == '43'
|
||||
assert dut.gdb_data_eval_expr('g_bss_var') == '55'
|
||||
assert re.search(r'0x[0-9a-fA-F]+ "Coredump Test"', dut.gdb_data_eval_expr('g_heap_ptr'))
|
||||
assert int(dut.gdb_data_eval_expr('g_cd_iram')) == 0x4243
|
||||
assert int(dut.gdb_data_eval_expr('g_cd_dram')) == 0x4344
|
||||
|
||||
if dut.target != 'esp32c2':
|
||||
assert int(dut.gdb_data_eval_expr('g_rtc_data_var')) == 0x55AA
|
||||
assert int(dut.gdb_data_eval_expr('g_rtc_fast_var')) == 0xAABBCCDD
|
||||
|
@ -0,0 +1,6 @@
|
||||
CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=y
|
||||
CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF=y
|
||||
CONFIG_ESP_COREDUMP_CHECKSUM_SHA256=y
|
||||
CONFIG_ESP_COREDUMP_CAPTURE_DRAM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_capture_dram.csv"
|
@ -120,15 +120,15 @@ class PanicTestDut(IdfDut):
|
||||
for pattern in patterns:
|
||||
if isinstance(pattern, str):
|
||||
position = coredump.find(pattern)
|
||||
assert position != -1, f"'{pattern}' not found in the coredump output"
|
||||
assert position != -1, f"'{pattern}' not in the coredump output"
|
||||
elif isinstance(pattern, re.Pattern):
|
||||
match = pattern.findall(coredump)
|
||||
assert match, f"'{pattern.pattern}' not found in the coredump output"
|
||||
assert match, f"'{pattern.pattern}' not in the coredump output"
|
||||
else:
|
||||
raise ValueError(f'Unsupported input type: {type(pattern).__name__}')
|
||||
|
||||
def _call_espcoredump(
|
||||
self, extra_args: List[str], coredump_file_name: str, output_file_name: str
|
||||
self, extra_args: List[str], output_file_name: str
|
||||
) -> None:
|
||||
# no "with" here, since we need the file to be open for later inspection by the test case
|
||||
if not self.coredump_output:
|
||||
@ -142,14 +142,13 @@ class PanicTestDut(IdfDut):
|
||||
espcoredump_script,
|
||||
'-b115200',
|
||||
'info_corefile',
|
||||
'--core',
|
||||
coredump_file_name,
|
||||
]
|
||||
espcoredump_args += extra_args
|
||||
espcoredump_args.append(self.app.elf_file)
|
||||
logging.info('Running %s', ' '.join(espcoredump_args))
|
||||
logging.info('espcoredump output is written to %s', self.coredump_output.name)
|
||||
|
||||
self.serial.close()
|
||||
subprocess.check_call(espcoredump_args, stdout=self.coredump_output)
|
||||
self.coredump_output.flush()
|
||||
self.coredump_output.seek(0)
|
||||
@ -165,33 +164,31 @@ class PanicTestDut(IdfDut):
|
||||
|
||||
output_file_name = os.path.join(self.logdir, 'coredump_uart_result.txt')
|
||||
self._call_espcoredump(
|
||||
['--core-format', 'b64'], coredump_file.name, output_file_name
|
||||
['--core-format', 'b64', '--core', coredump_file.name], output_file_name
|
||||
)
|
||||
if expected:
|
||||
self.expect_coredump(output_file_name, expected)
|
||||
|
||||
def process_coredump_flash(self, expected: Optional[List[Union[str, re.Pattern]]] = None) -> None:
|
||||
"""Extract the core dump from flash, run espcoredump on it"""
|
||||
def process_coredump_flash(self, expected: Optional[List[Union[str, re.Pattern]]] = None) -> Any:
|
||||
coredump_file_name = os.path.join(self.logdir, 'coredump_data.bin')
|
||||
logging.info('Writing flash binary core dump to %s', coredump_file_name)
|
||||
self.serial.dump_flash(partition='coredump', output=coredump_file_name)
|
||||
|
||||
output_file_name = os.path.join(self.logdir, 'coredump_flash_result.txt')
|
||||
self._call_espcoredump(
|
||||
['--core-format', 'raw'], coredump_file_name, output_file_name
|
||||
['--core-format', 'raw', '--save-core', coredump_file_name], output_file_name
|
||||
)
|
||||
if expected:
|
||||
self.expect_coredump(output_file_name, expected)
|
||||
return coredump_file_name
|
||||
|
||||
def gdb_write(self, command: str) -> Any:
|
||||
"""
|
||||
Wrapper to write to gdb with a longer timeout, as test runner
|
||||
host can be slow sometimes
|
||||
"""
|
||||
assert self.gdbmi, 'This function should be called only after start_gdb'
|
||||
assert self.gdbmi, 'This function should be called only after run_gdb'
|
||||
return self.gdbmi.write(command, timeout_sec=10)
|
||||
|
||||
def start_gdb(self) -> None:
|
||||
def run_gdb(self) -> None:
|
||||
"""
|
||||
Runs GDB and connects it to the "serial" port of the DUT.
|
||||
After this, the DUT expect methods can no longer be used to capture output.
|
||||
@ -262,6 +259,11 @@ class PanicTestDut(IdfDut):
|
||||
# Load the ELF file
|
||||
self.gdb_write('-file-exec-and-symbols {}'.format(self.app.elf_file))
|
||||
|
||||
# Prepare gdb for the gdb stub
|
||||
def start_gdb_for_gdbstub(self) -> None:
|
||||
|
||||
self.run_gdb()
|
||||
|
||||
# Connect GDB to UART
|
||||
self.serial.close()
|
||||
logging.info('Connecting to GDB Stub...')
|
||||
@ -290,7 +292,14 @@ class PanicTestDut(IdfDut):
|
||||
logging.info('Stopped in {func} at {addr} ({file}:{line})'.format(**frame))
|
||||
|
||||
# Drain remaining responses
|
||||
self.gdbmi.get_gdb_response(raise_error_on_timeout=False)
|
||||
if self.gdbmi:
|
||||
self.gdbmi.get_gdb_response(raise_error_on_timeout=False)
|
||||
|
||||
# Prepare gdb to debug coredump file
|
||||
def start_gdb_for_coredump(self, elf_file: str) -> None:
|
||||
|
||||
self.run_gdb()
|
||||
self.gdb_write('core {}'.format(elf_file))
|
||||
|
||||
def gdb_backtrace(self) -> Any:
|
||||
"""
|
||||
@ -302,6 +311,10 @@ class PanicTestDut(IdfDut):
|
||||
responses = self.gdb_write('-stack-list-frames')
|
||||
return self.find_gdb_response('done', 'result', responses)['payload']['stack']
|
||||
|
||||
def gdb_data_eval_expr(self, expr: str) -> Any:
|
||||
responses = self.gdb_write('-data-evaluate-expression "%s"' % expr)
|
||||
return self.find_gdb_response('done', 'result', responses)['payload']['value']
|
||||
|
||||
@staticmethod
|
||||
def verify_gdb_backtrace(
|
||||
gdb_backtrace: List[Any], expected_functions_list: List[Any]
|
||||
|
Loading…
Reference in New Issue
Block a user