Merge branch 'feature/enable_fp_backtracing' into 'master'

feat(riscv): implement frame pointer option for backtracing

See merge request espressif/esp-idf!32342
This commit is contained in:
Omar Chebib 2025-01-13 18:11:49 +08:00
commit 52b558d218
32 changed files with 499 additions and 169 deletions

View File

@ -207,6 +207,13 @@ if(CONFIG_ESP_SYSTEM_USE_EH_FRAME)
list(APPEND link_options "-Wl,--eh-frame-hdr")
endif()
if(CONFIG_ESP_SYSTEM_USE_FRAME_POINTER)
list(APPEND compile_options "-fno-omit-frame-pointer")
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
list(APPEND compile_options "-mno-omit-leaf-frame-pointer")
endif()
endif()
list(APPEND link_options "-fno-lto")
if(CONFIG_IDF_TARGET_LINUX AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")

View File

@ -60,9 +60,9 @@ endif()
if(CONFIG_HEAP_TRACING_TOHOST)
list(APPEND srcs "heap_trace_tohost.c")
set_source_files_properties(heap_trace_tohost.c
PROPERTIES COMPILE_FLAGS
-Wno-frame-address)
if(CONFIG_IDF_TARGET_ARCH_XTENSA)
set_source_files_properties(heap_trace_tohost.c PROPERTIES COMPILE_FLAGS -Wno-frame-address)
endif()
endif()
idf_component_register(SRCS "${srcs}"

View File

@ -1,13 +1,11 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sdkconfig.h>
#include "sdkconfig.h"
#define HEAP_TRACE_SRCFILE /* don't warn on inclusion here */
#include "esp_heap_trace.h"
#undef HEAP_TRACE_SRCFILE
#include "esp_heap_caps.h"
#if CONFIG_APPTRACE_SV_ENABLE
#include "esp_app_trace.h"
@ -16,7 +14,7 @@
#define STACK_DEPTH CONFIG_HEAP_TRACING_STACK_DEPTH
#ifdef CONFIG_HEAP_TRACING_TOHOST
#if CONFIG_HEAP_TRACING_TOHOST
#if !CONFIG_APPTRACE_SV_ENABLE
#error None of the heap tracing backends is enabled! You must enable SystemView compatible tracing to use this feature.

View File

@ -56,6 +56,10 @@ else()
list(APPEND srcs "eh_frame_parser.c")
endif()
if(CONFIG_ESP_SYSTEM_USE_FRAME_POINTER)
list(APPEND srcs "fp_unwind.c")
endif()
if(CONFIG_SOC_SYSTIMER_SUPPORT_ETM)
list(APPEND srcs "systick_etm.c")
endif()

View File

@ -105,17 +105,38 @@ menu "ESP System Settings"
similar to that of DRAM region but without DMA. Speed wise RTC fast memory operates on
APB clock and hence does not have much performance impact.
config ESP_SYSTEM_USE_EH_FRAME
bool "Generate and use eh_frame for backtracing"
default n
choice ESP_BACKTRACING_METHOD
prompt "Backtracing method"
default ESP_SYSTEM_NO_BACKTRACE
depends on IDF_TARGET_ARCH_RISCV
help
Generate DWARF information for each function of the project. These information will parsed and used to
perform backtracing when panics occur. Activating this option will activate asynchronous frame unwinding
and generation of both .eh_frame and .eh_frame_hdr sections, resulting in a bigger binary size (20% to
100% larger). The main purpose of this option is to be able to have a backtrace parsed and printed by
the program itself, regardless of the serial monitor used.
This option shall NOT be used for production.
Configure how backtracing will be performed at runtime when a panic occurs.
config ESP_SYSTEM_NO_BACKTRACE
bool "No backtracing"
help
When selected, no backtracing will be performed at runtime. By using idf.py monitor, it
is still possible to get a backtrace when a panic occurs.
config ESP_SYSTEM_USE_EH_FRAME
bool "Generate and use eh_frame for backtracing"
help
Generate DWARF information for each function of the project. These information will parsed and used to
perform backtracing when panics occur. Activating this option will activate asynchronous frame
unwinding and generation of both .eh_frame and .eh_frame_hdr sections, resulting in a bigger binary
size (20% to 100% larger). The main purpose of this option is to be able to have a backtrace parsed
and printed by the program itself, regardless of the serial monitor used.
This option is not recommended to be used for production.
config ESP_SYSTEM_USE_FRAME_POINTER
bool "Use CPU Frame Pointer register"
help
This configuration allows the compiler to allocate CPU register s0 as the frame pointer. The main usage
of the frame pointer is to be able to generate a backtrace from the panic handler on exception.
Enabling this option results in bigger and slightly slower code since all functions will have
to populate this register and won't be able to use it as a general-purpose register anymore.
endchoice
menu "Memory protection"

View File

@ -0,0 +1,117 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <string.h>
#include "esp_private/panic_internal.h"
#include "esp_memory_utils.h"
#include "riscv/libunwind-riscv.h"
#include "esp_private/fp_unwind.h"
#define FP_MAX_CALLERS 64
#define RA_INDEX_IN_FP -1
#define SP_INDEX_IN_FP -2
/**
* @brief When one step of the backtrace is generated, output it to the serial.
* This function can be overridden as it is defined as weak.
*
* @param pc Program counter of the backtrace step.
* @param sp Stack pointer of the backtrace step.
*/
void __attribute__((weak)) esp_fp_generated_step(uint32_t pc, uint32_t sp)
{
panic_print_str(" 0x");
panic_print_hex(pc);
panic_print_str(":0x");
panic_print_hex(sp);
}
/**
* @brief Check if the given pointer is a data pointer that can be dereferenced
*
* @param ptr Data pointer to check
*/
static inline bool esp_fp_ptr_is_data(void* ptr)
{
return esp_ptr_in_dram(ptr) || esp_ptr_external_ram(ptr);
}
/**
* @brief Generate a call stack starting at the given frame pointer.
*
* @note Since this function can be called from RAM (from heap trace), it shall also be put in RAM.
*
* @param frame[in] Frame pointer to start unrolling from.
* @param callers[out] Array of callers where 0 will store the most recent caller. Can be NULL.
* @param stacks[out] Array of callers' stacks where 0 will store the most recent caller's stack. Can be NULL.
* @param depth[in] Number of maximum entries to fill in the callers array.
*
* @returns Number of entries filled in the array.
*/
uint32_t IRAM_ATTR esp_fp_get_callers(uint32_t frame, void** callers, void** stacks, uint32_t depth)
{
uint32_t written = 0;
uint32_t pc = 0;
uint32_t sp = 0;
uint32_t* fp = (uint32_t*) frame;
if (depth == 0 || (callers == NULL && stacks == NULL)) {
return 0;
}
/**
* We continue filling the callers array until we meet a function in ROM (not compiled
* with frame pointer) or the pointer we retrieved is not in RAM (binary libraries
* not compiled with frame pointer configuration, which means what is stored in the register is not a valid pointer)
*/
while (depth > 0 && esp_fp_ptr_is_data(fp) && !esp_ptr_in_rom((void *) pc)) {
/* Dereference the RA register from the frame pointer and store it in PC */
pc = fp[RA_INDEX_IN_FP];
sp = fp[SP_INDEX_IN_FP];
fp = (uint32_t*) sp;
if (callers) {
callers[written] = (void*) pc;
}
if (stacks) {
stacks[written] = (void*) sp;
}
depth--;
written++;
}
return written;
}
/**
* @brief Print backtrace for the given execution frame.
*
* @param frame_or Snapshot of the CPU registers when the CPU stopped its normal execution.
*/
void esp_fp_print_backtrace(const void *frame_or)
{
assert(frame_or != NULL);
ExecutionFrame frame = *((ExecutionFrame*) frame_or);
const uint32_t sp = EXECUTION_FRAME_SP(frame);
const uint32_t pc = EXECUTION_FRAME_PC(frame);
const uint32_t fp = frame.s0;
void* callers[FP_MAX_CALLERS];
void* stacks[FP_MAX_CALLERS];
panic_print_str("Backtrace:");
esp_fp_generated_step(pc, sp);
uint32_t len = esp_fp_get_callers(fp, callers, stacks, FP_MAX_CALLERS);
for (int i = 0; i < len; i++) {
esp_fp_generated_step((uint32_t) callers[i], (uint32_t) stacks[i]);
}
panic_print_str("\r\n");
}

View File

@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef FP_UNWIND_H
#define FP_UNWIND_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Print backtrace for the given execution frame thanks to the frame pointers.
*
* @param frame_or Snapshot of the CPU registers when the program stopped its
* normal execution. This frame is usually generated on the
* stack when an exception or an interrupt occurs.
*/
void esp_fp_print_backtrace(const void *frame_or);
#ifdef __cplusplus
}
#endif
#endif // FP_UNWIND_H

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -17,19 +17,20 @@
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
#include "esp_private/eh_frame_parser.h"
#endif // CONFIG_ESP_SYSTEM_USE_EH_FRAME
#if !CONFIG_ESP_SYSTEM_USE_EH_FRAME
#elif CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
extern void esp_fp_print_backtrace(const void*);
#else // !CONFIG_ESP_SYSTEM_USE_EH_FRAME && !
/* Function used to print all the registers pointed by the given frame .*/
extern void panic_print_registers(const void *frame, int core);
#endif // !CONFIG_ESP_SYSTEM_USE_EH_FRAME
#endif // CONFIG_ESP_SYSTEM_USE_EH_FRAME
/* Targets based on a RISC-V CPU cannot perform backtracing that easily.
* We have two options here:
* - Perform backtracing at runtime.
* - Perform backtracing at runtime thanks to the configuration options
* CONFIG_ESP_SYSTEM_USE_EH_FRAME and CONFIG_ESP_SYSTEM_USE_FRAME_POINTER.
* - Let IDF monitor do the backtracing for us. Used during panic already.
* This could be configurable, choosing one or the other depending on
* CONFIG_ESP_SYSTEM_USE_EH_FRAME configuration option.
*
* In both cases, this takes time, and we might be in an ISR, we must
* exit this handler as fast as possible, then we will simply print
@ -54,14 +55,15 @@ esp_err_t IRAM_ATTR esp_backtrace_print(int depth)
memcpy(&backtrace_frame, frame, sizeof(esp_cpu_frame_t));
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
esp_rom_printf("esp_backtrace_print: Print CPU %d (current core) backtrace\n", current_core);
esp_eh_frame_print_backtrace(frame);
#elif CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
esp_fp_print_backtrace(frame);
#else // CONFIG_ESP_SYSTEM_USE_EH_FRAME
esp_rom_printf("esp_backtrace_print: Print CPU %d (current core) registers\n", current_core);
panic_prepare_frame_from_ctx(&backtrace_frame);
panic_print_registers(&backtrace_frame, current_core);
esp_rom_printf("\r\n");
esp_rom_printf("Please enable CONFIG_ESP_SYSTEM_USE_FRAME_POINTER option to have a full backtrace.\r\n");
#endif // CONFIG_ESP_SYSTEM_USE_EH_FRAME
return ESP_OK;

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -24,6 +24,10 @@
#include "esp_private/cache_utils.h"
#endif
#if CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
#include "esp_private/fp_unwind.h"
#endif
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
@ -305,6 +309,7 @@ void panic_arch_fill_info(void *frame, panic_info_t *info)
info->addr = (void *) regs->mepc;
}
#if !CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
static void panic_print_basic_backtrace(const void *frame, int core)
{
// Basic backtrace
@ -322,6 +327,7 @@ static void panic_print_basic_backtrace(const void *frame, int core)
}
}
}
#endif
void panic_print_backtrace(const void *frame, int core)
{
@ -333,6 +339,8 @@ void panic_print_backtrace(const void *frame, int core)
} else {
esp_eh_frame_print_backtrace(frame);
}
#elif CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
esp_fp_print_backtrace(frame);
#else
panic_print_basic_backtrace(frame, core);
#endif

View File

@ -29,14 +29,12 @@
#include "riscv/rvruntime-frames.h"
#endif //CONFIG_IDF_TARGET_ARCH_RISCV
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
#include "esp_private/eh_frame_parser.h"
#endif // CONFIG_ESP_SYSTEM_USE_EH_FRAME
#if CONFIG_IDF_TARGET_ARCH_RISCV && !CONFIG_ESP_SYSTEM_USE_EH_FRAME
/* Function used to print all the registers pointed by the given frame .*/
extern void panic_print_registers(const void *frame, int core);
#endif // CONFIG_IDF_TARGET_ARCH_RISCV && !CONFIG_ESP_SYSTEM_USE_EH_FRAME
#if CONFIG_ESP_SYSTEM_NO_BACKTRACE
/* If the target doesn't support backtrace, we will show CPU registers*/
#define BACKTRACE_MSG "registers"
#else // !CONFIG_ESP_SYSTEM_NO_BACKTRACE
#define BACKTRACE_MSG "backtrace"
#endif
/* We will use this function in order to simulate an `abort()` occurring in
* a different context than the one it's called from. */
@ -390,13 +388,11 @@ void task_wdt_timeout_abort(bool current_core)
g_twdt_isr = true;
void *frame = (void *) snapshot.pxTopOfStack;
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME | CONFIG_IDF_TARGET_ARCH_XTENSA
if (current_core) {
ESP_EARLY_LOGE(TAG, "Print CPU %d (current core) backtrace", xPortGetCoreID());
ESP_EARLY_LOGE(TAG, "Print CPU %d (current core) " BACKTRACE_MSG, xPortGetCoreID());
} else {
ESP_EARLY_LOGE(TAG, "Print CPU %d backtrace", xPortGetCoreID());
ESP_EARLY_LOGE(TAG, "Print CPU %d " BACKTRACE_MSG, xPortGetCoreID());
}
#endif
xt_unhandled_exception(frame);
}
@ -412,7 +408,7 @@ static void task_wdt_timeout_handling(int cores_fail, bool panic)
if ((cores_fail & BIT(0)) && (cores_fail & BIT(1))) {
/* In the case where both CPUs have failing tasks, print the current CPU backtrace and then let the
* other core fail. */
ESP_EARLY_LOGE(TAG, "Print CPU %d (current core) backtrace", current_core);
ESP_EARLY_LOGE(TAG, "Print CPU %d (current core) " BACKTRACE_MSG, current_core);
esp_backtrace_print(100);
/* TODO: the interrupt we send should have the highest priority */
esp_crosscore_int_send_twdt_abort(other_core);
@ -430,13 +426,13 @@ static void task_wdt_timeout_handling(int cores_fail, bool panic)
} else {
/* Print backtrace of the core that failed to reset the watchdog */
if (cores_fail & BIT(current_core)) {
ESP_EARLY_LOGE(TAG, "Print CPU %d (current core) backtrace", current_core);
ESP_EARLY_LOGE(TAG, "Print CPU %d (current core) " BACKTRACE_MSG, current_core);
esp_backtrace_print(100);
}
#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
const int other_core = !current_core;
if (cores_fail & BIT(other_core)) {
ESP_EARLY_LOGE(TAG, "Print CPU %d backtrace", other_core);
ESP_EARLY_LOGE(TAG, "Print CPU %d " BACKTRACE_MSG, other_core);
esp_crosscore_int_send_print_backtrace(other_core);
}
#endif // !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -11,16 +11,16 @@
#include <stdlib.h>
#include "unity.h"
#include "test_utils.h"
#include "esp_rom_sys.h"
#include "esp_rom_uart.h"
#if __XTENSA__
#if CONFIG_IDF_TARGET_ARCH_XTENSA
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "xtensa_api.h" // Replace with interrupt allocator API (IDF-3891)
#include "esp_debug_helpers.h"
#include "esp_intr_alloc.h"
#include "esp_rom_sys.h"
#include "esp_rom_uart.h"
#include "hal/misc.h"
#define SW_ISR_LEVEL_1 7
@ -145,4 +145,55 @@ TEST_CASE("Test esp_backtrace_print_all_tasks()", "[esp_system]")
}
}
#endif // CONFIG_IDF_TARGET_ARCH_XTENSA
#if CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
void my_putc(char c)
{
static bool first_exec = 1;
esp_rom_output_putc(c);
if (first_exec) {
first_exec = false;
*((int*) 1) = 0;
}
}
// TEST_CASE("Test backtrace detects corrupted frames", "[esp_system]")
TEST_CASE("Backtrace detects ROM functions", "[esp_system]")
{
esp_rom_install_channel_putc(1, my_putc);
esp_rom_printf("foo");
}
static void __attribute__((naked)) non_framed_function(void)
{
asm volatile(
"add sp, sp, -16\n"
/* Save anything on the top of the stack, not the frame pointer nor RA */
"sw zero, 12(sp)\n"
"sw s0, 8(sp)\n"
/* Set s0 to a constant that cannot be interpreted as an address */
"li s0, 0x42\n"
/* Fail to trigger an exception */
"sw s0, (s0)\n"
"ret\n"
::
);
}
/**
* Test that backtracing detects when the frame is it unwinding encounters a function (or routine)
* that was not compiles with frame pointer option. This does NOT guarantee that all the call stack
* will be valid (some data pointers may be interpreted as frames), but it guarantees that no
* exception will be triggered.
*/
TEST_CASE("Backtrace detects corrupted frames", "[esp_system]")
{
/* Add some prints to make sure the compiler doesn't optimize it with a tail call */
non_framed_function();
printf("ERROR: must not reach this point\n");
}
#endif

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@ -32,3 +32,27 @@ def test_stack_smash_protection(dut: Dut) -> None:
dut.write('"stack smashing protection"')
dut.expect_exact('Stack smashing protect failure!')
dut.expect_exact('Rebooting...')
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
# Testing this feature on a single RISC-V target is enough
pytest.param('framepointer', marks=[pytest.mark.esp32c3]),
]
)
def test_frame_pointer_backtracing(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('"Backtrace detects corrupted frames"')
dut.expect_exact('Guru Meditation Error')
# The backtrace should be composed of a single entry
dut.expect(r'Backtrace: 0x[0-9a-f]{8}:0x[0-9a-f]{8}\s*[\r]?\n')
dut.expect_exact('Rebooting...')
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('"Backtrace detects ROM functions"')
dut.expect_exact('Guru Meditation Error')
# The backtrace should have two entries
dut.expect(r'Backtrace: 0x[0-9a-f]{8}:0x[0-9a-f]{8} 0x[0-9a-f]{8}:0x[0-9a-f]{8}\s*[\r]?\n')
dut.expect_exact('Rebooting...')

View File

@ -0,0 +1 @@
CONFIG_ESP_SYSTEM_USE_FRAME_POINTER=y

View File

@ -31,15 +31,14 @@ menu "Heap memory debugging"
bool "Disabled"
config HEAP_TRACING_STANDALONE
bool "Standalone"
select HEAP_TRACING
config HEAP_TRACING_TOHOST
bool "Host-based"
select HEAP_TRACING
endchoice
config HEAP_TRACING
bool
default F
default n if HEAP_TRACING_OFF
default y if !HEAP_TRACING_OFF
help
Enables/disables heap tracing API.
@ -76,8 +75,8 @@ menu "Heap memory debugging"
config HEAP_TRACING_STACK_DEPTH
int "Heap tracing stack depth"
range 0 0 if IDF_TARGET_ARCH_RISCV # Disabled for RISC-V due to `__builtin_return_address` limitation
default 0 if IDF_TARGET_ARCH_RISCV
range 0 0 if IDF_TARGET_ARCH_RISCV && !ESP_SYSTEM_USE_FRAME_POINTER
default 0 if IDF_TARGET_ARCH_RISCV && !ESP_SYSTEM_USE_FRAME_POINTER
range 0 32
default 2
depends on HEAP_TRACING

View File

@ -4,13 +4,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <sdkconfig.h>
#include "sdkconfig.h"
#include <inttypes.h>
#include "esp_log.h"
#define HEAP_TRACE_SRCFILE /* don't warn on inclusion here */
#include "esp_heap_trace.h"
#undef HEAP_TRACE_SRCFILE
#include "esp_heap_caps.h"
#include "esp_attr.h"
#include "freertos/FreeRTOS.h"
@ -22,8 +20,6 @@ static __attribute__((unused)) const char* TAG = "heaptrace";
#define STACK_DEPTH CONFIG_HEAP_TRACING_STACK_DEPTH
#if CONFIG_HEAP_TRACING_STANDALONE
typedef enum {
TRACING_STARTED, // start recording allocs and free
TRACING_STOPPED, // stop recording allocs and free
@ -672,5 +668,3 @@ static HEAP_IRAM_ATTR void list_find_and_remove(void* p)
}
#include "heap_trace.inc"
#endif // CONFIG_HEAP_TRACING_STANDALONE

View File

@ -15,10 +15,6 @@
extern "C" {
#endif
#if !defined(CONFIG_HEAP_TRACING) && !defined(HEAP_TRACE_SRCFILE)
#warning "esp_heap_trace.h is included but heap tracing is disabled in menuconfig, functions are no-ops"
#endif
#ifndef CONFIG_HEAP_TRACING_STACK_DEPTH
#define CONFIG_HEAP_TRACING_STACK_DEPTH 0
#endif

View File

@ -1,18 +1,10 @@
// 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.
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <sdkconfig.h>
#include "sdkconfig.h"
#include "soc/soc_memory_layout.h"
#include "esp_attr.h"
#include "esp_cpu.h"
@ -31,11 +23,8 @@ inline static uint32_t get_ccount(void)
/* Architecture-specific return value of __builtin_return_address which
* should be interpreted as an invalid address.
*/
#ifdef __XTENSA__
#if CONFIG_IDF_TARGET_ARCH_XTENSA
#define HEAP_ARCH_INVALID_PC 0x40000000
#else
#define HEAP_ARCH_INVALID_PC 0x00000000
#endif
// Caller is 2 stack frames deeper than we care about
#define STACK_OFFSET 2
@ -94,6 +83,27 @@ static HEAP_IRAM_ATTR __attribute__((noinline)) void get_call_stack(void **calle
TEST_STACK(31);
}
#else // !CONFIG_IDF_TARGET_ARCH_XTENSA
extern uint32_t esp_fp_get_callers(uint32_t frame, void** callers, void** stacks, uint32_t depth);
static HEAP_IRAM_ATTR __attribute__((noinline)) void get_call_stack(void **callers)
{
uint32_t fp = (uint32_t) __builtin_frame_address(0);
memset(callers, 0, sizeof(void *) * STACK_DEPTH);
#if CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
/* We can skip the current return address since this function won't be inlined */
esp_fp_get_callers(fp, callers, NULL, STACK_DEPTH);
#else
/* RISC-V compiler doesn't support `__builtin_frame_address` with a parameter bigger than 0 */
callers[0] = (void*) fp;
#endif
}
#endif
ESP_STATIC_ASSERT(STACK_DEPTH >= 0 && STACK_DEPTH <= 32, "CONFIG_HEAP_TRACING_STACK_DEPTH must be in range 0-32");
typedef enum {

View File

@ -94,17 +94,13 @@ def test_heap_misc_options(dut: Dut) -> None:
@pytest.mark.generic
@pytest.mark.parametrize(
'target',
[
'esp32',
]
)
@pytest.mark.parametrize(
'config',
[
'heap_trace',
'heap_trace_hashmap'
pytest.param('heap_trace_esp32', marks=[pytest.mark.esp32]),
pytest.param('heap_trace_hashmap_esp32', marks=[pytest.mark.esp32]),
pytest.param('heap_trace_esp32c3', marks=[pytest.mark.esp32c3]),
pytest.param('heap_trace_hashmap_esp32c3', marks=[pytest.mark.esp32c3])
]
)
def test_heap_trace_dump(dut: Dut) -> None:

View File

@ -0,0 +1,3 @@
CONFIG_IDF_TARGET="esp32c3"
CONFIG_ESP_SYSTEM_USE_FRAME_POINTER=y
CONFIG_HEAP_TRACING_STANDALONE=y

View File

@ -0,0 +1,5 @@
CONFIG_IDF_TARGET="esp32c3"
CONFIG_ESP_SYSTEM_USE_FRAME_POINTER=y
CONFIG_HEAP_TRACING_STANDALONE=y
CONFIG_HEAP_TRACE_HASH_MAP=y
CONFIG_HEAP_TRACE_HASH_MAP_SIZE=10

View File

@ -234,7 +234,7 @@ If :doc:`IDF Monitor <tools/idf-monitor>` is used, Program Counter values will b
#5 0x00000000 in ?? ()
Backtrace stopped: frame did not save the PC
While the backtrace above is very handy, it requires the user to use :doc:`IDF Monitor <tools/idf-monitor>`. Thus, in order to generate and print a backtrace while using another monitor program, it is possible to activate :ref:`CONFIG_ESP_SYSTEM_USE_EH_FRAME` option from the menuconfig.
While the backtrace above is very handy, it requires the user to use :doc:`IDF Monitor <tools/idf-monitor>`. Thus, in order to generate and print a backtrace while using another monitor program, it is possible to activate ``CONFIG_ESP_SYSTEM_USE_EH_FRAME`` option from the menuconfig, under the "Backtracing method" menu.
This option will let the compiler generate DWARF information for each function of the project. Then, when a CPU exception occurs, the panic handler will parse these data and determine the backtrace of the task that failed. The output looks like this:
@ -245,7 +245,13 @@ If :doc:`IDF Monitor <tools/idf-monitor>` is used, Program Counter values will b
These ``PC:SP`` pairs represent the PC (Program Counter) and SP (Stack Pointer) for each stack frame of the current task.
The main benefit of the :ref:`CONFIG_ESP_SYSTEM_USE_EH_FRAME` option is that the backtrace is generated by the board itself (without the need for :doc:`IDF Monitor <tools/idf-monitor>`). However, the option's drawback is that it results in an increase of the compiled binary's size (ranging from 20% to 100% increase in size). Furthermore, this option causes debug information to be included within the compiled binary. Therefore, users are strongly advised not to enable this option in mass/final production builds.
The main benefit of the ``CONFIG_ESP_SYSTEM_USE_EH_FRAME`` option is that the backtrace is generated by the board itself (without the need for :doc:`IDF Monitor <tools/idf-monitor>`). However, the option's drawback is that it results in an increase of the compiled binary's size (ranging from 20% to 100% increase in size). Furthermore, this option causes debug information to be included within the compiled binary. Therefore, users are strongly advised not to enable this option in mass/final production builds.
Another option to generate such backtrace on the device itself is to enable ``CONFIG_ESP_SYSTEM_USE_FRAME_POINTER`` option from the menuconfig, under the "Backtracing method" menu.
This option will let the compiler reserve a CPU register that keeps track of the frame of each routine of the program. This registers makes it possible for the panic handler to unwind the call stack at any given time, and more importantly, when a CPU exception occurs.
Enabling ``CONFIG_ESP_SYSTEM_USE_FRAME_POINTER`` option will result in an increase of the compiled binary's size of around +5-6% and a performance decrease of around 1%. Contrarily to the ``CONFIG_ESP_SYSTEM_USE_EH_FRAME`` option, the compiler won't generate debug information in the generated binary, so it is possible to use this feature in mass/final production builds.
To find the location where a fatal error has happened, look at the lines which follow the "Backtrace" line. Fatal error location is the top line, and subsequent lines show the call stack.

View File

@ -269,79 +269,6 @@ The following code snippet demonstrates how application code would typically ini
The output from the heap trace has a similar format to the following example:
.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA
.. code-block:: none
====== Heap Trace: 8 records (8 capacity) ======
6 bytes (@ 0x3fc9f620, Internal) allocated CPU 0 ccount 0x1a31ac84 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
9 bytes (@ 0x3fc9f630, Internal) allocated CPU 0 ccount 0x1a31b618 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
12 bytes (@ 0x3fc9f640, Internal) allocated CPU 0 ccount 0x1a31bfac caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
15 bytes (@ 0x3fc9f650, Internal) allocated CPU 0 ccount 0x1a31c940 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
18 bytes (@ 0x3fc9f664, Internal) allocated CPU 0 ccount 0x1a31d2d4 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
21 bytes (@ 0x3fc9f67c, Internal) allocated CPU 0 ccount 0x1a31dc68 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
24 bytes (@ 0x3fc9f698, Internal) allocated CPU 0 ccount 0x1a31e600 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
6 bytes (@ 0x3fc9f6b4, Internal) allocated CPU 0 ccount 0x1a320698 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
====== Heap Trace Summary ======
Mode: Heap Trace All
6 bytes alive in trace (1/8 allocations)
records: 8 (8 capacity, 8 high water mark)
total allocations: 9
total frees: 8
================================
.. only:: CONFIG_IDF_TARGET_ARCH_RISCV
.. code-block:: none
@ -363,6 +290,79 @@ The output from the heap trace has a similar format to the following example:
total frees: 8
================================
Or the following example, when the ``CONFIG_ESP_SYSTEM_USE_FRAME_POINTER`` option is enabled and the stack depth is configured properly:
.. code-block:: none
====== Heap Trace: 8 records (8 capacity) ======
6 bytes (@ 0x3fc9f620, Internal) allocated CPU 0 ccount 0x1a31ac84 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
9 bytes (@ 0x3fc9f630, Internal) allocated CPU 0 ccount 0x1a31b618 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
12 bytes (@ 0x3fc9f640, Internal) allocated CPU 0 ccount 0x1a31bfac caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
15 bytes (@ 0x3fc9f650, Internal) allocated CPU 0 ccount 0x1a31c940 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
18 bytes (@ 0x3fc9f664, Internal) allocated CPU 0 ccount 0x1a31d2d4 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
21 bytes (@ 0x3fc9f67c, Internal) allocated CPU 0 ccount 0x1a31dc68 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
24 bytes (@ 0x3fc9f698, Internal) allocated CPU 0 ccount 0x1a31e600 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
freed by 0x403839e4:0x42008096
0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)
6 bytes (@ 0x3fc9f6b4, Internal) allocated CPU 0 ccount 0x1a320698 caller 0x40376321:0x40376379
0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110
====== Heap Trace Summary ======
Mode: Heap Trace All
6 bytes alive in trace (1/8 allocations)
records: 8 (8 capacity, 8 high water mark)
total allocations: 9
total frees: 8
================================
.. note::
The above example output uses :doc:`IDF Monitor </api-guides/tools/idf-monitor>` to automatically decode PC addresses to their source files and line numbers.
@ -396,6 +396,10 @@ In ``HEAP_TRACE_ALL``:
The depth of the call stack recorded for each trace entry can be configured in the project configuration menu, under ``Heap Memory Debugging`` > ``Enable heap tracing`` > :ref:`CONFIG_HEAP_TRACING_STACK_DEPTH`. Up to 32 stack frames can be recorded for each allocation (the default is 2). Each additional stack frame increases the memory usage of each ``heap_trace_record_t`` record by eight bytes.
.. only:: CONFIG_IDF_TARGET_ARCH_RISCV
By default, the depth of the call stack recorded for each trace entry is 0, which means that only the direct caller of the memory allocation function can be retrieve. However, when the ``CONFIG_ESP_SYSTEM_USE_FRAME_POINTER`` option is enabled, this call stack depth can be configured in the project configuration menu, under ``Heap Memory Debugging`` > ``Enable heap tracing`` > :ref:`CONFIG_HEAP_TRACING_STACK_DEPTH`. Up to 32 stack frames can be recorded for each allocation (the default is 2). Each additional stack frame increases the memory usage of each ``heap_trace_record_t`` record by eight bytes.
Finally, the total number of the 'leaked' bytes (bytes allocated but not freed while the trace is running) is printed together with the total number of allocations it represents.
Using hashmap for increased performance

View File

@ -234,7 +234,7 @@
#5 0x00000000 in ?? ()
Backtrace stopped: frame did not save the PC
虽然以上的回溯信息非常方便,但要求用户使用 :doc:`IDF 监视器 <tools/idf-monitor>`。因此,如果用户希望使用其它的串口监控软件也能显示堆栈回溯信息,则需要在 menuconfig 中启用 :ref:`CONFIG_ESP_SYSTEM_USE_EH_FRAME` 选项。
虽然以上的回溯信息非常方便,但要求用户使用 :doc:`IDF 监视器 <tools/idf-monitor>`。因此,如果用户希望使用其它的串口监控软件也能显示堆栈回溯信息,则需要在 menuconfig 中启用 ``CONFIG_ESP_SYSTEM_USE_EH_FRAME`` 选项。
该选项会让编译器为项目的每个函数生成 DWARF 信息。然后,当 CPU 异常发生时,紧急处理程序将解析这些数据并生成出错任务的堆栈回溯信息。输出结果如下:
@ -245,7 +245,7 @@
这些 ``PC:SP`` 对代表当前任务每一个栈帧的程序计数器值 (Program Counter) 和栈顶地址 (Stack Pointer)。
:ref:`CONFIG_ESP_SYSTEM_USE_EH_FRAME` 选项的主要优点是,回溯信息可以由程序自己解析生成并打印(而不依靠 :doc:`tools/idf-monitor`)。但是该选项会导致编译后的二进制文件更大(增幅可达 20% 甚至 100%)。此外,该选项会将调试信息也保存在二进制文件里。因此,强烈不建议用户在量产/生产版本中启用该选项。
``CONFIG_ESP_SYSTEM_USE_EH_FRAME`` 选项的主要优点是,回溯信息可以由程序自己解析生成并打印(而不依靠 :doc:`tools/idf-monitor`)。但是该选项会导致编译后的二进制文件更大(增幅可达 20% 甚至 100%)。此外,该选项会将调试信息也保存在二进制文件里。因此,强烈不建议用户在量产/生产版本中启用该选项。
若要查找发生严重错误的代码位置,请查看 "Backtrace" 的后面几行,发生严重错误的代码显示在顶行,后续几行显示的是调用堆栈。

View File

@ -53,7 +53,7 @@ static void alloc_task(void *p)
snprintf(task_name, sizeof(task_name), "free%d", task_args->idx);
xTaskCreatePinnedToCore(free_task, task_name, 2500, queue, 5, NULL, CONFIG_FREERTOS_NUMBER_OF_CORES-1);
// here GDB will stop at brekpoint and execute OpenOCD command to start tracing
// here GDB will stop at breakpoint and execute OpenOCD command to start tracing
for(int i = 1; i < 10; i++) {
uint32_t sz = 2*i*(task_args->idx + 1);
void *p = malloc(sz/2);
@ -100,6 +100,6 @@ void app_main(void)
uint32_t val = ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
ESP_LOGI(TAG, "Got notify val %"PRIu32, val);
}
// here GDB will stop at brekpoint and execute OpenOCD command to stop tracing
// here GDB will stop at breakpoint and execute OpenOCD command to stop tracing
heap_trace_stop();
}

View File

@ -24,3 +24,5 @@ CONFIG_APPTRACE_SV_EVT_TIMER_EXIT_ENABLE=y
CONFIG_LOG_COLORS=n
# Enable heap tracing to host
CONFIG_HEAP_TRACING_TOHOST=y
# For RISC-V targets
CONFIG_ESP_SYSTEM_USE_FRAME_POINTER=y

View File

@ -75,6 +75,8 @@ void test_setup_coredump_summary(void);
void test_coredump_summary(void);
#endif
void test_panic_print_backtrace(void);
#ifdef __cplusplus
}
#endif

