diff --git a/src/backtrace/espidf.rs b/src/backtrace/espidf.rs new file mode 100644 index 000000000..d9a26e58b --- /dev/null +++ b/src/backtrace/espidf.rs @@ -0,0 +1,98 @@ +//! Implementation of backtracing for ESP-IDF. + +use core::ffi::c_void; + +#[derive(Clone, Debug)] +pub struct Frame { + ip: *mut c_void, + sp: *mut c_void, +} + +// SAFETY: The pointers returned in this struct can be used from any thread. +unsafe impl Send for Frame {} +unsafe impl Sync for Frame {} + +impl Frame { + pub fn ip(&self) -> *mut c_void { + self.ip + } + + pub fn sp(&self) -> *mut c_void { + self.sp + } + + pub fn symbol_address(&self) -> *mut c_void { + 0 as *mut _ + } + + pub fn module_base_address(&self) -> Option<*mut c_void> { + None + } +} + +// Reference: https://github.com/espressif/esp-idf/blob/master/components/esp_system/include/esp_debug_helpers.h +#[cfg(target_arch = "xtensa")] +#[inline(always)] +pub fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) { + #[repr(C)] + struct esp_backtrace_frame_t { + pc: *mut c_void, // PC of the current frame + sp: *mut c_void, // SP of the current frame + next_pc: *mut c_void, // PC of the current frame's caller + exc_frame: *const c_void, // Pointer to the full frame data structure, if applicable + } + + impl From<&esp_backtrace_frame_t> for Frame { + #[inline(always)] + fn from(esp_frame: &esp_backtrace_frame_t) -> Self { + let mut pc = esp_frame.pc as u32; + + // Reference: https://github.com/espressif/esp-idf/blob/master/components/esp_hw_support/include/soc/cpu.h#L38 + if (pc & 0x80000000) != 0 { + // Top two bits of a0 (return address) specify window increment. Overwrite to map to address space. + pc = (pc & 0x3fffffff) | 0x40000000; + } + pc = pc - 3; // Minus 3 to get PC of previous instruction (i.e. instruction executed before return address) + + Self { + ip: pc as *mut _, + sp: esp_frame.sp, + } + } + } + + extern "C" { + fn esp_backtrace_get_start( + pc: *mut *mut c_void, + sp: *mut *mut c_void, + next_pc: *mut *mut c_void, + ); + fn esp_backtrace_get_next_frame(frame: *mut esp_backtrace_frame_t) -> bool; + } + + let mut frame: esp_backtrace_frame_t = unsafe { ::core::mem::zeroed() }; + + unsafe { + esp_backtrace_get_start( + &mut frame.pc as *mut _, + &mut frame.sp as *mut _, + &mut frame.next_pc as *mut _, + ) + }; + + cb(&super::Frame { + inner: (&frame).into(), + }); + + while unsafe { esp_backtrace_get_next_frame(&mut frame as *mut _) } { + cb(&super::Frame { + inner: (&frame).into(), + }); + } +} + +#[cfg(not(target_arch = "xtensa"))] +#[inline(always)] +pub fn trace(_cb: &mut dyn FnMut(&super::Frame) -> bool) { + // RiscV is not supported yet +} diff --git a/src/backtrace/mod.rs b/src/backtrace/mod.rs index ca1e9148e..17a0f8691 100644 --- a/src/backtrace/mod.rs +++ b/src/backtrace/mod.rs @@ -137,6 +137,7 @@ cfg_if::cfg_if! { all( unix, not(target_os = "emscripten"), + not(target_os = "espidf"), not(all(target_os = "ios", target_arch = "arm")), ), all( @@ -154,6 +155,10 @@ cfg_if::cfg_if! { pub(crate) use self::dbghelp::Frame as FrameImp; #[cfg(target_env = "msvc")] // only used in dbghelp symbolize pub(crate) use self::dbghelp::StackFrame; + } else if #[cfg(target_os = "espidf")] { + pub(crate) mod espidf; + use self::espidf::trace as trace_imp; + pub(crate) use self::espidf::Frame as FrameImp; } else { mod noop; use self::noop::trace as trace_imp; diff --git a/src/print.rs b/src/print.rs index cc677122a..0b3c25f7c 100644 --- a/src/print.rs +++ b/src/print.rs @@ -191,6 +191,15 @@ impl BacktraceFrameFmt<'_, '_, '_> { // printing addresses in our own format here. if cfg!(target_os = "fuchsia") { self.print_raw_fuchsia(frame_ip)?; + } + // ESP-IDF is unable to symbolize because the firmware running on the chip + // does not have any symbolic information. + // The TTY monitor software however is symbolizing all outputted hexadecimal + // addresses which happen to fall within the memory region where the executed + // firmware is mapped. + // Therefore, it is enough to just print the frame instruction pointer value. + else if cfg!(target_os = "espidf") { + write!(self.fmt.fmt, "{:4}: {:?}", self.fmt.frame_index, frame_ip) } else { self.print_raw_generic(frame_ip, symbol_name, filename, lineno, colno)?; } diff --git a/src/symbolize/mod.rs b/src/symbolize/mod.rs index cf8d90f65..76c885bd1 100644 --- a/src/symbolize/mod.rs +++ b/src/symbolize/mod.rs @@ -474,6 +474,7 @@ cfg_if::cfg_if! { any(unix, windows), not(target_vendor = "uwp"), not(target_os = "emscripten"), + not(target_os = "espidf"), any(not(backtrace_in_libstd), feature = "backtrace"), ))] { mod gimli;