Skip to content

Commit 635e159

Browse files
authored
Add the Frame::sp method to get the frame's stack pointer (#341)
* Add the `Frame::sp` method to get the frame's stack pointer * rustfmt
1 parent 02bc46f commit 635e159

File tree

6 files changed

+185
-34
lines changed

6 files changed

+185
-34
lines changed

src/backtrace/dbghelp.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ impl Frame {
4242
self.addr_pc().Offset as *mut _
4343
}
4444

45+
pub fn sp(&self) -> *mut c_void {
46+
self.addr_stack().Offset as *mut _
47+
}
48+
4549
pub fn symbol_address(&self) -> *mut c_void {
4650
self.ip()
4751
}
@@ -67,6 +71,13 @@ impl Frame {
6771
}
6872
}
6973

74+
fn addr_stack(&self) -> &ADDRESS64 {
75+
match self {
76+
Frame::New(new) => &new.AddrStack,
77+
Frame::Old(old) => &old.AddrStack,
78+
}
79+
}
80+
7081
fn addr_stack_mut(&mut self) -> &mut ADDRESS64 {
7182
match self {
7283
Frame::New(new) => &mut new.AddrStack,

src/backtrace/libunwind.rs

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub enum Frame {
3131
Raw(*mut uw::_Unwind_Context),
3232
Cloned {
3333
ip: *mut c_void,
34+
sp: *mut c_void,
3435
symbol_address: *mut c_void,
3536
},
3637
}
@@ -51,6 +52,13 @@ impl Frame {
5152
unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void }
5253
}
5354

55+
pub fn sp(&self) -> *mut c_void {
56+
match *self {
57+
Frame::Raw(ctx) => unsafe { uw::get_sp(ctx) as *mut c_void },
58+
Frame::Cloned { sp, .. } => sp,
59+
}
60+
}
61+
5462
pub fn symbol_address(&self) -> *mut c_void {
5563
if let Frame::Cloned { symbol_address, .. } = *self {
5664
return symbol_address;
@@ -76,6 +84,7 @@ impl Clone for Frame {
7684
fn clone(&self) -> Frame {
7785
Frame::Cloned {
7886
ip: self.ip(),
87+
sp: self.sp(),
7988
symbol_address: self.symbol_address(),
8089
}
8190
}
@@ -155,22 +164,46 @@ mod uw {
155164
pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
156165

157166
#[cfg(all(
158-
not(target_os = "android"),
167+
not(all(target_os = "android", target_arch = "arm")),
159168
not(all(target_os = "freebsd", target_arch = "arm")),
160169
not(all(target_os = "linux", target_arch = "arm"))
161170
))]
162171
pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
172+
173+
#[cfg(all(
174+
not(all(target_os = "android", target_arch = "arm")),
175+
not(all(target_os = "freebsd", target_arch = "arm")),
176+
not(all(target_os = "linux", target_arch = "arm"))
177+
))]
178+
// This function is a misnomer: rather than getting this frame's
179+
// Canonical Frame Address (aka the caller frame's SP) it
180+
// returns this frame's SP.
181+
//
182+
// https://github.com/libunwind/libunwind/blob/d32956507cf29d9b1a98a8bce53c78623908f4fe/src/unwind/GetCFA.c#L28-L35
183+
#[link_name = "_Unwind_GetCFA"]
184+
pub fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
163185
}
164186

