]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/backtrace.rs
doc: remove incomplete sentence
[rust.git] / src / libstd / sys / unix / backtrace.rs
1 // 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
86 use c_str::CString;
87 use io::{IoResult, Writer};
88 use libc;
89 use mem;
90 use option::Option::{mod, Some, None};
91 use result::Result::{Ok, Err};
92 use sync::{StaticMutex, MUTEX_INIT};
93
94 use 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)]
107 pub fn write(w: &mut Writer) -> IoResult<()> {
108     use iter::{IteratorExt, range};
109     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
120     static LOCK: StaticMutex = MUTEX_INIT;
121     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
139 pub fn write(w: &mut Writer) -> IoResult<()> {
140     use io::IoError;
141
142     struct Context<'a> {
143         idx: int,
144         writer: &'a mut (Writer+'a),
145         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.
153     static LOCK: StaticMutex = MUTEX_INIT;
154     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"))]
218 fn 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")))]
243 fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
244     use iter::{Iterator, IteratorExt};
245     use os;
246     use path::GenericPath;
247     use ptr::PtrExt;
248     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.
376 fn 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)]
394 mod 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 }