2025-01-09 11:57:02 +08:00

118 lines
3.5 KiB
C

/*
* 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");
}