165-
// On android, the function _Unwind_GetIP is a macro, and this is the
166-
// expansion of the macro. This is all copy/pasted directly from the
167-
// header file with the definition of _Unwind_GetIP.
187+
// On android and arm, the function `_Unwind_GetIP` and a bunch of others
188+
// are macros, so we define functions containing the expansion of the
189+
// macros.
190+
//
191+
// TODO: link to the header file that defines these macros, if you can find
192+
// it. (I, fitzgen, cannot find the header file that some of these macro
193+
// expansions were originally borrowed from.)
194+
#[cfg(any(
195+
all(target_os = "android", target_arch = "arm"),
196+
all(target_os = "freebsd", target_arch = "arm"),
197+
all(target_os = "linux", target_arch = "arm")
198+
))]
199+
pub use self::arm::*;
168200
#[cfg(any(
169201
all(target_os = "android", target_arch = "arm"),
170202
all(target_os = "freebsd", target_arch = "arm"),
171203
all(target_os = "linux", target_arch = "arm")
172204
))]
173-
pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
205+
mod arm {
206+
pub use super::*;
174207
#[repr(C)]
175208
enum _Unwind_VRS_Result {
176209
_UVRSR_OK = 0,
@@ -206,26 +239,39 @@ mod uw {
206239
) -> _Unwind_VRS_Result;
207240
}
208241

209-
let mut val: _Unwind_Word = 0;
210-
let ptr = &mut val as *mut _Unwind_Word;
211-
let _ = _Unwind_VRS_Get(
212-
ctx,
213-
_Unwind_VRS_RegClass::_UVRSC_CORE,
214-
15,
215-
_Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
216-
ptr as *mut c_void,
217-
);
218-
(val & !1) as libc::uintptr_t
219-
}
242+
pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
243+
let mut val: _Unwind_Word = 0;
244+
let ptr = &mut val as *mut _Unwind_Word;
245+
let _ = _Unwind_VRS_Get(
246+
ctx,
247+
_Unwind_VRS_RegClass::_UVRSC_CORE,
248+
15,
249+
_Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
250+
ptr as *mut c_void,
251+
);
252+
(val & !1) as libc::uintptr_t
253+
}
220254

221-
// This function also doesn't exist on Android or ARM/Linux, so make it
222-
// a no-op
223-
#[cfg(any(
224-
target_os = "android",
225-
all(target_os = "freebsd", target_arch = "arm"),
226-
all(target_os = "linux", target_arch = "arm")
227-
))]
228-
pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
229-
pc
255+
// R13 is the stack pointer on arm.
256+
const SP: _Unwind_Word = 13;
257+
258+
pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
259+
let mut val: _Unwind_Word = 0;
260+
let ptr = &mut val as *mut _Unwind_Word;
261+
let _ = _Unwind_VRS_Get(
262+
ctx,
263+
_Unwind_VRS_RegClass::_UVRSC_CORE,
264+
SP,
265+
_Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
266+
ptr as *mut c_void,
267+
);
268+
val as libc::uintptr_t
269+
}
270+
271+
// This function also doesn't exist on Android or ARM/Linux, so make it
272+
// a no-op.
273+
pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
274+
pc
275+
}
230276
}
231277
}

src/backtrace/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ impl Frame {
9090
self.inner.ip()
9191
}
9292

93+
/// Returns the current stack pointer of this frame.
94+
///
95+
/// In the case that a backend cannot recover the stack pointer for this
96+
/// frame, a null pointer is returned.
97+
pub fn sp(&self) -> *mut c_void {
98+
self.inner.sp()
99+
}
100+
93101
/// Returns the starting symbol address of the frame of this function.
94102
///
95103
/// This will attempt to rewind the instruction pointer returned by `ip` to

src/backtrace/noop.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ impl Frame {
1414
0 as *mut _
1515
}
1616

17+
pub fn sp(&self) -> *mut c_void {
18+
0 as *mut _
19+
}
20+
1721
pub fn symbol_address(&self) -> *mut c_void {
1822
0 as *mut _
1923
}

