mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(coredump): add panic details to the elf file
This commit is contained in:
parent
d3f2e4dab5
commit
568c397822
@ -19,7 +19,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
extern bool g_panic_abort;
|
||||
|
||||
extern char *g_panic_abort_details;
|
||||
extern void *g_exc_frames[SOC_CPU_CORES_NUM];
|
||||
|
||||
// Function to print longer amounts of information such as the details
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -63,7 +63,7 @@
|
||||
#define MWDT_DEFAULT_TICKS_PER_US 500
|
||||
|
||||
bool g_panic_abort = false;
|
||||
static char *s_panic_abort_details = NULL;
|
||||
char *g_panic_abort_details = NULL;
|
||||
|
||||
static wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT();
|
||||
|
||||
@ -222,7 +222,7 @@ static inline void disable_all_wdts(void)
|
||||
|
||||
static void print_abort_details(const void *f)
|
||||
{
|
||||
panic_print_str(s_panic_abort_details);
|
||||
panic_print_str(g_panic_abort_details);
|
||||
}
|
||||
|
||||
// Control arrives from chip-specific panic handler, environment prepared for
|
||||
@ -237,7 +237,7 @@ void esp_panic_handler(panic_info_t *info)
|
||||
// If the exception was due to an abort, override some of the panic info
|
||||
if (g_panic_abort) {
|
||||
info->description = NULL;
|
||||
info->details = s_panic_abort_details ? print_abort_details : NULL;
|
||||
info->details = g_panic_abort_details ? print_abort_details : NULL;
|
||||
info->reason = NULL;
|
||||
info->exception = PANIC_EXCEPTION_ABORT;
|
||||
}
|
||||
@ -438,7 +438,7 @@ void esp_panic_handler(panic_info_t *info)
|
||||
void IRAM_ATTR __attribute__((noreturn, no_sanitize_undefined)) panic_abort(const char *details)
|
||||
{
|
||||
g_panic_abort = true;
|
||||
s_panic_abort_details = (char *) details;
|
||||
g_panic_abort_details = (char *) details;
|
||||
|
||||
#if CONFIG_APPTRACE_ENABLE
|
||||
#if CONFIG_APPTRACE_SV_ENABLE
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -127,6 +127,30 @@ esp_err_t esp_core_dump_image_erase(void);
|
||||
|
||||
#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF
|
||||
|
||||
/**
|
||||
* @brief Get panic reason from the core dump.
|
||||
*
|
||||
* This function retrieves the panic reason from the core dump data and copies it to the provided buffer.
|
||||
*
|
||||
* @param[in,out] reason_buffer Pointer to the buffer where the panic reason will be copied.
|
||||
* @param[in] buffer_size Size of the destination buffer in bytes.
|
||||
* @return
|
||||
* - ESP_OK if the panic reason was successfully copied.
|
||||
* - ESP_ERR_INVALID_ARG if reason_buffer is NULL or buffer_size is 0.
|
||||
* - Other error codes indicating the outcome of the core dump retrieval.
|
||||
* - ESP_ERR_NOT_FOUND if the panic reason is not found in the core dump.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
char panic_reason[200];
|
||||
esp_err_t err = esp_core_dump_get_panic_reason(panic_reason, sizeof(panic_reason));
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGW(TAG, "%s", panic_reason);
|
||||
}
|
||||
* @endcode
|
||||
*/
|
||||
esp_err_t esp_core_dump_get_panic_reason(char *reason_buffer, size_t buffer_size);
|
||||
|
||||
/**
|
||||
* @brief Get the summary of a core dump.
|
||||
*
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "esp_core_dump_common.h"
|
||||
|
||||
#ifdef CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF
|
||||
#include <sys/param.h> // for the MIN macro
|
||||
#include "esp_app_desc.h"
|
||||
#endif
|
||||
|
||||
@ -29,6 +30,7 @@
|
||||
#define ELF_PR_STATUS_SEG_NUM 0
|
||||
#define ELF_ESP_CORE_DUMP_INFO_TYPE 8266
|
||||
#define ELF_ESP_CORE_DUMP_EXTRA_INFO_TYPE 677
|
||||
#define ELF_ESP_CORE_DUMP_PANIC_DETAILS_TYPE 679
|
||||
#define ELF_NOTE_NAME_MAX_SIZE 32
|
||||
#define ELF_APP_SHA256_SIZE 66
|
||||
|
||||
@ -517,13 +519,23 @@ static int elf_write_core_dump_info(core_dump_elf_t *self)
|
||||
}
|
||||
|
||||
ret = elf_add_note(self,
|
||||
"EXTRA_INFO",
|
||||
"ESP_EXTRA_INFO",
|
||||
ELF_ESP_CORE_DUMP_EXTRA_INFO_TYPE,
|
||||
extra_info,
|
||||
extra_info_len);
|
||||
ELF_CHECK_ERR((ret > 0), ret, "Extra info note write failed. Returned (%d).", ret);
|
||||
data_len += ret;
|
||||
|
||||
if (g_panic_abort_details && strlen(g_panic_abort_details) > 0) {
|
||||
ret = elf_add_note(self,
|
||||
"ESP_PANIC_DETAILS",
|
||||
ELF_ESP_CORE_DUMP_PANIC_DETAILS_TYPE,
|
||||
g_panic_abort_details,
|
||||
strlen(g_panic_abort_details));
|
||||
ELF_CHECK_ERR((ret > 0), ret, "Panic details note write failed. Returned (%d).", ret);
|
||||
data_len += ret;
|
||||
}
|
||||
|
||||
ret = elf_process_note_segment(self, data_len);
|
||||
ELF_CHECK_ERR((ret > 0), ret,
|
||||
"EXTRA_INFO note segment processing failure, returned(%d).", ret);
|
||||
@ -642,8 +654,18 @@ esp_err_t esp_core_dump_write_elf(core_dump_write_config_t *write_cfg)
|
||||
|
||||
#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
|
||||
/* Below are the helper function to parse the core dump ELF stored in flash */
|
||||
typedef struct {
|
||||
#if ELF_CLASS == ELFCLASS32
|
||||
Elf32_Word n_type;
|
||||
Elf32_Word n_descsz;
|
||||
#else
|
||||
Elf64_Word n_type;
|
||||
Elf64_Word n_descsz;
|
||||
#endif
|
||||
void *n_ptr;
|
||||
} elf_note_content_t;
|
||||
|
||||
/* Below are the helper function to parse the core dump ELF stored in flash */
|
||||
static esp_err_t elf_core_dump_image_mmap(esp_partition_mmap_handle_t* core_data_handle, const void **map_addr)
|
||||
{
|
||||
size_t out_size;
|
||||
@ -700,62 +722,118 @@ static void elf_parse_exc_task_name(esp_core_dump_summary_t *summary, void *tcb_
|
||||
ESP_COREDUMP_LOGD("Crashing task %s", summary->exc_task);
|
||||
}
|
||||
|
||||
esp_err_t esp_core_dump_get_summary(esp_core_dump_summary_t *summary)
|
||||
static uint8_t *elf_core_dump_image_ptr(esp_partition_mmap_handle_t *core_data_handle)
|
||||
{
|
||||
int i;
|
||||
elf_phdr *ph;
|
||||
elf_note *note;
|
||||
const void *map_addr;
|
||||
size_t consumed_note_sz;
|
||||
esp_partition_mmap_handle_t core_data_handle;
|
||||
if (!core_data_handle) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!summary) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_err_t err = elf_core_dump_image_mmap(&core_data_handle, &map_addr);
|
||||
const void *map_addr;
|
||||
|
||||
esp_err_t err = elf_core_dump_image_mmap(core_data_handle, &map_addr);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
return NULL;
|
||||
}
|
||||
uint8_t *ptr = (uint8_t *) map_addr + sizeof(core_dump_header_t);
|
||||
elfhdr *eh = (elfhdr *)ptr;
|
||||
return (uint8_t *)map_addr + sizeof(core_dump_header_t);
|
||||
}
|
||||
|
||||
ESP_COREDUMP_LOGD("ELF ident %02x %c %c %c", eh->e_ident[0], eh->e_ident[1], eh->e_ident[2], eh->e_ident[3]);
|
||||
static void esp_core_dump_parse_note_section(uint8_t *coredump_data, elf_note_content_t *target_notes, size_t size)
|
||||
{
|
||||
elfhdr *eh = (elfhdr *)coredump_data;
|
||||
elf_phdr *phdr = (elf_phdr *)(coredump_data + eh->e_phoff);
|
||||
|
||||
ESP_COREDUMP_LOGD("ELF ident %02x %c %c %c", eh->e_ident[0], eh->e_ident[1], eh->e_ident[2], eh->e_ident[3]);
|
||||
ESP_COREDUMP_LOGD("Ph_num %d offset %x", eh->e_phnum, eh->e_phoff);
|
||||
|
||||
for (i = 0; i < eh->e_phnum; i++) {
|
||||
ph = (elf_phdr *)((ptr + i * sizeof(*ph)) + eh->e_phoff);
|
||||
ESP_COREDUMP_LOGD("PHDR type %d off %x vaddr %x paddr %x filesz %x memsz %x flags %x align %x",
|
||||
ph->p_type, ph->p_offset, ph->p_vaddr, ph->p_paddr, ph->p_filesz, ph->p_memsz,
|
||||
ph->p_flags, ph->p_align);
|
||||
for (unsigned int i = 0; i < eh->e_phnum; i++) {
|
||||
const elf_phdr *ph = &phdr[i];
|
||||
ESP_COREDUMP_LOGD("PHDR type %d off %x vaddr %x paddr %x filesz %x memsz %x flags %x align %x",
|
||||
ph->p_type, ph->p_offset, ph->p_vaddr, ph->p_paddr, ph->p_filesz, ph->p_memsz,
|
||||
ph->p_flags, ph->p_align);
|
||||
if (ph->p_type == PT_NOTE) {
|
||||
consumed_note_sz = 0;
|
||||
while(consumed_note_sz < ph->p_memsz) {
|
||||
note = (elf_note *)(ptr + ph->p_offset + consumed_note_sz);
|
||||
char *nm = (char *)(ptr + ph->p_offset + consumed_note_sz + sizeof(elf_note));
|
||||
ESP_COREDUMP_LOGD("Note NameSZ %x DescSZ %x Type %x name %s", note->n_namesz,
|
||||
note->n_descsz, note->n_type, nm);
|
||||
if (strncmp(nm, "EXTRA_INFO", note->n_namesz) == 0 ) {
|
||||
esp_core_dump_summary_parse_extra_info(summary, (void *)(nm + note->n_namesz));
|
||||
}
|
||||
if (strncmp(nm, "ESP_CORE_DUMP_INFO", note->n_namesz) == 0 ) {
|
||||
elf_parse_version_info(summary, (void *)(nm + note->n_namesz));
|
||||
}
|
||||
size_t consumed_note_sz = 0;
|
||||
while (consumed_note_sz < ph->p_memsz) {
|
||||
const elf_note *note = (const elf_note *)(coredump_data + ph->p_offset + consumed_note_sz);
|
||||
for (size_t idx = 0; idx < size; ++idx) {
|
||||
if (target_notes[idx].n_type == note->n_type) {
|
||||
char *nm = (char *)¬e[1];
|
||||
target_notes[idx].n_ptr = nm + note->n_namesz;
|
||||
target_notes[idx].n_descsz = note->n_descsz;
|
||||
ESP_COREDUMP_LOGD("%d bytes target note (%X) found in the note section",
|
||||
note->n_descsz, note->n_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
consumed_note_sz += note->n_namesz + note->n_descsz + sizeof(elf_note);
|
||||
ALIGN(4, consumed_note_sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_core_dump_get_panic_reason(char *reason_buffer, size_t buffer_size)
|
||||
{
|
||||
if (!reason_buffer || buffer_size == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_partition_mmap_handle_t core_data_handle;
|
||||
uint8_t *ptr = elf_core_dump_image_ptr(&core_data_handle);
|
||||
if (ptr == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
elf_note_content_t target_note = { .n_type = ELF_ESP_CORE_DUMP_PANIC_DETAILS_TYPE, .n_ptr = NULL };
|
||||
|
||||
esp_core_dump_parse_note_section(ptr, &target_note, 1);
|
||||
if (target_note.n_ptr) {
|
||||
size_t len = MIN(target_note.n_descsz, buffer_size - 1);
|
||||
strncpy(reason_buffer, target_note.n_ptr, len);
|
||||
reason_buffer[len] = '\0';
|
||||
}
|
||||
esp_partition_munmap(core_data_handle);
|
||||
|
||||
return target_note.n_ptr ? ESP_OK : ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
esp_err_t esp_core_dump_get_summary(esp_core_dump_summary_t *summary)
|
||||
{
|
||||
if (!summary) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_partition_mmap_handle_t core_data_handle;
|
||||
uint8_t *ptr = elf_core_dump_image_ptr(&core_data_handle);
|
||||
if (ptr == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
elf_note_content_t target_notes[2] = {
|
||||
[0] = { .n_type = ELF_ESP_CORE_DUMP_EXTRA_INFO_TYPE, .n_ptr = NULL },
|
||||
[1] = { .n_type = ELF_ESP_CORE_DUMP_INFO_TYPE, .n_ptr = NULL }
|
||||
};
|
||||
|
||||
esp_core_dump_parse_note_section(ptr, target_notes, sizeof(target_notes) / sizeof(target_notes[0]));
|
||||
if (target_notes[0].n_ptr) {
|
||||
esp_core_dump_summary_parse_extra_info(summary, target_notes[0].n_ptr);
|
||||
}
|
||||
if (target_notes[1].n_ptr) {
|
||||
elf_parse_version_info(summary, target_notes[1].n_ptr);
|
||||
}
|
||||
|
||||
/* Following code assumes that task stack segment follows the TCB segment for the respective task.
|
||||
* In general ELF does not impose any restrictions on segments' order so this can be changed without impacting core dump version.
|
||||
* More universal and flexible way would be to retrieve stack start address from crashed task TCB segment and then look for the stack segment with that address.
|
||||
*/
|
||||
elfhdr *eh = (elfhdr *)ptr;
|
||||
elf_phdr *phdr = (elf_phdr *)(ptr + eh->e_phoff);
|
||||
int flag = 0;
|
||||
for (i = 0; i < eh->e_phnum; i++) {
|
||||
ph = (elf_phdr *)((ptr + i * sizeof(*ph)) + eh->e_phoff);
|
||||
for (unsigned int i = 0; i < eh->e_phnum; i++) {
|
||||
const elf_phdr *ph = &phdr[i];
|
||||
if (ph->p_type == PT_LOAD) {
|
||||
if (flag) {
|
||||
esp_core_dump_summary_parse_exc_regs(summary, (void *)(ptr + ph->p_offset));
|
||||
esp_core_dump_summary_parse_backtrace_info(&summary->exc_bt_info, (void *) ph->p_vaddr,
|
||||
esp_core_dump_summary_parse_backtrace_info(&summary->exc_bt_info, (void *)ph->p_vaddr,
|
||||
(void *)(ptr + ph->p_offset), ph->p_memsz);
|
||||
break;
|
||||
}
|
||||
@ -765,7 +843,9 @@ esp_err_t esp_core_dump_get_summary(esp_core_dump_summary_t *summary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_partition_munmap(core_data_handle);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -10,7 +10,7 @@ This test app is relatively complex because it has to check many possible combin
|
||||
- Chip target: esp32, esp32c3, ...
|
||||
- Configuration: default, GDB Stub, Core Dump to UART, ...
|
||||
|
||||
Failure scenarios are implemented in [test_panic_main.c](main/test_panic_main.c). The test application receives the name of the scenario from console (e.g. `test_illegal_instruction` ). The failure scenario is executed and the app panics. Once the panic output is printed, the pytest-based test case parses the output and verifies that the behavior of the panic handler was correct.
|
||||
Failure scenarios are implemented in [test_panic.c](main/test_panic.c). The test application receives the name of the scenario from console (e.g. `test_illegal_instruction` ). The failure scenario is executed and the app panics. Once the panic output is printed, the pytest-based test case parses the output and verifies that the behavior of the panic handler was correct.
|
||||
|
||||
In [pytest_panic.py](pytest_panic.py), there typically is one test function for each failure scenario. Each test function is then parametrized by `config` parameter. This creates "copies" of the test case for each of the configurations (default, GDB Stub, etc.) Tests are also parametrized with target-specific markers. Most tests can run on every target, but there are a few exceptions, such as failure scenarios specific to the dual-core chips.
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import re
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import pexpect
|
||||
import pytest
|
||||
@ -52,12 +52,16 @@ CONFIGS_EXTRAM_STACK = [
|
||||
pytest.param('coredump_extram_stack', marks=[pytest.mark.esp32, pytest.mark.esp32s2, pytest.mark.psram, pytest.mark.esp32s3, pytest.mark.quad_psram])
|
||||
]
|
||||
|
||||
# Panic abort information will start with this string.
|
||||
PANIC_ABORT_PREFIX = 'Panic reason: '
|
||||
|
||||
|
||||
def get_default_backtrace(config: str) -> List[str]:
|
||||
return [config, 'app_main', 'main_task', 'vPortTaskWrapper']
|
||||
|
||||
|
||||
def common_test(dut: PanicTestDut, config: str, expected_backtrace: Optional[List[str]] = None) -> None:
|
||||
def common_test(dut: PanicTestDut, config: str, expected_backtrace: Optional[List[str]] = None, check_cpu_reset: Optional[bool] = True,
|
||||
expected_coredump: Optional[List[Union[str, re.Pattern]]] = None) -> None:
|
||||
if 'gdbstub' in config:
|
||||
dut.expect_exact('Entering gdb stub now.')
|
||||
dut.start_gdb()
|
||||
@ -67,10 +71,14 @@ def common_test(dut: PanicTestDut, config: str, expected_backtrace: Optional[Lis
|
||||
dut.revert_log_level()
|
||||
return # don't expect "Rebooting" output below
|
||||
|
||||
# We will only perform comparisons for ELF files, as we are not introducing any new fields to the binary file format.
|
||||
if 'bin' in config:
|
||||
expected_coredump = None
|
||||
|
||||
if 'uart' in config:
|
||||
dut.process_coredump_uart()
|
||||
dut.process_coredump_uart(expected_coredump)
|
||||
elif 'flash' in config:
|
||||
dut.process_coredump_flash()
|
||||
dut.process_coredump_flash(expected_coredump)
|
||||
elif 'panic' in config:
|
||||
pass
|
||||
|
||||
@ -347,7 +355,8 @@ def test_storeprohibited(dut: PanicTestDut, config: str, test_func_name: str) ->
|
||||
@pytest.mark.generic
|
||||
def test_abort(dut: PanicTestDut, config: str, test_func_name: str) -> None:
|
||||
dut.run_test_func(test_func_name)
|
||||
dut.expect(r'abort\(\) was called at PC [0-9xa-f]+ on core 0')
|
||||
regex_pattern = rb'abort\(\) was called at PC [0-9xa-f]+ on core 0'
|
||||
dut.expect(regex_pattern)
|
||||
if dut.is_xtensa:
|
||||
dut.expect_backtrace()
|
||||
else:
|
||||
@ -355,6 +364,7 @@ def test_abort(dut: PanicTestDut, config: str, test_func_name: str) -> None:
|
||||
dut.expect_elf_sha256()
|
||||
dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
|
||||
|
||||
coredump_pattern = re.compile(PANIC_ABORT_PREFIX + regex_pattern.decode('utf-8'))
|
||||
common_test(
|
||||
dut,
|
||||
config,
|
||||
@ -363,6 +373,7 @@ def test_abort(dut: PanicTestDut, config: str, test_func_name: str) -> None:
|
||||
'esp_system_abort',
|
||||
'abort'
|
||||
] + get_default_backtrace(test_func_name),
|
||||
expected_coredump=[coredump_pattern]
|
||||
)
|
||||
|
||||
|
||||
@ -370,7 +381,8 @@ def test_abort(dut: PanicTestDut, config: str, test_func_name: str) -> None:
|
||||
@pytest.mark.generic
|
||||
def test_ub(dut: PanicTestDut, config: str, test_func_name: str) -> None:
|
||||
dut.run_test_func(test_func_name)
|
||||
dut.expect('Undefined behavior of type out_of_bounds')
|
||||
regex_pattern = rb'Undefined behavior of type out_of_bounds'
|
||||
dut.expect(regex_pattern)
|
||||
if dut.is_xtensa:
|
||||
dut.expect_backtrace()
|
||||
else:
|
||||
@ -378,6 +390,7 @@ def test_ub(dut: PanicTestDut, config: str, test_func_name: str) -> None:
|
||||
dut.expect_elf_sha256()
|
||||
dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
|
||||
|
||||
coredump_pattern = re.compile(PANIC_ABORT_PREFIX + regex_pattern.decode('utf-8'))
|
||||
common_test(
|
||||
dut,
|
||||
config,
|
||||
@ -387,6 +400,7 @@ def test_ub(dut: PanicTestDut, config: str, test_func_name: str) -> None:
|
||||
'__ubsan_default_handler',
|
||||
'__ubsan_handle_out_of_bounds'
|
||||
] + get_default_backtrace(test_func_name),
|
||||
expected_coredump=[coredump_pattern]
|
||||
)
|
||||
|
||||
|
||||
@ -398,13 +412,16 @@ def test_abort_cache_disabled(
|
||||
if dut.target == 'esp32s2':
|
||||
pytest.xfail(reason='Crashes in itoa which is not in ROM, IDF-3572')
|
||||
dut.run_test_func(test_func_name)
|
||||
dut.expect(r'abort\(\) was called at PC [0-9xa-f]+ on core 0')
|
||||
regex_pattern = rb'abort\(\) was called at PC [0-9xa-f]+ on core 0'
|
||||
dut.expect(regex_pattern)
|
||||
if dut.is_xtensa:
|
||||
dut.expect_backtrace()
|
||||
else:
|
||||
dut.expect_stack_dump()
|
||||
dut.expect_elf_sha256()
|
||||
dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
|
||||
|
||||
coredump_pattern = re.compile(PANIC_ABORT_PREFIX + regex_pattern.decode('utf-8'))
|
||||
common_test(
|
||||
dut,
|
||||
config,
|
||||
@ -413,6 +430,7 @@ def test_abort_cache_disabled(
|
||||
'esp_system_abort',
|
||||
'abort'
|
||||
] + get_default_backtrace(test_func_name),
|
||||
expected_coredump=[coredump_pattern]
|
||||
)
|
||||
|
||||
|
||||
@ -420,17 +438,16 @@ def test_abort_cache_disabled(
|
||||
@pytest.mark.generic
|
||||
def test_assert(dut: PanicTestDut, config: str, test_func_name: str) -> None:
|
||||
dut.run_test_func(test_func_name)
|
||||
dut.expect(
|
||||
re.compile(
|
||||
rb'assert failed:[\s\w()]*?\s[.\w/]*\.(?:c|cpp|h|hpp):\d.*$', re.MULTILINE
|
||||
)
|
||||
)
|
||||
regex_pattern = rb'assert failed:[\s\w()]*?\s[.\w/]*\.(?:c|cpp|h|hpp):\d.*$'
|
||||
dut.expect(re.compile(regex_pattern, re.MULTILINE))
|
||||
if dut.is_xtensa:
|
||||
dut.expect_backtrace()
|
||||
else:
|
||||
dut.expect_stack_dump()
|
||||
dut.expect_elf_sha256()
|
||||
dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
|
||||
|
||||
coredump_pattern = re.compile(PANIC_ABORT_PREFIX + regex_pattern.decode('utf-8'), re.MULTILINE)
|
||||
common_test(
|
||||
dut,
|
||||
config,
|
||||
@ -438,7 +455,9 @@ def test_assert(dut: PanicTestDut, config: str, test_func_name: str) -> None:
|
||||
'panic_abort',
|
||||
'esp_system_abort',
|
||||
'__assert_func'
|
||||
] + get_default_backtrace(test_func_name))
|
||||
] + get_default_backtrace(test_func_name),
|
||||
expected_coredump=[coredump_pattern]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('config', CONFIGS, indirect=True)
|
||||
@ -449,13 +468,16 @@ def test_assert_cache_disabled(
|
||||
if dut.target == 'esp32s2':
|
||||
pytest.xfail(reason='Crashes in itoa which is not in ROM, IDF-3572')
|
||||
dut.run_test_func(test_func_name)
|
||||
dut.expect(re.compile(rb'assert failed: [0-9xa-fA-F]+.*$', re.MULTILINE))
|
||||
regex_pattern = rb'assert failed: [0-9xa-fA-F]+.*$'
|
||||
dut.expect(re.compile(regex_pattern, re.MULTILINE))
|
||||
if dut.is_xtensa:
|
||||
dut.expect_backtrace()
|
||||
else:
|
||||
dut.expect_stack_dump()
|
||||
dut.expect_elf_sha256()
|
||||
dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
|
||||
|
||||
coredump_pattern = re.compile(PANIC_ABORT_PREFIX + regex_pattern.decode('utf-8'), re.MULTILINE)
|
||||
common_test(
|
||||
dut,
|
||||
config,
|
||||
@ -463,7 +485,9 @@ def test_assert_cache_disabled(
|
||||
'panic_abort',
|
||||
'esp_system_abort',
|
||||
'__assert_func'
|
||||
] + get_default_backtrace(test_func_name))
|
||||
] + get_default_backtrace(test_func_name),
|
||||
expected_coredump=[coredump_pattern]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
|
@ -2,9 +2,10 @@
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Any, Dict, List, Optional, TextIO
|
||||
from typing import Any, Dict, List, Optional, TextIO, Union
|
||||
|
||||
import pexpect
|
||||
from panic_utils import NoGdbProcessError, attach_logger, quote_string, sha256, verify_valid_gdb_subprocess
|
||||
@ -96,6 +97,19 @@ class PanicTestDut(IdfDut):
|
||||
)
|
||||
self.expect_exact('ELF file SHA256: ' + elf_sha256[0:elf_sha256_len])
|
||||
|
||||
def expect_coredump(self, output_file_name: str, patterns: List[Union[str, re.Pattern]]) -> None:
|
||||
with open(output_file_name, 'r') as file:
|
||||
coredump = file.read()
|
||||
for pattern in patterns:
|
||||
if isinstance(pattern, str):
|
||||
position = coredump.find(pattern)
|
||||
assert position != -1, f"'{pattern}' not found in the coredump output"
|
||||
elif isinstance(pattern, re.Pattern):
|
||||
match = pattern.findall(coredump)
|
||||
assert match, f"'{pattern.pattern}' not found 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
|
||||
) -> None:
|
||||
@ -122,7 +136,7 @@ class PanicTestDut(IdfDut):
|
||||
self.coredump_output.flush()
|
||||
self.coredump_output.seek(0)
|
||||
|
||||
def process_coredump_uart(self) -> None:
|
||||
def process_coredump_uart(self, expected: Optional[List[Union[str, re.Pattern]]] = None) -> None:
|
||||
"""Extract the core dump from UART output of the test, run espcoredump on it"""
|
||||
self.expect(self.COREDUMP_UART_START)
|
||||
res = self.expect('(.+)' + self.COREDUMP_UART_END)
|
||||
@ -135,8 +149,10 @@ class PanicTestDut(IdfDut):
|
||||
self._call_espcoredump(
|
||||
['--core-format', 'b64'], coredump_file.name, output_file_name
|
||||
)
|
||||
if expected:
|
||||
self.expect_coredump(output_file_name, expected)
|
||||
|
||||
def process_coredump_flash(self) -> None:
|
||||
def process_coredump_flash(self, expected: Optional[List[Union[str, re.Pattern]]] = None) -> None:
|
||||
"""Extract the core dump from flash, run espcoredump on it"""
|
||||
coredump_file_name = os.path.join(self.logdir, 'coredump_data.bin')
|
||||
logging.info('Writing flash binary core dump to %s', coredump_file_name)
|
||||
@ -146,6 +162,8 @@ class PanicTestDut(IdfDut):
|
||||
self._call_espcoredump(
|
||||
['--core-format', 'raw'], coredump_file_name, output_file_name
|
||||
)
|
||||
if expected:
|
||||
self.expect_coredump(output_file_name, expected)
|
||||
|
||||
def gdb_write(self, command: str) -> Any:
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user