Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions components/esp_system/include/esp_debug_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ extern "C" {
#include "esp_err.h"
#include "esp_cpu.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

/*
* @brief Structure used for backtracing
*
Expand Down Expand Up @@ -129,6 +132,8 @@ esp_err_t esp_backtrace_print(int depth);
*/
esp_err_t esp_backtrace_print_all_tasks(int depth);

esp_err_t esp_backtrace_print_task(TaskHandle_t pxTask, int depth, bool panic);

/**
* @brief Set a watchpoint to break/panic when a certain memory range is accessed.
* Superseded by esp_cpu_set_watchpoint in esp_cpu.h.
Expand Down
67 changes: 67 additions & 0 deletions components/esp_system/port/arch/xtensa/debug_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,70 @@ esp_err_t IRAM_ATTR esp_backtrace_print_all_tasks(int depth)
malloc_err:
return ret;
}

esp_err_t esp_backtrace_print_task(TaskHandle_t pxTask, int depth, bool panic)
{
esp_err_t ret = ESP_OK;
TaskSnapshot_t task_snapshot;
cur_task_backtrace_ctrl_t ctrl = {0};

// Suspend the scheduler to prevent task switching
vTaskSuspend(pxTask);

/*
Initialize backtracing for this core:

- Flush current core's register windows back onto current task's stack using esp_backtrace_get_start()
- Get starting frame for backtracing (starting frame is the caller of this function) using esp_backtrace_get_start()
- Save the starting frame details into the control block
*/
BaseType_t core_id = xPortGetCoreID(); // Get core ID now that task switching is disabled
ctrl.cur_tasks[core_id].task_hdl = xTaskGetCurrentTaskHandle();
esp_backtrace_get_start(&ctrl.cur_tasks[core_id].starting_pc,
&ctrl.cur_tasks[core_id].starting_sp,
&ctrl.cur_tasks[core_id].next_pc);

vTaskGetSnapshot(pxTask, &task_snapshot);

// Print the backtrace of the task
bool cur_running = false;
TaskHandle_t task_hdl = (TaskHandle_t) task_snapshot.pxTCB;
esp_backtrace_frame_t stk_frame = {0};

// Check if the task is one of the currently running tasks
for (BaseType_t core_id = 0; core_id < configNUMBER_OF_CORES; core_id++) {
if (task_hdl == ctrl.cur_tasks[core_id].task_hdl) {
cur_running = true;
break;
}
}
// Initialize the starting backtrace frame of the task
if (cur_running) {
/*
Setting the starting backtrace frame for currently running tasks is different. We cannot
use the current frame of each running task as the starting frame (due to the possibility
of the SP changing). Thus, each currently running task will have initialized their callers
as the starting frame for backtracing, which is saved inside the
cur_task_backtrace_ctrl_t block.
*/
stk_frame.pc = ctrl.cur_tasks[core_id].starting_pc;
stk_frame.sp = ctrl.cur_tasks[core_id].starting_sp;
stk_frame.next_pc = ctrl.cur_tasks[core_id].next_pc;
} else {
// Set the starting backtrace frame using the task's saved stack pointer
XtExcFrame* exc_frame = (XtExcFrame*) task_snapshot.pxTopOfStack;
stk_frame.pc = exc_frame->pc;
stk_frame.sp = exc_frame->a1;
stk_frame.next_pc = exc_frame->a0;
}
// Print backtrace
esp_err_t bt_ret = esp_backtrace_print_from_frame(depth, &stk_frame, panic);
if (bt_ret != ESP_OK) {
ret = bt_ret;
}

// Resume the scheduler to allow task switching again
vTaskResume(pxTask);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Task Management and Core ID Issues

Calling vTaskSuspend() and vTaskResume() from an ISR context is unsafe and can cause system crashes. Separately, core_id variable shadowing combined with ctrl.cur_tasks being initialized only for the current core leads to incorrect task detection and access to uninitialized data for tasks on other cores, resulting in invalid backtraces.

Fix in Cursor Fix in Web


return ret;
}
3 changes: 3 additions & 0 deletions components/esp_system/task_wdt/task_wdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,10 @@ esp_err_t esp_task_wdt_print_triggered_tasks(task_wdt_msg_handler msg_handler, v
msg_handler(opaque, name);
msg_handler(opaque, cpu);
}

esp_backtrace_print_task(entry->task_handle, 100, true);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Backtrace in WDT ISR Causes System Instability

Calling esp_backtrace_print_task from the task_wdt_isr is problematic. It uses non-ISR-safe FreeRTOS calls (vTaskSuspend, vTaskResume) and is passed entry->task_handle, which can be NULL for user entries. This can lead to system crashes or undefined behavior.

Fix in Cursor Fix in Web

}
}

return ESP_OK;
}