View File

@ -119,6 +119,9 @@ void app_main(void)
HANDLE_TEST(test_name, test_assert_cache_disabled);
HANDLE_TEST(test_name, test_assert_cache_write_back_error_can_print_backtrace);
HANDLE_TEST(test_name, test_tcb_corrupted);
#if CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
HANDLE_TEST(test_name, test_panic_print_backtrace);
#endif
#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF
HANDLE_TEST(test_name, test_setup_coredump_summary);
HANDLE_TEST(test_name, test_coredump_summary);

View File

@ -344,3 +344,34 @@ void test_capture_dram(void)
assert(0);
}
#endif
#if CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
static NOINLINE_ATTR void step3(void) {
printf("Step 3\n");
/* For some reason, the compiler doesn't generate the proper sequence for `panic_abort` function:
* the `ra` register is not saved on the stack upon entry */
abort();
}
static NOINLINE_ATTR void step2(void) {
step3();
printf("Step 2\n");
}
static NOINLINE_ATTR void step1(void) {
step2();
printf("Step 1\n");
}
/**
* @brief Create a stack trace of several functions that will be shown at runtime,
* The functions must not be inlined!
*/
void test_panic_print_backtrace(void)
{
step1();
}
#endif

View File

@ -59,6 +59,11 @@ CONFIGS = [
pytest.param('panic', marks=TARGETS_ALL),
]
CONFIGS_BACKTRACE = [
# One single-core target and one dual-core target is enough
pytest.param('framepointer', marks=[pytest.mark.esp32c3, pytest.mark.esp32p4]),
]
CONFIGS_DUAL_CORE = [
pytest.param('coredump_flash_bin_crc', marks=TARGETS_DUAL_CORE),
pytest.param('coredump_flash_elf_sha', marks=TARGETS_DUAL_CORE),
@ -1090,3 +1095,22 @@ def test_tcb_corrupted(dut: PanicTestDut, target: str, config: str, test_func_na
expected_backtrace=None,
expected_coredump=coredump_pattern
)
@pytest.mark.parametrize('config', CONFIGS_BACKTRACE, indirect=True)
@pytest.mark.generic
def test_panic_print_backtrace(dut: PanicTestDut, config: str, test_func_name: str) -> None:
dut.run_test_func(test_func_name)
regex_pattern = rb'abort\(\) was called at PC [0-9xa-f]+ on core 0'
dut.expect(regex_pattern)
dut.expect_backtrace()
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,
expected_backtrace=None,
expected_coredump=[coredump_pattern]
)

View File

@ -81,7 +81,6 @@ class PanicTestDut(IdfDut):
pass
def expect_backtrace(self, corrupted: bool = False) -> None:
assert self.is_xtensa, 'Backtrace can be printed only on Xtensa'
match = self.expect(r'Backtrace:( 0x[0-9a-fA-F]{8}:0x[0-9a-fA-F]{8})+(?P<corrupted> \|<-CORRUPTED)?')
if corrupted:
assert match.group('corrupted')