Skip to content

Commit 2ede3b0

Browse files
author
imarkov
committed
Backtrace support for the ESP-IDF framework
1 parent 4f925f8 commit 2ede3b0

File tree

4 files changed

+113
-0
lines changed

4 files changed

+113
-0
lines changed

src/backtrace/espidf.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//! Implementation of backtracing for ESP-IDF.
2+
3+
use core::ffi::c_void;
4+
5+
#[derive(Clone, Debug)]
6+
pub struct Frame {
7+
ip: *mut c_void,
8+
sp: *mut c_void,
9+
}
10+
11+
// SAFETY: The pointers returned in this struct can be used from any thread.
12+
unsafe impl Send for Frame {}
13+
unsafe impl Sync for Frame {}
14+
15+
impl Frame {
16+
pub fn ip(&self) -> *mut c_void {
17+
self.ip
18+
}
19+
20+
pub fn sp(&self) -> *mut c_void {
21+
self.sp
22+
}
23+
24+
pub fn symbol_address(&self) -> *mut c_void {
25+
0 as *mut _
26+
}
27+
28+
pub fn module_base_address(&self) -> Option<*mut c_void> {
29+
None
30+
}
31+
}
32+
33+
// Reference: https://github.com/espressif/esp-idf/blob/master/components/esp_system/include/esp_debug_helpers.h
34+
#[cfg(target_arch = "xtensa")]
35+
#[inline(always)]
36+
pub fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
37+
#[repr(C)]
38+
struct esp_backtrace_frame_t {
39+
pc: *mut c_void, // PC of the current frame
40+
sp: *mut c_void, // SP of the current frame
41+
next_pc: *mut c_void, // PC of the current frame's caller
42+
exc_frame: *const c_void, // Pointer to the full frame data structure, if applicable
43+
}
44+
45+
impl From<&esp_backtrace_frame_t> for Frame {
46+
#[inline(always)]
47+
fn from(esp_frame: &esp_backtrace_frame_t) -> Self {
48+
let mut pc = esp_frame.pc as u32;
49+
50+
// Reference: https://github.com/espressif/esp-idf/blob/master/components/esp_hw_support/include/soc/cpu.h#L38
51+
if (pc & 0x80000000) != 0 {
52+
// Top two bits of a0 (return address) specify window increment. Overwrite to map to address space.
53+
pc = (pc & 0x3fffffff) | 0x40000000;
54+
}
55+
pc = pc - 3; // Minus 3 to get PC of previous instruction (i.e. instruction executed before return address)
56+
57+
Self {
58+
ip: pc as *mut _,
59+
sp: esp_frame.sp,
60+
}
61+
}
62+
}
63+
64+
extern "C" {
65+
fn esp_backtrace_get_start(
66+
pc: *mut *mut c_void,
67+
sp: *mut *mut c_void,
68+
next_pc: *mut *mut c_void,
69+
);
70+
fn esp_backtrace_get_next_frame(frame: *mut esp_backtrace_frame_t) -> bool;
71+
}
72+
73+
let mut frame: esp_backtrace_frame_t = unsafe { ::core::mem::zeroed() };
74+
75+
unsafe {
76+
esp_backtrace_get_start(
77+
&mut frame.pc as *mut _,
78+
&mut frame.sp as *mut _,
79+
&mut frame.next_pc as *mut _,
80+
)
81+
};
82+
83+
cb(&super::Frame {
84+
inner: (&frame).into(),
85+
});
86+
87+
while unsafe { esp_backtrace_get_next_frame(&mut frame as *mut _) } {
88+
cb(&super::Frame {
89+
inner: (&frame).into(),
90+
});
91+
}
92+
}
93+
94+
#[cfg(not(target_arch = "xtensa"))]
95+
#[inline(always)]
96+
pub fn trace(_cb: &mut dyn FnMut(&super::Frame) -> bool) {
97+
// RiscV is not supported yet
98+
}

src/backtrace/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ cfg_if::cfg_if! {
137137
all(
138138
unix,
139139
not(target_os = "emscripten"),
140+
not(target_os = "espidf"),
140141
not(all(target_os = "ios", target_arch = "arm")),
141142
),
142143
all(
@@ -154,6 +155,10 @@ cfg_if::cfg_if! {
154155
pub(crate) use self::dbghelp::Frame as FrameImp;
155156
#[cfg(target_env = "msvc")] // only used in dbghelp symbolize
156157
pub(crate) use self::dbghelp::StackFrame;
158+
} else if #[cfg(target_os = "espidf")] {
159+
pub(crate) mod espidf;
160+
use self::espidf::trace as trace_imp;
161+
pub(crate) use self::espidf::Frame as FrameImp;
157162
} else {
158163
mod noop;
159164
use self::noop::trace as trace_imp;

src/print.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,15 @@ impl BacktraceFrameFmt<'_, '_, '_> {
191191
// printing addresses in our own format here.
192192
if cfg!(target_os = "fuchsia") {
193193
self.print_raw_fuchsia(frame_ip)?;
194+
}
195+
// ESP-IDF is unable to symbolize because the firmware running on the chip
196+
// does not have any symbolic information.
197+
// The TTY monitor software however is symbolizing all outputted hexadecimal
198+
// addresses which happen to fall within the memory region where the executed
199+
// firmware is mapped.
200+
// Therefore, it is enough to just print the frame instruction pointer value.
201+
else if cfg!(target_os = "espidf") {
202+
write!(self.fmt.fmt, "{:4}: {:?}", self.fmt.frame_index, frame_ip)
194203
} else {
195204
self.print_raw_generic(frame_ip, symbol_name, filename, lineno, colno)?;
196205
}

src/symbolize/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,7 @@ cfg_if::cfg_if! {
474474
any(unix, windows),
475475
not(target_vendor = "uwp"),
476476
not(target_os = "emscripten"),
477+
not(target_os = "espidf"),
477478
any(not(backtrace_in_libstd), feature = "backtrace"),
478479
))] {
479480
mod gimli;

0 commit comments

Comments
 (0)