]>
Commit | Line | Data |
---|---|---|
e9174d1e SL |
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 io; | |
12 | use io::prelude::*; | |
13 | use libc; | |
14 | use mem; | |
15 | use sync::StaticMutex; | |
16 | ||
17 | use super::super::printing::print; | |
18 | ||
19 | #[inline(never)] // if we know this is a function call, we can skip it when | |
20 | // tracing | |
21 | pub fn write(w: &mut Write) -> io::Result<()> { | |
22 | struct Context<'a> { | |
23 | idx: isize, | |
24 | writer: &'a mut (Write+'a), | |
25 | last_error: Option<io::Error>, | |
26 | } | |
27 | ||
28 | // When using libbacktrace, we use some necessary global state, so we | |
29 | // need to prevent more than one thread from entering this block. This | |
30 | // is semi-reasonable in terms of printing anyway, and we know that all | |
31 | // I/O done here is blocking I/O, not green I/O, so we don't have to | |
32 | // worry about this being a native vs green mutex. | |
33 | static LOCK: StaticMutex = StaticMutex::new(); | |
34 | let _g = LOCK.lock(); | |
35 | ||
54a0048b | 36 | writeln!(w, "stack backtrace:")?; |
e9174d1e SL |
37 | |
38 | let mut cx = Context { writer: w, last_error: None, idx: 0 }; | |
39 | return match unsafe { | |
40 | uw::_Unwind_Backtrace(trace_fn, | |
41 | &mut cx as *mut Context as *mut libc::c_void) | |
42 | } { | |
43 | uw::_URC_NO_REASON => { | |
44 | match cx.last_error { | |
45 | Some(err) => Err(err), | |
46 | None => Ok(()) | |
47 | } | |
48 | } | |
49 | _ => Ok(()), | |
50 | }; | |
51 | ||
52 | extern fn trace_fn(ctx: *mut uw::_Unwind_Context, | |
53 | arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { | |
54 | let cx: &mut Context = unsafe { mem::transmute(arg) }; | |
55 | let mut ip_before_insn = 0; | |
56 | let mut ip = unsafe { | |
57 | uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void | |
58 | }; | |
59 | if !ip.is_null() && ip_before_insn == 0 { | |
60 | // this is a non-signaling frame, so `ip` refers to the address | |
61 | // after the calling instruction. account for that. | |
62 | ip = (ip as usize - 1) as *mut _; | |
63 | } | |
64 | ||
65 | // dladdr() on osx gets whiny when we use FindEnclosingFunction, and | |
66 | // it appears to work fine without it, so we only use | |
67 | // FindEnclosingFunction on non-osx platforms. In doing so, we get a | |
68 | // slightly more accurate stack trace in the process. | |
69 | // | |
70 | // This is often because panic involves the last instruction of a | |
71 | // function being "call std::rt::begin_unwind", with no ret | |
72 | // instructions after it. This means that the return instruction | |
73 | // pointer points *outside* of the calling function, and by | |
74 | // unwinding it we go back to the original function. | |
75 | let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { | |
76 | ip | |
77 | } else { | |
78 | unsafe { uw::_Unwind_FindEnclosingFunction(ip) } | |
79 | }; | |
80 | ||
81 | // Don't print out the first few frames (they're not user frames) | |
82 | cx.idx += 1; | |
83 | if cx.idx <= 0 { return uw::_URC_NO_REASON } | |
84 | // Don't print ginormous backtraces | |
85 | if cx.idx > 100 { | |
86 | match write!(cx.writer, " ... <frames omitted>\n") { | |
87 | Ok(()) => {} | |
88 | Err(e) => { cx.last_error = Some(e); } | |
89 | } | |
90 | return uw::_URC_FAILURE | |
91 | } | |
92 | ||
93 | // Once we hit an error, stop trying to print more frames | |
94 | if cx.last_error.is_some() { return uw::_URC_FAILURE } | |
95 | ||
96 | match print(cx.writer, cx.idx, ip, symaddr) { | |
97 | Ok(()) => {} | |
98 | Err(e) => { cx.last_error = Some(e); } | |
99 | } | |
100 | ||
101 | // keep going | |
102 | uw::_URC_NO_REASON | |
103 | } | |
104 | } | |
105 | ||
106 | /// Unwind library interface used for backtraces | |
107 | /// | |
108 | /// Note that dead code is allowed as here are just bindings | |
109 | /// iOS doesn't use all of them it but adding more | |
110 | /// platform-specific configs pollutes the code too much | |
111 | #[allow(non_camel_case_types)] | |
112 | #[allow(non_snake_case)] | |
113 | mod uw { | |
114 | pub use self::_Unwind_Reason_Code::*; | |
115 | ||
116 | use libc; | |
117 | ||
118 | #[repr(C)] | |
119 | pub enum _Unwind_Reason_Code { | |
120 | _URC_NO_REASON = 0, | |
121 | _URC_FOREIGN_EXCEPTION_CAUGHT = 1, | |
122 | _URC_FATAL_PHASE2_ERROR = 2, | |
123 | _URC_FATAL_PHASE1_ERROR = 3, | |
124 | _URC_NORMAL_STOP = 4, | |
125 | _URC_END_OF_STACK = 5, | |
126 | _URC_HANDLER_FOUND = 6, | |
127 | _URC_INSTALL_CONTEXT = 7, | |
128 | _URC_CONTINUE_UNWIND = 8, | |
129 | _URC_FAILURE = 9, // used only by ARM EABI | |
130 | } | |
131 | ||
132 | pub enum _Unwind_Context {} | |
133 | ||
134 | pub type _Unwind_Trace_Fn = | |
135 | extern fn(ctx: *mut _Unwind_Context, | |
136 | arg: *mut libc::c_void) -> _Unwind_Reason_Code; | |
137 | ||
138 | extern { | |
139 | // No native _Unwind_Backtrace on iOS | |
140 | #[cfg(not(all(target_os = "ios", target_arch = "arm")))] | |
141 | pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, | |
142 | trace_argument: *mut libc::c_void) | |
143 | -> _Unwind_Reason_Code; | |
144 | ||
145 | // available since GCC 4.2.0, should be fine for our purpose | |
146 | #[cfg(all(not(all(target_os = "android", target_arch = "arm")), | |
147 | not(all(target_os = "linux", target_arch = "arm"))))] | |
148 | pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, | |
149 | ip_before_insn: *mut libc::c_int) | |
150 | -> libc::uintptr_t; | |
151 | ||
152 | #[cfg(all(not(target_os = "android"), | |
153 | not(all(target_os = "linux", target_arch = "arm"))))] | |
154 | pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) | |
155 | -> *mut libc::c_void; | |
156 | } | |
157 | ||
158 | // On android, the function _Unwind_GetIP is a macro, and this is the | |
159 | // expansion of the macro. This is all copy/pasted directly from the | |
160 | // header file with the definition of _Unwind_GetIP. | |
161 | #[cfg(any(all(target_os = "android", target_arch = "arm"), | |
162 | all(target_os = "linux", target_arch = "arm")))] | |
163 | pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { | |
164 | #[repr(C)] | |
165 | enum _Unwind_VRS_Result { | |
166 | _UVRSR_OK = 0, | |
167 | _UVRSR_NOT_IMPLEMENTED = 1, | |
168 | _UVRSR_FAILED = 2, | |
169 | } | |
170 | #[repr(C)] | |
171 | enum _Unwind_VRS_RegClass { | |
172 | _UVRSC_CORE = 0, | |
173 | _UVRSC_VFP = 1, | |
174 | _UVRSC_FPA = 2, | |
175 | _UVRSC_WMMXD = 3, | |
176 | _UVRSC_WMMXC = 4, | |
177 | } | |
178 | #[repr(C)] | |
179 | enum _Unwind_VRS_DataRepresentation { | |
180 | _UVRSD_UINT32 = 0, | |
181 | _UVRSD_VFPX = 1, | |
182 | _UVRSD_FPAX = 2, | |
183 | _UVRSD_UINT64 = 3, | |
184 | _UVRSD_FLOAT = 4, | |
185 | _UVRSD_DOUBLE = 5, | |
186 | } | |
187 | ||
188 | type _Unwind_Word = libc::c_uint; | |
189 | extern { | |
190 | fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, | |
191 | klass: _Unwind_VRS_RegClass, | |
192 | word: _Unwind_Word, | |
193 | repr: _Unwind_VRS_DataRepresentation, | |
194 | data: *mut libc::c_void) | |
195 | -> _Unwind_VRS_Result; | |
196 | } | |
197 | ||
198 | let mut val: _Unwind_Word = 0; | |
199 | let ptr = &mut val as *mut _Unwind_Word; | |
200 | let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, | |
201 | _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, | |
202 | ptr as *mut libc::c_void); | |
203 | (val & !1) as libc::uintptr_t | |
204 | } | |
205 | ||
206 | // This function doesn't exist on Android or ARM/Linux, so make it same | |
207 | // to _Unwind_GetIP | |
208 | #[cfg(any(all(target_os = "android", target_arch = "arm"), | |
209 | all(target_os = "linux", target_arch = "arm")))] | |
210 | pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, | |
211 | ip_before_insn: *mut libc::c_int) | |
212 | -> libc::uintptr_t | |
213 | { | |
214 | *ip_before_insn = 0; | |
215 | _Unwind_GetIP(ctx) | |
216 | } | |
217 | ||
218 | // This function also doesn't exist on Android or ARM/Linux, so make it | |
219 | // a no-op | |
220 | #[cfg(any(target_os = "android", | |
221 | all(target_os = "linux", target_arch = "arm")))] | |
222 | pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) | |
223 | -> *mut libc::c_void | |
224 | { | |
225 | pc | |
226 | } | |
227 | } |