Merge branch 'coredump_save_all_regions' into 'master'

Save .bss, .data and .heap sections in to the coredump

Closes IDF-2354 and IDFGH-11603

See merge request espressif/esp-idf!29028
master
Erhan Kurubas 2024-04-24 02:49:01 +08:00
commit 5bac46d996
15 zmienionych plików z 390 dodań i 78 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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).

Wyświetl plik

@ -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());

Wyświetl plik

@ -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 = &param->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);
}

Wyświetl plik

@ -56,9 +56,28 @@ 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.
.. only:: not esp32c5
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.
.. only:: SOC_SPIRAM_SUPPORTED
.. note::
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
------------------

Wyświetl plik

@ -56,8 +56,26 @@ ELF 格式具备扩展特性,支持在发生崩溃时保存更多关于错误
.. note::
如果使用了独立的栈,建议栈大小应大于 800 字节,确保核心转储例程本身不会导致栈溢出。
如果使用了独立的栈,建议栈大小应大于 1300 字节,确保核心转储例程本身不会导致栈溢出。
.. only:: not esp32c5
核心转储内存区域
^^^^^^^^^^^^^^^^
核心转储默认保存 CPU 寄存器、任务数据和崩溃原因。选择 :ref:`CONFIG_ESP_COREDUMP_CAPTURE_DRAM` 选项后,``.bss`` 段和 ``.data`` 段以及 ``heap`` 数据也将保存到转储中。
推荐将上面提到的几个数据段都保存到核心转储中,以方便调试。但这会导致核心转储文件变大,具体所需的额外存储空间取决于应用程序使用的 DRAM 大小。
.. only:: SOC_SPIRAM_SUPPORTED
.. note::
除了崩溃任务的 TCB 和栈外,位于外部 RAM 中的数据不会保存到核心转储文件中,包括使用 ``EXT_RAM_BSS_ATTR````EXT_RAM_NOINIT_ATTR`` 属性定义的变量,以及存储在 ``extram_bss`` 段中的任何数据。
.. note::
该功能仅在使用 ELF 文件格式时可用。
将核心转储保存到 flash
-----------------------

Wyświetl plik

@ -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);

Wyświetl plik

@ -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'

Wyświetl plik

@ -61,6 +61,8 @@ void test_assert_cache_disabled(void);
void test_illegal_access(void);
void test_capture_dram(void);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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,
1 # ESP-IDF Partition Table
2 # Name, Type, SubType, Offset, Size, Flags
3 nvs,data,nvs,0x9000,24K,
4 phy_init,data,phy,0xf000,4K,
5 factory,app,factory,0x10000,1M,
6 coredump,data,coredump,0x110000,128K,

Wyświetl plik

@ -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

Wyświetl plik

@ -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"

Wyświetl plik

@ -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]