src/capture.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -338,17 +338,18 @@ impl fmt::Debug for Backtrace {
338338
// short format, because if it's full we presumably want to print
339339
// everything.
340340
let cwd = std::env::current_dir();
341-
let mut print_path = move |fmt: &mut fmt::Formatter<'_>, path: crate::BytesOrWideString<'_>| {
342-
let path = path.into_path_buf();
343-
if !full {
344-
if let Ok(cwd) = &cwd {
345-
if let Ok(suffix) = path.strip_prefix(cwd) {
346-
return fmt::Display::fmt(&suffix.display(), fmt);
341+
let mut print_path =
342+
move |fmt: &mut fmt::Formatter<'_>, path: crate::BytesOrWideString<'_>| {
343+
let path = path.into_path_buf();
344+
if !full {
345+
if let Ok(cwd) = &cwd {
346+
if let Ok(suffix) = path.strip_prefix(cwd) {
347+
return fmt::Display::fmt(&suffix.display(), fmt);
348+
}
347349
}
348350
}
349-
}
350-
fmt::Display::fmt(&path.display(), fmt)
351-
};
351+
fmt::Display::fmt(&path.display(), fmt)
352+
};
352353

353354
let mut f = BacktraceFmt::new(fmt, style, &mut print_path);
354355
f.add_context()?;

tests/smoke.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,84 @@ fn is_serde() {
228228
is_serialize::<backtrace::Backtrace>();
229229
is_deserialize::<backtrace::Backtrace>();
230230
}
231+
232+
#[test]
233+
fn sp_smoke_test() {
234+
let mut refs = vec![];
235+
recursive_stack_references(&mut refs);
236+
return;
237+
238+
#[inline(never)]
239+
fn recursive_stack_references(refs: &mut Vec<usize>) {
240+
assert!(refs.len() < 5);
241+
242+
let x = refs.len();
243+
refs.push(&x as *const _ as usize);
244+
245+
if refs.len() < 5 {
246+
recursive_stack_references(refs);
247+
eprintln!("exiting: {}", x);
248+
return;
249+
}
250+
251+
backtrace::trace(make_trace_closure(refs));
252+
eprintln!("exiting: {}", x);
253+
}
254+
255+
// NB: the following `make_*` functions are pulled out of line, rather than
256+
// defining their results as inline closures at their call sites, so that
257+
// the resulting closures don't have "recursive_stack_references" in their
258+
// mangled names.
259+
260+
fn make_trace_closure<'a>(
261+
refs: &'a mut Vec<usize>,
262+
) -> impl FnMut(&backtrace::Frame) -> bool + 'a {
263+
let mut child_sp = None;
264+
let mut child_ref = None;
265+
move |frame| {
266+
eprintln!("\n=== frame ===================================");
267+
268+
let mut is_recursive_stack_references = false;
269+
backtrace::resolve(frame.ip(), |sym| {
270+
is_recursive_stack_references |= (LIBBACKTRACE || GIMLI_SYMBOLIZE)
271+
&& sym
272+
.name()
273+
.and_then(|name| name.as_str())
274+
.map_or(false, |name| {
275+
eprintln!("name = {}", name);
276+
name.contains("recursive_stack_references")
277+
})
278+
});
279+
280+
let sp = frame.sp() as usize;
281+
eprintln!("sp = {:p}", sp as *const u8);
282+
if sp == 0 {
283+
// If the SP is null, then we don't have an implementation for
284+
// getting the SP on this target. Just keep walking the stack,
285+
// but don't make our assertions about the on-stack pointers and
286+
// SP values.
287+
return true;
288+
}
289+
290+
// The stack grows down.
291+
if let Some(child_sp) = child_sp {
292+
assert!(child_sp <= sp);
293+
}
294+
295+
if is_recursive_stack_references {
296+
let r = refs.pop().unwrap();
297+
eprintln!("ref = {:p}", r as *const u8);
298+
if sp != 0 {
299+
assert!(r > sp);
300+
if let Some(child_ref) = child_ref {
301+
assert!(sp >= child_ref);
302+
}
303+
}
304+
child_ref = Some(r);
305+
}
306+
307+
child_sp = Some(sp);
308+
true
309+
}
310+
}
311+
}

0 commit comments

Comments
 (0)