]>
Commit | Line | Data |
---|---|---|
3b2f2976 XL |
1 | // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | use error::Error; | |
12 | use io; | |
13 | use libc; | |
14 | use sys::backtrace::BacktraceContext; | |
15 | use sys_common::backtrace::Frame; | |
16 | ||
17 | use unwind as uw; | |
18 | ||
19 | struct Context<'a> { | |
20 | idx: usize, | |
21 | frames: &'a mut [Frame], | |
22 | } | |
23 | ||
24 | #[derive(Debug)] | |
25 | struct UnwindError(uw::_Unwind_Reason_Code); | |
26 | ||
27 | impl Error for UnwindError { | |
28 | fn description(&self) -> &'static str { | |
29 | "unexpected return value while unwinding" | |
30 | } | |
31 | } | |
32 | ||
33 | impl ::fmt::Display for UnwindError { | |
34 | fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result { | |
35 | write!(f, "{}: {:?}", self.description(), self.0) | |
36 | } | |
37 | } | |
38 | ||
39 | #[inline(never)] // if we know this is a function call, we can skip it when | |
40 | // tracing | |
41 | pub fn unwind_backtrace(frames: &mut [Frame]) | |
42 | -> io::Result<(usize, BacktraceContext)> | |
43 | { | |
44 | let mut cx = Context { | |
45 | idx: 0, | |
46 | frames: frames, | |
47 | }; | |
48 | let result_unwind = unsafe { | |
49 | uw::_Unwind_Backtrace(trace_fn, | |
50 | &mut cx as *mut Context | |
51 | as *mut libc::c_void) | |
52 | }; | |
53 | // See libunwind:src/unwind/Backtrace.c for the return values. | |
54 | // No, there is no doc. | |
55 | match result_unwind { | |
56 | // These return codes seem to be benign and need to be ignored for backtraces | |
57 | // to show up properly on all tested platforms. | |
58 | uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => { | |
59 | Ok((cx.idx, BacktraceContext)) | |
60 | } | |
61 | _ => { | |
62 | Err(io::Error::new(io::ErrorKind::Other, | |
63 | UnwindError(result_unwind))) | |
64 | } | |
65 | } | |
66 | } | |
67 | ||
68 | extern fn trace_fn(ctx: *mut uw::_Unwind_Context, | |
69 | arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { | |
70 | let cx = unsafe { &mut *(arg as *mut Context) }; | |
71 | let mut ip_before_insn = 0; | |
72 | let mut ip = unsafe { | |
73 | uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void | |
74 | }; | |
75 | if !ip.is_null() && ip_before_insn == 0 { | |
76 | // this is a non-signaling frame, so `ip` refers to the address | |
77 | // after the calling instruction. account for that. | |
78 | ip = (ip as usize - 1) as *mut _; | |
79 | } | |
80 | ||
81 | // dladdr() on osx gets whiny when we use FindEnclosingFunction, and | |
82 | // it appears to work fine without it, so we only use | |
83 | // FindEnclosingFunction on non-osx platforms. In doing so, we get a | |
84 | // slightly more accurate stack trace in the process. | |
85 | // | |
86 | // This is often because panic involves the last instruction of a | |
87 | // function being "call std::rt::begin_unwind", with no ret | |
88 | // instructions after it. This means that the return instruction | |
89 | // pointer points *outside* of the calling function, and by | |
90 | // unwinding it we go back to the original function. | |
91 | let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { | |
92 | ip | |
93 | } else { | |
94 | unsafe { uw::_Unwind_FindEnclosingFunction(ip) } | |
95 | }; | |
96 | ||
97 | if cx.idx < cx.frames.len() { | |
98 | cx.frames[cx.idx] = Frame { | |
abe05a73 XL |
99 | symbol_addr: symaddr as *mut u8, |
100 | exact_position: ip as *mut u8, | |
2c00a5a8 | 101 | inline_context: 0, |
3b2f2976 XL |
102 | }; |
103 | cx.idx += 1; | |
104 | } | |
105 | ||
106 | uw::_URC_NO_REASON | |
107 | } |