|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
| 11 | +use error::Error; |
11 | 12 | use io;
|
12 |
| -use io::prelude::*; |
13 | 13 | use libc;
|
14 |
| -use mem; |
15 |
| -use sys_common::mutex::Mutex; |
| 14 | +use sys::backtrace::BacktraceContext; |
| 15 | +use sys_common::backtrace::Frame; |
16 | 16 |
|
17 |
| -use super::super::printing::print; |
18 | 17 | use unwind as uw;
|
19 | 18 |
|
20 |
| -#[inline(never)] // if we know this is a function call, we can skip it when |
21 |
| - // tracing |
22 |
| -pub fn write(w: &mut Write) -> io::Result<()> { |
23 |
| - struct Context<'a> { |
24 |
| - idx: isize, |
25 |
| - writer: &'a mut (Write+'a), |
26 |
| - last_error: Option<io::Error>, |
27 |
| - } |
| 19 | +struct Context<'a> { |
| 20 | + idx: usize, |
| 21 | + frames: &'a mut [Frame], |
| 22 | +} |
28 | 23 |
|
29 |
| - // When using libbacktrace, we use some necessary global state, so we |
30 |
| - // need to prevent more than one thread from entering this block. This |
31 |
| - // is semi-reasonable in terms of printing anyway, and we know that all |
32 |
| - // I/O done here is blocking I/O, not green I/O, so we don't have to |
33 |
| - // worry about this being a native vs green mutex. |
34 |
| - static LOCK: Mutex = Mutex::new(); |
35 |
| - unsafe { |
36 |
| - LOCK.lock(); |
| 24 | +#[derive(Debug)] |
| 25 | +struct UnwindError(uw::_Unwind_Reason_Code); |
37 | 26 |
|
38 |
| - writeln!(w, "stack backtrace:")?; |
| 27 | +impl Error for UnwindError { |
| 28 | + fn description(&self) -> &'static str { |
| 29 | + "unexpected return value while unwinding" |
| 30 | + } |
| 31 | +} |
39 | 32 |
|
40 |
| - let mut cx = Context { writer: w, last_error: None, idx: 0 }; |
41 |
| - let ret = match { |
42 |
| - uw::_Unwind_Backtrace(trace_fn, |
43 |
| - &mut cx as *mut Context as *mut libc::c_void) |
44 |
| - } { |
45 |
| - uw::_URC_NO_REASON => { |
46 |
| - match cx.last_error { |
47 |
| - Some(err) => Err(err), |
48 |
| - None => Ok(()) |
49 |
| - } |
50 |
| - } |
51 |
| - _ => Ok(()), |
52 |
| - }; |
53 |
| - LOCK.unlock(); |
54 |
| - return ret |
| 33 | +impl ::fmt::Display for UnwindError { |
| 34 | + fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result { |
| 35 | + write!(f, "{}: {:?}", self.description(), self.0) |
55 | 36 | }
|
| 37 | +} |
56 | 38 |
|
57 |
| - extern fn trace_fn(ctx: *mut uw::_Unwind_Context, |
58 |
| - arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { |
59 |
| - let cx: &mut Context = unsafe { mem::transmute(arg) }; |
60 |
| - let mut ip_before_insn = 0; |
61 |
| - let mut ip = unsafe { |
62 |
| - uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void |
63 |
| - }; |
64 |
| - if !ip.is_null() && ip_before_insn == 0 { |
65 |
| - // this is a non-signaling frame, so `ip` refers to the address |
66 |
| - // after the calling instruction. account for that. |
67 |
| - ip = (ip as usize - 1) as *mut _; |
| 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 | + uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR => { |
| 57 | + Ok((cx.idx, BacktraceContext)) |
68 | 58 | }
|
69 |
| - |
70 |
| - // dladdr() on osx gets whiny when we use FindEnclosingFunction, and |
71 |
| - // it appears to work fine without it, so we only use |
72 |
| - // FindEnclosingFunction on non-osx platforms. In doing so, we get a |
73 |
| - // slightly more accurate stack trace in the process. |
74 |
| - // |
75 |
| - // This is often because panic involves the last instruction of a |
76 |
| - // function being "call std::rt::begin_unwind", with no ret |
77 |
| - // instructions after it. This means that the return instruction |
78 |
| - // pointer points *outside* of the calling function, and by |
79 |
| - // unwinding it we go back to the original function. |
80 |
| - let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { |
81 |
| - ip |
82 |
| - } else { |
83 |
| - unsafe { uw::_Unwind_FindEnclosingFunction(ip) } |
84 |
| - }; |
85 |
| - |
86 |
| - // Don't print out the first few frames (they're not user frames) |
87 |
| - cx.idx += 1; |
88 |
| - if cx.idx <= 0 { return uw::_URC_NO_REASON } |
89 |
| - // Don't print ginormous backtraces |
90 |
| - if cx.idx > 100 { |
91 |
| - match write!(cx.writer, " ... <frames omitted>\n") { |
92 |
| - Ok(()) => {} |
93 |
| - Err(e) => { cx.last_error = Some(e); } |
94 |
| - } |
95 |
| - return uw::_URC_FAILURE |
| 59 | + _ => { |
| 60 | + Err(io::Error::new(io::ErrorKind::Other, |
| 61 | + UnwindError(result_unwind))) |
96 | 62 | }
|
| 63 | + } |
| 64 | +} |
97 | 65 |
|
98 |
| - // Once we hit an error, stop trying to print more frames |
99 |
| - if cx.last_error.is_some() { return uw::_URC_FAILURE } |
| 66 | +extern fn trace_fn(ctx: *mut uw::_Unwind_Context, |
| 67 | + arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { |
| 68 | + let cx = unsafe { &mut *(arg as *mut Context) }; |
| 69 | + let mut ip_before_insn = 0; |
| 70 | + let mut ip = unsafe { |
| 71 | + uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void |
| 72 | + }; |
| 73 | + if !ip.is_null() && ip_before_insn == 0 { |
| 74 | + // this is a non-signaling frame, so `ip` refers to the address |
| 75 | + // after the calling instruction. account for that. |
| 76 | + ip = (ip as usize - 1) as *mut _; |
| 77 | + } |
100 | 78 |
|
101 |
| - match print(cx.writer, cx.idx, ip, symaddr) { |
102 |
| - Ok(()) => {} |
103 |
| - Err(e) => { cx.last_error = Some(e); } |
104 |
| - } |
| 79 | + // dladdr() on osx gets whiny when we use FindEnclosingFunction, and |
| 80 | + // it appears to work fine without it, so we only use |
| 81 | + // FindEnclosingFunction on non-osx platforms. In doing so, we get a |
| 82 | + // slightly more accurate stack trace in the process. |
| 83 | + // |
| 84 | + // This is often because panic involves the last instruction of a |
| 85 | + // function being "call std::rt::begin_unwind", with no ret |
| 86 | + // instructions after it. This means that the return instruction |
| 87 | + // pointer points *outside* of the calling function, and by |
| 88 | + // unwinding it we go back to the original function. |
| 89 | + let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { |
| 90 | + ip |
| 91 | + } else { |
| 92 | + unsafe { uw::_Unwind_FindEnclosingFunction(ip) } |
| 93 | + }; |
105 | 94 |
|
106 |
| - // keep going |
107 |
| - uw::_URC_NO_REASON |
| 95 | + if cx.idx < cx.frames.len() { |
| 96 | + cx.frames[cx.idx] = Frame { |
| 97 | + symbol_addr: symaddr, |
| 98 | + exact_position: ip, |
| 99 | + }; |
| 100 | + cx.idx += 1; |
108 | 101 | }
|
| 102 | + |
| 103 | + uw::_URC_NO_REASON |
109 | 104 | }
|
0 commit comments