mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
feat(esp_system): add esp_backtrace_print_all_tasks support for RISC-V
Extends esp_backtrace_print_all_tasks() to RISC-V targets. For running tasks, registers are captured using UNW_GET_CONTEXT(); for suspended tasks, the saved RvExcFrame on the task stack is used. Dual-core support follows the same IPC pattern as the Xtensa implementation. Backtrace output adapts to the configured backtrace method: EH-frame, frame-pointer, or register dump (with a hint to enable frame-pointer mode).
This commit is contained in:
@@ -103,10 +103,11 @@ esp_err_t esp_backtrace_print_from_frame(int depth, const esp_backtrace_frame_t*
|
|||||||
*
|
*
|
||||||
* @param depth The maximum number of stack frames to print (should be > 0)
|
* @param depth The maximum number of stack frames to print (should be > 0)
|
||||||
*
|
*
|
||||||
* @note On RISC-V targets printing backtrace at run-time is only available if
|
* @note On RISC-V targets printing a backtrace at run-time is only available if
|
||||||
* CONFIG_ESP_SYSTEM_USE_EH_FRAME is selected. Otherwise we simply print
|
* CONFIG_ESP_SYSTEM_USE_EH_FRAME or CONFIG_ESP_SYSTEM_USE_FRAME_POINTER is
|
||||||
* a register dump. Function assumes it is called in a context where the
|
* selected. Otherwise we simply print a register dump. Function assumes it is
|
||||||
* calling task will not migrate to another core, e.g. interrupts disabled/panic handler.
|
* called in a context where the calling task will not migrate to another core,
|
||||||
|
* e.g. interrupt context or panic handler.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK Backtrace successfully printed to completion or to depth limit
|
* - ESP_OK Backtrace successfully printed to completion or to depth limit
|
||||||
@@ -121,10 +122,19 @@ esp_err_t esp_backtrace_print(int depth);
|
|||||||
*
|
*
|
||||||
* @note Users must ensure that no tasks are created or deleted while this function is running.
|
* @note Users must ensure that no tasks are created or deleted while this function is running.
|
||||||
* @note This function must be called from a task context.
|
* @note This function must be called from a task context.
|
||||||
|
* @note On RISC-V targets, the 'depth' argument is not honored and the output depends on the
|
||||||
|
* selected backtracing method (ESP_BACKTRACING_METHOD):
|
||||||
|
* - CONFIG_ESP_SYSTEM_USE_FRAME_POINTER or CONFIG_ESP_SYSTEM_USE_EH_FRAME: a full
|
||||||
|
* backtrace is printed for each task (the unwinders always unwind to completion).
|
||||||
|
* - Otherwise (no backtracing method selected): only a register dump is printed.
|
||||||
|
* Because the underlying RISC-V print routines do not report frame corruption,
|
||||||
|
* this function does not return ESP_FAIL on corrupt stacks.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK All backtraces successfully printed to completion or to depth limit
|
* - ESP_OK All backtraces printed
|
||||||
* - ESP_FAIL One or more backtraces are corrupt
|
* - ESP_ERR_INVALID_ARG depth was <= 0
|
||||||
|
* - ESP_ERR_NO_MEM Failed to allocate the task snapshot buffer
|
||||||
|
* - ESP_FAIL One or more backtraces are corrupt (Xtensa only)
|
||||||
*/
|
*/
|
||||||
esp_err_t esp_backtrace_print_all_tasks(int depth);
|
esp_err_t esp_backtrace_print_all_tasks(int depth);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -7,6 +7,8 @@
|
|||||||
#ifndef FP_UNWIND_H
|
#ifndef FP_UNWIND_H
|
||||||
#define FP_UNWIND_H
|
#define FP_UNWIND_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@@ -20,6 +22,34 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
void esp_fp_print_backtrace(const void *frame_or);
|
void esp_fp_print_backtrace(const void *frame_or);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate a call stack starting at the given frame pointer.
|
||||||
|
*
|
||||||
|
* @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] Maximum number of entries to fill in the callers and stacks arrays.
|
||||||
|
* Both arrays (when provided) must be able to hold at least this many entries.
|
||||||
|
*
|
||||||
|
* @returns Number of entries filled in the array.
|
||||||
|
*/
|
||||||
|
uint32_t esp_fp_get_callers(uint32_t frame, void** callers, void** stacks, uint32_t depth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Output a single backtrace step to the serial.
|
||||||
|
*
|
||||||
|
* Defined as weak so that it can be overridden by the user, e.g. to capture each
|
||||||
|
* backtrace step and store it or forward it elsewhere instead of printing.
|
||||||
|
*
|
||||||
|
* @warning This function is also used to print the backtrace from the panic handler.
|
||||||
|
* Overriding it therefore changes the backtrace output on a panic as well,
|
||||||
|
* so any override must remain safe to call from a panic/exception context.
|
||||||
|
*
|
||||||
|
* @param pc Program counter of the backtrace step.
|
||||||
|
* @param sp Stack pointer of the backtrace step.
|
||||||
|
*/
|
||||||
|
void esp_fp_generated_step(uint32_t pc, uint32_t sp);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -11,31 +11,70 @@
|
|||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/freertos_debug.h"
|
#include "freertos/freertos_debug.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
|
#include "esp_check.h"
|
||||||
#include "esp_private/esp_system_attr.h"
|
#include "esp_private/esp_system_attr.h"
|
||||||
#include "esp_private/esp_cpu_internal.h"
|
#include "esp_private/esp_cpu_internal.h"
|
||||||
|
#include "esp_rom_sys.h"
|
||||||
|
#include "riscv/rvruntime-frames.h"
|
||||||
|
#include "riscv/libunwind-riscv.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
|
||||||
|
#if !CONFIG_FREERTOS_UNICORE
|
||||||
|
#include "esp_ipc.h"
|
||||||
|
#endif // !CONFIG_FREERTOS_UNICORE
|
||||||
|
|
||||||
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
|
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
|
||||||
#include "esp_private/eh_frame_parser.h"
|
#include "esp_private/eh_frame_parser.h"
|
||||||
|
|
||||||
#elif CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
|
#elif CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
|
||||||
extern void esp_fp_print_backtrace(const void*);
|
#include "esp_private/fp_unwind.h"
|
||||||
|
|
||||||
#else // !CONFIG_ESP_SYSTEM_USE_EH_FRAME && !
|
#else // !CONFIG_ESP_SYSTEM_USE_EH_FRAME && !CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
|
||||||
/* Function used to print all the registers pointed by the given frame .*/
|
|
||||||
extern void panic_print_registers(const void *frame, int core);
|
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.
|
static const char *DEBUG_HELPER_TAG = "DBG HLPR";
|
||||||
* We have two options here:
|
|
||||||
* - Perform backtracing at runtime thanks to the configuration options
|
/**
|
||||||
* CONFIG_ESP_SYSTEM_USE_EH_FRAME and CONFIG_ESP_SYSTEM_USE_FRAME_POINTER.
|
* @brief Print the backtrace (or register dump) for a single saved CPU frame.
|
||||||
* - Let IDF monitor do the backtracing for us. Used during panic already.
|
*
|
||||||
*
|
* Targets based on a RISC-V CPU cannot perform backtracing that easily. Depending on
|
||||||
* In both cases, this takes time, and we might be in an ISR, we must
|
* the configured backtracing method (ESP_BACKTRACING_METHOD) this prints either a full
|
||||||
* exit this handler as fast as possible, then we will simply print
|
* call stack (EH-frame or frame-pointer mode) or, as a fallback, a register dump that
|
||||||
* the interruptee's registers.
|
* can be decoded offline by idf.py monitor.
|
||||||
*/
|
*
|
||||||
|
* @param task_name Name of the task being printed. If NULL, no name line is printed
|
||||||
|
* (used when printing the current stack's backtrace).
|
||||||
|
* @param frame Saved/captured register frame to start backtracing from
|
||||||
|
* @param core_id Core whose CSRs are reflected in the register-dump fallback
|
||||||
|
*/
|
||||||
|
static void ESP_SYSTEM_IRAM_ATTR print_task_backtrace(const char *task_name, const RvExcFrame *frame, int core_id)
|
||||||
|
{
|
||||||
|
if (task_name) {
|
||||||
|
esp_rom_printf("%s\r\n", task_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
|
||||||
|
(void)core_id;
|
||||||
|
esp_eh_frame_print_backtrace(frame);
|
||||||
|
#elif CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
|
||||||
|
(void)core_id;
|
||||||
|
esp_fp_print_backtrace(frame);
|
||||||
|
#else // !CONFIG_ESP_SYSTEM_USE_EH_FRAME && !CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
|
||||||
|
esp_cpu_frame_t backtrace_frame;
|
||||||
|
memcpy(&backtrace_frame, frame, sizeof(esp_cpu_frame_t));
|
||||||
|
panic_prepare_frame_from_ctx(&backtrace_frame);
|
||||||
|
panic_print_registers(&backtrace_frame, core_id);
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: On RISC-V the depth argument is not honored. The frame-pointer and eh_frame
|
||||||
|
* unwinders always unwind to completion, and the register-dump fallback prints no stack.
|
||||||
|
*/
|
||||||
esp_err_t ESP_SYSTEM_IRAM_ATTR esp_backtrace_print(int depth)
|
esp_err_t ESP_SYSTEM_IRAM_ATTR esp_backtrace_print(int depth)
|
||||||
{
|
{
|
||||||
(void)depth;
|
(void)depth;
|
||||||
@@ -49,22 +88,136 @@ esp_err_t ESP_SYSTEM_IRAM_ATTR esp_backtrace_print(int depth)
|
|||||||
return ESP_ERR_NOT_FOUND;
|
return ESP_ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *frame = snapshot.pxTopOfStack;
|
print_task_backtrace(NULL, (const RvExcFrame *)snapshot.pxTopOfStack, current_core);
|
||||||
|
|
||||||
esp_cpu_frame_t backtrace_frame = {};
|
|
||||||
memcpy(&backtrace_frame, frame, sizeof(esp_cpu_frame_t));
|
|
||||||
|
|
||||||
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
|
|
||||||
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
|
|
||||||
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;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
#if !CONFIG_FREERTOS_UNICORE
|
||||||
|
volatile bool start_tracing;
|
||||||
|
volatile bool finished_tracing;
|
||||||
|
#endif // !CONFIG_FREERTOS_UNICORE
|
||||||
|
struct {
|
||||||
|
TaskHandle_t task_hdl;
|
||||||
|
RvExcFrame frame;
|
||||||
|
} cur_tasks[configNUMBER_OF_CORES];
|
||||||
|
} riscv_backtrace_ctrl_t;
|
||||||
|
|
||||||
|
#if !CONFIG_FREERTOS_UNICORE
|
||||||
|
static void backtrace_other_cores_ipc_func(void *arg)
|
||||||
|
{
|
||||||
|
riscv_backtrace_ctrl_t *ctrl = (riscv_backtrace_ctrl_t *)arg;
|
||||||
|
|
||||||
|
vTaskSuspendAll();
|
||||||
|
|
||||||
|
BaseType_t core_id = xPortGetCoreID();
|
||||||
|
ctrl->cur_tasks[core_id].task_hdl = xTaskGetCurrentTaskHandle();
|
||||||
|
UNW_GET_CONTEXT(&ctrl->cur_tasks[core_id].frame);
|
||||||
|
|
||||||
|
/* Ensure the task_hdl and frame writes are visible to the backtracing core
|
||||||
|
before it observes start_tracing == true. */
|
||||||
|
__sync_synchronize();
|
||||||
|
ctrl->start_tracing = true;
|
||||||
|
while (!ctrl->finished_tracing) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
xTaskResumeAll();
|
||||||
|
}
|
||||||
|
#endif // !CONFIG_FREERTOS_UNICORE
|
||||||
|
|
||||||
|
esp_err_t esp_backtrace_print_all_tasks(int depth)
|
||||||
|
{
|
||||||
|
if (depth <= 0) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
TaskSnapshot_t *task_snapshots;
|
||||||
|
riscv_backtrace_ctrl_t ctrl = {0};
|
||||||
|
|
||||||
|
const UBaseType_t num_tasks = uxTaskGetNumberOfTasks();
|
||||||
|
task_snapshots = calloc(num_tasks, sizeof(TaskSnapshot_t));
|
||||||
|
ESP_GOTO_ON_FALSE(task_snapshots, ESP_ERR_NO_MEM, malloc_err, DEBUG_HELPER_TAG, "Task snapshot alloc failed");
|
||||||
|
|
||||||
|
#if !CONFIG_FREERTOS_UNICORE
|
||||||
|
// Use IPC call to prepare other core for backtracing
|
||||||
|
ESP_GOTO_ON_ERROR(esp_ipc_call(!xPortGetCoreID(), backtrace_other_cores_ipc_func, (void *)&ctrl),
|
||||||
|
ipc_err,
|
||||||
|
DEBUG_HELPER_TAG,
|
||||||
|
"IPC call failed");
|
||||||
|
// Wait for other core to confirm it is ready for backtracing
|
||||||
|
while (!ctrl.start_tracing) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
#endif // !CONFIG_FREERTOS_UNICORE
|
||||||
|
|
||||||
|
// Suspend the scheduler to prevent task switching
|
||||||
|
vTaskSuspendAll();
|
||||||
|
|
||||||
|
// Capture the current core's running task context now that task switching is disabled
|
||||||
|
const BaseType_t cur_core_id = xPortGetCoreID();
|
||||||
|
ctrl.cur_tasks[cur_core_id].task_hdl = xTaskGetCurrentTaskHandle();
|
||||||
|
UNW_GET_CONTEXT(&ctrl.cur_tasks[cur_core_id].frame);
|
||||||
|
|
||||||
|
// Get snapshot of all tasks in the system
|
||||||
|
UBaseType_t snapshot_count = uxTaskGetSnapshotAll(task_snapshots, num_tasks, NULL);
|
||||||
|
const UBaseType_t num_snapshots = MIN(num_tasks, snapshot_count);
|
||||||
|
|
||||||
|
// Print the backtrace of every task in the system
|
||||||
|
for (UBaseType_t task_idx = 0; task_idx < num_snapshots; task_idx++) {
|
||||||
|
TaskHandle_t task_hdl = (TaskHandle_t)task_snapshots[task_idx].pxTCB;
|
||||||
|
const RvExcFrame *frame_ptr = NULL;
|
||||||
|
RvExcFrame saved_frame;
|
||||||
|
|
||||||
|
// Check if the task is one of the currently running tasks
|
||||||
|
bool cur_running = false;
|
||||||
|
BaseType_t running_core_id = 0;
|
||||||
|
for (BaseType_t i = 0; i < configNUMBER_OF_CORES; i++) {
|
||||||
|
if (task_hdl == ctrl.cur_tasks[i].task_hdl) {
|
||||||
|
cur_running = true;
|
||||||
|
running_core_id = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur_running) {
|
||||||
|
/*
|
||||||
|
* For the currently running task(s) use the context we captured
|
||||||
|
* via UNW_GET_CONTEXT() rather than pxTopOfStack, which may not
|
||||||
|
* yet reflect the suspended state.
|
||||||
|
*/
|
||||||
|
memcpy(&saved_frame, &ctrl.cur_tasks[running_core_id].frame, sizeof(RvExcFrame));
|
||||||
|
frame_ptr = &saved_frame;
|
||||||
|
} else {
|
||||||
|
// Set the starting backtrace frame using the task's saved stack pointer
|
||||||
|
frame_ptr = (const RvExcFrame *)task_snapshots[task_idx].pxTopOfStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The register-dump fallback reflects the CSRs of the core executing this
|
||||||
|
* function, so report the current core for the dump's "Core N" label.
|
||||||
|
*/
|
||||||
|
char *name = pcTaskGetName(task_hdl);
|
||||||
|
print_task_backtrace(name ? name : "No Name", frame_ptr, (int)cur_core_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume the scheduler to allow task switching again
|
||||||
|
xTaskResumeAll();
|
||||||
|
|
||||||
|
#if !CONFIG_FREERTOS_UNICORE
|
||||||
|
// Indicate to the other core that backtracing is complete
|
||||||
|
ctrl.finished_tracing = true;
|
||||||
|
#endif // !CONFIG_FREERTOS_UNICORE
|
||||||
|
|
||||||
|
free(task_snapshots);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
#if !CONFIG_FREERTOS_UNICORE
|
||||||
|
ipc_err:
|
||||||
|
free(task_snapshots);
|
||||||
|
#endif // !CONFIG_FREERTOS_UNICORE
|
||||||
|
malloc_err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -147,6 +147,39 @@ TEST_CASE("Test esp_backtrace_print_all_tasks()", "[esp_system]")
|
|||||||
|
|
||||||
#endif // CONFIG_IDF_TARGET_ARCH_XTENSA
|
#endif // CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ARCH_RISCV
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_debug_helpers.h"
|
||||||
|
|
||||||
|
#define NUM_TEST_FUNCS_RISCV 2
|
||||||
|
|
||||||
|
static void backtrace_suspend_func_riscv(void *arg)
|
||||||
|
{
|
||||||
|
vTaskSuspend(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test esp_backtrace_print_all_tasks() on RISC-V", "[esp_system]")
|
||||||
|
{
|
||||||
|
TaskHandle_t task_handles[NUM_TEST_FUNCS_RISCV];
|
||||||
|
|
||||||
|
// An invalid depth must be rejected
|
||||||
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_backtrace_print_all_tasks(0));
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_TEST_FUNCS_RISCV; i++) {
|
||||||
|
xTaskCreate(backtrace_suspend_func_riscv, "trace_func", 2048, NULL, UNITY_FREERTOS_PRIORITY + i + 1, &task_handles[i]);
|
||||||
|
}
|
||||||
|
vTaskDelay(10);
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, esp_backtrace_print_all_tasks(3));
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_TEST_FUNCS_RISCV; i++) {
|
||||||
|
vTaskDelete(task_handles[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONFIG_IDF_TARGET_ARCH_RISCV
|
||||||
|
|
||||||
#if CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
|
#if CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
|
||||||
|
|
||||||
void my_putc(char c)
|
void my_putc(char c)
|
||||||
|
|||||||
+15
@@ -184,3 +184,18 @@ def test_frame_pointer_backtracing(dut: Dut) -> None:
|
|||||||
# The backtrace should have two entries
|
# 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(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...')
|
dut.expect_exact('Rebooting...')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic
|
||||||
|
@idf_parametrize('config', ['framepointer'], indirect=['config'])
|
||||||
|
@idf_parametrize('target', ['esp32s31'], indirect=['target'])
|
||||||
|
def test_print_all_tasks_backtracing(dut: Dut) -> None:
|
||||||
|
# With frame-pointer mode enabled, esp_backtrace_print_all_tasks() should print a
|
||||||
|
# valid backtrace for each task. Run on a multi-core RISC-V target so the dual-core
|
||||||
|
# IPC path is exercised: the task running on each core is captured via UNW_GET_CONTEXT,
|
||||||
|
# so we expect a well-formed "Backtrace:" line for each of the cores.
|
||||||
|
dut.expect_exact('Press ENTER to see the list of tests')
|
||||||
|
dut.write('"Test esp_backtrace_print_all_tasks() on RISC-V"')
|
||||||
|
for _ in range(2):
|
||||||
|
dut.expect(r'Backtrace: 0x[0-9a-f]{8}:0x[0-9a-f]{8}')
|
||||||
|
dut.expect_unity_test_output()
|
||||||
|
|||||||
Reference in New Issue
Block a user