]>
Commit | Line | Data |
---|---|---|
3b2f2976 XL |
1 | use error::Error; |
2 | use io; | |
3 | use libc; | |
4 | use sys::backtrace::BacktraceContext; | |
5 | use sys_common::backtrace::Frame; | |
6 | ||
7 | use unwind as uw; | |
8 | ||
9 | struct Context<'a> { | |
10 | idx: usize, | |
11 | frames: &'a mut [Frame], | |
12 | } | |
13 | ||
14 | #[derive(Debug)] | |
15 | struct UnwindError(uw::_Unwind_Reason_Code); | |
16 | ||
17 | impl Error for UnwindError { | |
18 | fn description(&self) -> &'static str { | |
19 | "unexpected return value while unwinding" | |
20 | } | |
21 | } | |
22 | ||
23 | impl ::fmt::Display for UnwindError { | |
24 | fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result { | |
25 | write!(f, "{}: {:?}", self.description(), self.0) | |
26 | } | |
27 | } | |
28 | ||
29 | #[inline(never)] // if we know this is a function call, we can skip it when | |
30 | // tracing | |
31 | pub fn unwind_backtrace(frames: &mut [Frame]) | |
32 | -> io::Result<(usize, BacktraceContext)> | |
33 | { | |
34 | let mut cx = Context { | |
35 | idx: 0, | |
36 | frames: frames, | |
37 | }; | |
38 | let result_unwind = unsafe { | |
39 | uw::_Unwind_Backtrace(trace_fn, | |
40 | &mut cx as *mut Context | |
41 | as *mut libc::c_void) | |
42 | }; | |
43 | // See libunwind:src/unwind/Backtrace.c for the return values. | |
44 | // No, there is no doc. | |
45 | match result_unwind { | |
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)) | |
50 | } | |
51 | _ => { | |
52 | Err(io::Error::new(io::ErrorKind::Other, | |
53 | UnwindError(result_unwind))) | |
54 | } | |
55 | } | |
56 | } | |
57 | ||
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) }; | |
b7449926 XL |
61 | if cx.idx >= cx.frames.len() { |
62 | return uw::_URC_NORMAL_STOP; | |
63 | } | |
64 | ||
3b2f2976 XL |
65 | let mut ip_before_insn = 0; |
66 | let mut ip = unsafe { | |
67 | uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void | |
68 | }; | |
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 _; | |
73 | } | |
74 | ||
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. | |
79 | // | |
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") { | |
86 | ip | |
87 | } else { | |
88 | unsafe { uw::_Unwind_FindEnclosingFunction(ip) } | |
89 | }; | |
90 | ||
b7449926 XL |
91 | cx.frames[cx.idx] = Frame { |
92 | symbol_addr: symaddr as *mut u8, | |
93 | exact_position: ip as *mut u8, | |
94 | inline_context: 0, | |
95 | }; | |
96 | cx.idx += 1; | |
3b2f2976 XL |
97 | |
98 | uw::_URC_NO_REASON | |
99 | } |