blob: ddae9a132c31483a88b79286e6d3c1c747a872df [file] [log] [blame]
Aaron Turon2b3477d2014-11-24 03:21:171// Copyright 2014 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/// Backtrace support built on libgcc with some extra OS-specific support
12///
13/// Some methods of getting a backtrace:
14///
15/// * The backtrace() functions on unix. It turns out this doesn't work very
16/// well for green threads on OSX, and the address to symbol portion of it
17/// suffers problems that are described below.
18///
19/// * Using libunwind. This is more difficult than it sounds because libunwind
20/// isn't installed everywhere by default. It's also a bit of a hefty library,
21/// so possibly not the best option. When testing, libunwind was excellent at
22/// getting both accurate backtraces and accurate symbols across platforms.
23/// This route was not chosen in favor of the next option, however.
24///
25/// * We're already using libgcc_s for exceptions in rust (triggering task
26/// unwinding and running destructors on the stack), and it turns out that it
27/// conveniently comes with a function that also gives us a backtrace. All of
28/// these functions look like _Unwind_*, but it's not quite the full
29/// repertoire of the libunwind API. Due to it already being in use, this was
30/// the chosen route of getting a backtrace.
31///
32/// After choosing libgcc_s for backtraces, the sad part is that it will only
33/// give us a stack trace of instruction pointers. Thankfully these instruction
34/// pointers are accurate (they work for green and native threads), but it's
35/// then up to us again to figure out how to translate these addresses to
36/// symbols. As with before, we have a few options. Before, that, a little bit
37/// of an interlude about symbols. This is my very limited knowledge about
38/// symbol tables, and this information is likely slightly wrong, but the
39/// general idea should be correct.
40///
41/// When talking about symbols, it's helpful to know a few things about where
42/// symbols are located. Some symbols are located in the dynamic symbol table
43/// of the executable which in theory means that they're available for dynamic
44/// linking and lookup. Other symbols end up only in the local symbol table of
45/// the file. This loosely corresponds to pub and priv functions in Rust.
46///
47/// Armed with this knowledge, we know that our solution for address to symbol
48/// translation will need to consult both the local and dynamic symbol tables.
49/// With that in mind, here's our options of translating an address to
50/// a symbol.
51///
52/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr()
53/// behind the scenes to translate, and this is why backtrace() was not used.
54/// Conveniently, this method works fantastically on OSX. It appears dladdr()
55/// uses magic to consult the local symbol table, or we're putting everything
56/// in the dynamic symbol table anyway. Regardless, for OSX, this is the
57/// method used for translation. It's provided by the system and easy to do.o
58///
59/// Sadly, all other systems have a dladdr() implementation that does not
60/// consult the local symbol table. This means that most functions are blank
61/// because they don't have symbols. This means that we need another solution.
62///
63/// * Use unw_get_proc_name(). This is part of the libunwind api (not the
64/// libgcc_s version of the libunwind api), but involves taking a dependency
65/// to libunwind. We may pursue this route in the future if we bundle
66/// libunwind, but libunwind was unwieldy enough that it was not chosen at
67/// this time to provide this functionality.
68///
69/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a
70/// semi-reasonable solution. The stdlib already knows how to spawn processes,
71/// so in theory it could invoke readelf, parse the output, and consult the
72/// local/dynamic symbol tables from there. This ended up not getting chosen
73/// due to the craziness of the idea plus the advent of the next option.
74///
75/// * Use `libbacktrace`. It turns out that this is a small library bundled in
76/// the gcc repository which provides backtrace and symbol translation
77/// functionality. All we really need from it is the backtrace functionality,
78/// and we only really need this on everything that's not OSX, so this is the
79/// chosen route for now.
80///
81/// In summary, the current situation uses libgcc_s to get a trace of stack
82/// pointers, and we use dladdr() or libbacktrace to translate these addresses
83/// to symbols. This is a bit of a hokey implementation as-is, but it works for
84/// all unix platforms we support right now, so it at least gets the job done.
85
86use c_str::CString;
87use io::{IoResult, Writer};
88use libc;
89use mem;
Aaron Turona27fbac2014-12-14 08:05:3290use option::Option::{mod, Some, None};
91use result::Result::{Ok, Err};
Aaron Turond8e47802014-12-01 16:49:3292use sync::{StaticMutex, MUTEX_INIT};
Aaron Turon2b3477d2014-11-24 03:21:1793
94use sys_common::backtrace::*;
95
96/// As always - iOS on arm uses SjLj exceptions and
97/// _Unwind_Backtrace is even not available there. Still,
98/// backtraces could be extracted using a backtrace function,
99/// which thanks god is public
100///
101/// As mentioned in a huge comment block above, backtrace doesn't
102/// play well with green threads, so while it is extremely nice
103/// and simple to use it should be used only on iOS devices as the
104/// only viable option.
105#[cfg(all(target_os = "ios", target_arch = "arm"))]
106#[inline(never)]
107pub fn write(w: &mut Writer) -> IoResult<()> {
Valerii Hiorae5d8c852014-12-19 14:11:26108 use iter::{IteratorExt, range};
Aaron Turon2b3477d2014-11-24 03:21:17109 use result;
110 use slice::SliceExt;
111
112 extern {
113 fn backtrace(buf: *mut *mut libc::c_void,
114 sz: libc::c_int) -> libc::c_int;
115 }
116
117 // while it doesn't requires lock for work as everything is
118 // local, it still displays much nicer backtraces when a
119 // couple of tasks panic simultaneously
Valerii Hiorae5d8c852014-12-19 14:11:26120 static LOCK: StaticMutex = MUTEX_INIT;
Aaron Turon2b3477d2014-11-24 03:21:17121 let _g = unsafe { LOCK.lock() };
122
123 try!(writeln!(w, "stack backtrace:"));
124 // 100 lines should be enough
125 const SIZE: uint = 100;
126 let mut buf: [*mut libc::c_void, ..SIZE] = unsafe {mem::zeroed()};
127 let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as uint};
128
129 // skipping the first one as it is write itself
130 let iter = range(1, cnt).map(|i| {
131 print(w, i as int, buf[i])
132 });
133 result::fold(iter, (), |_, _| ())
134}
135
136#[cfg(not(all(target_os = "ios", target_arch = "arm")))]
137#[inline(never)] // if we know this is a function call, we can skip it when
138 // tracing
139pub fn write(w: &mut Writer) -> IoResult<()> {
140 use io::IoError;
141
142 struct Context<'a> {
143 idx: int,
Aaron Turon43ae4b32014-12-07 02:34:37144 writer: &'a mut (Writer+'a),
Aaron Turon2b3477d2014-11-24 03:21:17145 last_error: Option<IoError>,
146 }
147
148 // When using libbacktrace, we use some necessary global state, so we
149 // need to prevent more than one thread from entering this block. This
150 // is semi-reasonable in terms of printing anyway, and we know that all
151 // I/O done here is blocking I/O, not green I/O, so we don't have to
152 // worry about this being a native vs green mutex.
Aaron Turond8e47802014-12-01 16:49:32153 static LOCK: StaticMutex = MUTEX_INIT;
Aaron Turon2b3477d2014-11-24 03:21:17154 let _g = unsafe { LOCK.lock() };
155
156 try!(writeln!(w, "stack backtrace:"));
157
158 let mut cx = Context { writer: w, last_error: None, idx: 0 };
159 return match unsafe {
160 uw::_Unwind_Backtrace(trace_fn,
161 &mut cx as *mut Context as *mut libc::c_void)
162 } {
163 uw::_URC_NO_REASON => {
164 match cx.last_error {
165 Some(err) => Err(err),
166 None => Ok(())
167 }
168 }
169 _ => Ok(()),
170 };
171
172 extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
173 arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
174 let cx: &mut Context = unsafe { mem::transmute(arg) };
175 let ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut libc::c_void };
176 // dladdr() on osx gets whiny when we use FindEnclosingFunction, and
177 // it appears to work fine without it, so we only use
178 // FindEnclosingFunction on non-osx platforms. In doing so, we get a
179 // slightly more accurate stack trace in the process.
180 //
181 // This is often because panic involves the last instruction of a
182 // function being "call std::rt::begin_unwind", with no ret
183 // instructions after it. This means that the return instruction
184 // pointer points *outside* of the calling function, and by
185 // unwinding it we go back to the original function.
186 let ip = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
187 ip
188 } else {
189 unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
190 };
191
192 // Don't print out the first few frames (they're not user frames)
193 cx.idx += 1;
194 if cx.idx <= 0 { return uw::_URC_NO_REASON }
195 // Don't print ginormous backtraces
196 if cx.idx > 100 {
197 match write!(cx.writer, " ... <frames omitted>\n") {
198 Ok(()) => {}
199 Err(e) => { cx.last_error = Some(e); }
200 }
201 return uw::_URC_FAILURE
202 }
203
204 // Once we hit an error, stop trying to print more frames
205 if cx.last_error.is_some() { return uw::_URC_FAILURE }
206
207 match print(cx.writer, cx.idx, ip) {
208 Ok(()) => {}
209 Err(e) => { cx.last_error = Some(e); }
210 }
211
212 // keep going
213 return uw::_URC_NO_REASON
214 }
215}
216
217#[cfg(any(target_os = "macos", target_os = "ios"))]
218fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
219 use intrinsics;
220 #[repr(C)]
221 struct Dl_info {
222 dli_fname: *const libc::c_char,
223 dli_fbase: *mut libc::c_void,
224 dli_sname: *const libc::c_char,
225 dli_saddr: *mut libc::c_void,
226 }
227 extern {
228 fn dladdr(addr: *const libc::c_void,
229 info: *mut Dl_info) -> libc::c_int;
230 }
231
232 let mut info: Dl_info = unsafe { intrinsics::init() };
233 if unsafe { dladdr(addr as *const libc::c_void, &mut info) == 0 } {
234 output(w, idx,addr, None)
235 } else {
236 output(w, idx, addr, Some(unsafe {
237 CString::new(info.dli_sname, false)
238 }))
239 }
240}
241
242#[cfg(not(any(target_os = "macos", target_os = "ios")))]
243fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
Alex Crichton7a6c54c2014-12-10 15:37:33244 use iter::{Iterator, IteratorExt};
Aaron Turon2b3477d2014-11-24 03:21:17245 use os;
246 use path::GenericPath;
Alex Crichton54452cd2014-12-19 16:57:12247 use ptr::PtrExt;
Aaron Turon2b3477d2014-11-24 03:21:17248 use ptr;
249 use slice::SliceExt;
250
251 ////////////////////////////////////////////////////////////////////////
252 // libbacktrace.h API
253 ////////////////////////////////////////////////////////////////////////
254 type backtrace_syminfo_callback =
255 extern "C" fn(data: *mut libc::c_void,
256 pc: libc::uintptr_t,
257 symname: *const libc::c_char,
258 symval: libc::uintptr_t,
259 symsize: libc::uintptr_t);
260 type backtrace_error_callback =
261 extern "C" fn(data: *mut libc::c_void,
262 msg: *const libc::c_char,
263 errnum: libc::c_int);
264 enum backtrace_state {}
265 #[link(name = "backtrace", kind = "static")]
266 #[cfg(not(test))]
267 extern {}
268
269 extern {
270 fn backtrace_create_state(filename: *const libc::c_char,
271 threaded: libc::c_int,
272 error: backtrace_error_callback,
273 data: *mut libc::c_void)
274 -> *mut backtrace_state;
275 fn backtrace_syminfo(state: *mut backtrace_state,
276 addr: libc::uintptr_t,
277 cb: backtrace_syminfo_callback,
278 error: backtrace_error_callback,
279 data: *mut libc::c_void) -> libc::c_int;
280 }
281
282 ////////////////////////////////////////////////////////////////////////
283 // helper callbacks
284 ////////////////////////////////////////////////////////////////////////
285
286 extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
287 _errnum: libc::c_int) {
288 // do nothing for now
289 }
290 extern fn syminfo_cb(data: *mut libc::c_void,
291 _pc: libc::uintptr_t,
292 symname: *const libc::c_char,
293 _symval: libc::uintptr_t,
294 _symsize: libc::uintptr_t) {
295 let slot = data as *mut *const libc::c_char;
296 unsafe { *slot = symname; }
297 }
298
299 // The libbacktrace API supports creating a state, but it does not
300 // support destroying a state. I personally take this to mean that a
301 // state is meant to be created and then live forever.
302 //
303 // I would love to register an at_exit() handler which cleans up this
304 // state, but libbacktrace provides no way to do so.
305 //
306 // With these constraints, this function has a statically cached state
307 // that is calculated the first time this is requested. Remember that
308 // backtracing all happens serially (one global lock).
309 //
310 // An additionally oddity in this function is that we initialize the
311 // filename via self_exe_name() to pass to libbacktrace. It turns out
312 // that on Linux libbacktrace seamlessly gets the filename of the
313 // current executable, but this fails on freebsd. by always providing
314 // it, we make sure that libbacktrace never has a reason to not look up
315 // the symbols. The libbacktrace API also states that the filename must
316 // be in "permanent memory", so we copy it to a static and then use the
317 // static as the pointer.
318 //
319 // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't
320 // tested if this is required or not.
321 unsafe fn init_state() -> *mut backtrace_state {
322 static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state;
323 static mut LAST_FILENAME: [libc::c_char, ..256] = [0, ..256];
324 if !STATE.is_null() { return STATE }
325 let selfname = if cfg!(target_os = "freebsd") ||
326 cfg!(target_os = "dragonfly") {
327 os::self_exe_name()
328 } else {
329 None
330 };
331 let filename = match selfname {
332 Some(path) => {
333 let bytes = path.as_vec();
334 if bytes.len() < LAST_FILENAME.len() {
335 let i = bytes.iter();
336 for (slot, val) in LAST_FILENAME.iter_mut().zip(i) {
337 *slot = *val as libc::c_char;
338 }
339 LAST_FILENAME.as_ptr()
340 } else {
341 ptr::null()
342 }
343 }
344 None => ptr::null(),
345 };
346 STATE = backtrace_create_state(filename, 0, error_cb,
347 ptr::null_mut());
348 return STATE
349 }
350
351 ////////////////////////////////////////////////////////////////////////
352 // translation
353 ////////////////////////////////////////////////////////////////////////
354
355 // backtrace errors are currently swept under the rug, only I/O
356 // errors are reported
357 let state = unsafe { init_state() };
358 if state.is_null() {
359 return output(w, idx, addr, None)
360 }
361 let mut data = 0 as *const libc::c_char;
362 let data_addr = &mut data as *mut *const libc::c_char;
363 let ret = unsafe {
364 backtrace_syminfo(state, addr as libc::uintptr_t,
365 syminfo_cb, error_cb,
366 data_addr as *mut libc::c_void)
367 };
368 if ret == 0 || data.is_null() {
369 output(w, idx, addr, None)
370 } else {
371 output(w, idx, addr, Some(unsafe { CString::new(data, false) }))
372 }
373}
374
375// Finally, after all that work above, we can emit a symbol.
376fn output(w: &mut Writer, idx: int, addr: *mut libc::c_void,
377 s: Option<CString>) -> IoResult<()> {
378 try!(write!(w, " {:2}: {:2$} - ", idx, addr, HEX_WIDTH));
379 match s.as_ref().and_then(|c| c.as_str()) {
380 Some(string) => try!(demangle(w, string)),
381 None => try!(write!(w, "<unknown>")),
382 }
383 w.write(&['\n' as u8])
384}
385
386/// Unwind library interface used for backtraces
387///
388/// Note that dead code is allowed as here are just bindings
389/// iOS doesn't use all of them it but adding more
390/// platform-specific configs pollutes the code too much
391#[allow(non_camel_case_types)]
392#[allow(non_snake_case)]
393#[allow(dead_code)]
394mod uw {
395 pub use self::_Unwind_Reason_Code::*;
396
397 use libc;
398
399 #[repr(C)]
400 pub enum _Unwind_Reason_Code {
401 _URC_NO_REASON = 0,
402 _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
403 _URC_FATAL_PHASE2_ERROR = 2,
404 _URC_FATAL_PHASE1_ERROR = 3,
405 _URC_NORMAL_STOP = 4,
406 _URC_END_OF_STACK = 5,
407 _URC_HANDLER_FOUND = 6,
408 _URC_INSTALL_CONTEXT = 7,
409 _URC_CONTINUE_UNWIND = 8,
410 _URC_FAILURE = 9, // used only by ARM EABI
411 }
412
413 pub enum _Unwind_Context {}
414
415 pub type _Unwind_Trace_Fn =
416 extern fn(ctx: *mut _Unwind_Context,
417 arg: *mut libc::c_void) -> _Unwind_Reason_Code;
418
419 extern {
420 // No native _Unwind_Backtrace on iOS
421 #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
422 pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
423 trace_argument: *mut libc::c_void)
424 -> _Unwind_Reason_Code;
425
426 #[cfg(all(not(target_os = "android"),
427 not(all(target_os = "linux", target_arch = "arm"))))]
428 pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
429
430 #[cfg(all(not(target_os = "android"),
431 not(all(target_os = "linux", target_arch = "arm"))))]
432 pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void)
433 -> *mut libc::c_void;
434 }
435
436 // On android, the function _Unwind_GetIP is a macro, and this is the
437 // expansion of the macro. This is all copy/pasted directly from the
438 // header file with the definition of _Unwind_GetIP.
439 #[cfg(any(target_os = "android",
440 all(target_os = "linux", target_arch = "arm")))]
441 pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
442 #[repr(C)]
443 enum _Unwind_VRS_Result {
444 _UVRSR_OK = 0,
445 _UVRSR_NOT_IMPLEMENTED = 1,
446 _UVRSR_FAILED = 2,
447 }
448 #[repr(C)]
449 enum _Unwind_VRS_RegClass {
450 _UVRSC_CORE = 0,
451 _UVRSC_VFP = 1,
452 _UVRSC_FPA = 2,
453 _UVRSC_WMMXD = 3,
454 _UVRSC_WMMXC = 4,
455 }
456 #[repr(C)]
457 enum _Unwind_VRS_DataRepresentation {
458 _UVRSD_UINT32 = 0,
459 _UVRSD_VFPX = 1,
460 _UVRSD_FPAX = 2,
461 _UVRSD_UINT64 = 3,
462 _UVRSD_FLOAT = 4,
463 _UVRSD_DOUBLE = 5,
464 }
465
466 type _Unwind_Word = libc::c_uint;
467 extern {
468 fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context,
469 klass: _Unwind_VRS_RegClass,
470 word: _Unwind_Word,
471 repr: _Unwind_VRS_DataRepresentation,
472 data: *mut libc::c_void)
473 -> _Unwind_VRS_Result;
474 }
475
476 let mut val: _Unwind_Word = 0;
477 let ptr = &mut val as *mut _Unwind_Word;
478 let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15,
479 _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
480 ptr as *mut libc::c_void);
481 (val & !1) as libc::uintptr_t
482 }
483
484 // This function also doesn't exist on Android or ARM/Linux, so make it
485 // a no-op
486 #[cfg(any(target_os = "android",
487 all(target_os = "linux", target_arch = "arm")))]
488 pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void)
489 -> *mut libc::c_void
490 {
491 pc
492 }
493}