4 use sys::backtrace::BacktraceContext;
5 use sys_common::backtrace::Frame;
11 frames: &'a mut [Frame],
15 struct UnwindError(uw::_Unwind_Reason_Code);
17 impl Error for UnwindError {
18 fn description(&self) -> &'static str {
19 "unexpected return value while unwinding"
23 impl ::fmt::Display for UnwindError {
24 fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
25 write!(f, "{}: {:?}", self.description(), self.0)
29 #[inline(never)] // if we know this is a function call, we can skip it when
31 pub fn unwind_backtrace(frames: &mut [Frame])
32 -> io::Result<(usize, BacktraceContext)>
34 let mut cx = Context {
38 let result_unwind = unsafe {
39 uw::_Unwind_Backtrace(trace_fn,
40 &mut cx as *mut Context
43 // See libunwind:src/unwind/Backtrace.c for the return values.
44 // No, there is no doc.
46 // These return codes seem to be benign and need to be ignored for backtraces
47 // to show up properly on all tested platforms.
48 uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => {
49 Ok((cx.idx, BacktraceContext))
52 Err(io::Error::new(io::ErrorKind::Other,
53 UnwindError(result_unwind)))
58 extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
59 arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
60 let cx = unsafe { &mut *(arg as *mut Context) };
61 if cx.idx >= cx.frames.len() {
62 return uw::_URC_NORMAL_STOP;
65 let mut ip_before_insn = 0;
67 uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void
69 if !ip.is_null() && ip_before_insn == 0 {
70 // this is a non-signaling frame, so `ip` refers to the address
71 // after the calling instruction. account for that.
72 ip = (ip as usize - 1) as *mut _;
75 // dladdr() on osx gets whiny when we use FindEnclosingFunction, and
76 // it appears to work fine without it, so we only use
77 // FindEnclosingFunction on non-osx platforms. In doing so, we get a
78 // slightly more accurate stack trace in the process.
80 // This is often because panic involves the last instruction of a
81 // function being "call std::rt::begin_unwind", with no ret
82 // instructions after it. This means that the return instruction
83 // pointer points *outside* of the calling function, and by
84 // unwinding it we go back to the original function.
85 let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
88 unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
91 cx.frames[cx.idx] = Frame {
92 symbol_addr: symaddr as *mut u8,
93 exact_position: ip as *mut u8,