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.
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.
11 /// Backtrace support built on libgcc with some extra OS-specific support
13 /// Some methods of getting a backtrace:
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.
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.
25 /// * We're already using libgcc_s for exceptions in rust (triggering thread
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.
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.
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.
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
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
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.
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.
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.
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.
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.
94 use sync
::{StaticMutex, MUTEX_INIT}
;
96 use sys_common
::backtrace
::*;
98 /// As always - iOS on arm uses SjLj exceptions and
99 /// _Unwind_Backtrace is even not available there. Still,
100 /// backtraces could be extracted using a backtrace function,
101 /// which thanks god is public
103 /// As mentioned in a huge comment block above, backtrace doesn't
104 /// play well with green threads, so while it is extremely nice
105 /// and simple to use it should be used only on iOS devices as the
106 /// only viable option.
107 #[cfg(all(target_os = "ios", target_arch = "arm"))]
109 pub fn write(w
: &mut Write
) -> io
::Result
<()> {
113 fn backtrace(buf
: *mut *mut libc
::c_void
,
114 sz
: libc
::c_int
) -> libc
::c_int
;
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 threads panic simultaneously
120 static LOCK
: StaticMutex
= MUTEX_INIT
;
121 let _g
= LOCK
.lock();
123 try
!(writeln
!(w
, "stack backtrace:"));
124 // 100 lines should be enough
125 const SIZE
: usize = 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 usize}
;
129 // skipping the first one as it is write itself
130 let iter
= (1..cnt
).map(|i
| {
131 print(w
, i
as isize, buf
[i
], buf
[i
])
133 result
::fold(iter
, (), |_
, _
| ())
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
139 pub fn write(w
: &mut Write
) -> io
::Result
<()> {
142 writer
: &'a
mut (Write
+'a
),
143 last_error
: Option
<io
::Error
>,
146 // When using libbacktrace, we use some necessary global state, so we
147 // need to prevent more than one thread from entering this block. This
148 // is semi-reasonable in terms of printing anyway, and we know that all
149 // I/O done here is blocking I/O, not green I/O, so we don't have to
150 // worry about this being a native vs green mutex.
151 static LOCK
: StaticMutex
= MUTEX_INIT
;
152 let _g
= LOCK
.lock();
154 try
!(writeln
!(w
, "stack backtrace:"));
156 let mut cx
= Context { writer: w, last_error: None, idx: 0 }
;
157 return match unsafe {
158 uw
::_Unwind_Backtrace(trace_fn
,
159 &mut cx
as *mut Context
as *mut libc
::c_void
)
161 uw
::_URC_NO_REASON
=> {
162 match cx
.last_error
{
163 Some(err
) => Err(err
),
170 extern fn trace_fn(ctx
: *mut uw
::_Unwind_Context
,
171 arg
: *mut libc
::c_void
) -> uw
::_Unwind_Reason_Code
{
172 let cx
: &mut Context
= unsafe { mem::transmute(arg) }
;
173 let mut ip_before_insn
= 0;
174 let mut ip
= unsafe {
175 uw
::_Unwind_GetIPInfo(ctx
, &mut ip_before_insn
) as *mut libc
::c_void
177 if !ip
.is_null() && ip_before_insn
== 0 {
178 // this is a non-signaling frame, so `ip` refers to the address
179 // after the calling instruction. account for that.
180 ip
= (ip
as usize - 1) as *mut _
;
183 // dladdr() on osx gets whiny when we use FindEnclosingFunction, and
184 // it appears to work fine without it, so we only use
185 // FindEnclosingFunction on non-osx platforms. In doing so, we get a
186 // slightly more accurate stack trace in the process.
188 // This is often because panic involves the last instruction of a
189 // function being "call std::rt::begin_unwind", with no ret
190 // instructions after it. This means that the return instruction
191 // pointer points *outside* of the calling function, and by
192 // unwinding it we go back to the original function.
193 let symaddr
= if cfg
!(target_os
= "macos") || cfg
!(target_os
= "ios") {
196 unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
199 // Don't print out the first few frames (they're not user frames)
201 if cx
.idx
<= 0 { return uw::_URC_NO_REASON }
202 // Don't print ginormous backtraces
204 match write
!(cx
.writer
, " ... <frames omitted>\n") {
206 Err(e
) => { cx.last_error = Some(e); }
208 return uw
::_URC_FAILURE
211 // Once we hit an error, stop trying to print more frames
212 if cx
.last_error
.is_some() { return uw::_URC_FAILURE }
214 match print(cx
.writer
, cx
.idx
, ip
, symaddr
) {
216 Err(e
) => { cx.last_error = Some(e); }
220 return uw
::_URC_NO_REASON
224 #[cfg(any(target_os = "macos", target_os = "ios"))]
225 fn print(w
: &mut Write
, idx
: isize, addr
: *mut libc
::c_void
,
226 _symaddr
: *mut libc
::c_void
) -> io
::Result
<()> {
230 dli_fname
: *const libc
::c_char
,
231 dli_fbase
: *mut libc
::c_void
,
232 dli_sname
: *const libc
::c_char
,
233 dli_saddr
: *mut libc
::c_void
,
236 fn dladdr(addr
: *const libc
::c_void
,
237 info
: *mut Dl_info
) -> libc
::c_int
;
240 let mut info
: Dl_info
= unsafe { intrinsics::init() }
;
241 if unsafe { dladdr(addr, &mut info) == 0 }
{
242 output(w
, idx
,addr
, None
)
244 output(w
, idx
, addr
, Some(unsafe {
245 CStr
::from_ptr(info
.dli_sname
).to_bytes()
250 #[cfg(not(any(target_os = "macos", target_os = "ios")))]
251 fn print(w
: &mut Write
, idx
: isize, addr
: *mut libc
::c_void
,
252 symaddr
: *mut libc
::c_void
) -> io
::Result
<()> {
254 use os
::unix
::prelude
::*;
257 ////////////////////////////////////////////////////////////////////////
258 // libbacktrace.h API
259 ////////////////////////////////////////////////////////////////////////
260 type backtrace_syminfo_callback
=
261 extern "C" fn(data
: *mut libc
::c_void
,
263 symname
: *const libc
::c_char
,
264 symval
: libc
::uintptr_t
,
265 symsize
: libc
::uintptr_t
);
266 type backtrace_full_callback
=
267 extern "C" fn(data
: *mut libc
::c_void
,
269 filename
: *const libc
::c_char
,
271 function
: *const libc
::c_char
) -> libc
::c_int
;
272 type backtrace_error_callback
=
273 extern "C" fn(data
: *mut libc
::c_void
,
274 msg
: *const libc
::c_char
,
275 errnum
: libc
::c_int
);
276 enum backtrace_state {}
277 #[link(name = "backtrace", kind = "static")]
282 fn backtrace_create_state(filename
: *const libc
::c_char
,
283 threaded
: libc
::c_int
,
284 error
: backtrace_error_callback
,
285 data
: *mut libc
::c_void
)
286 -> *mut backtrace_state
;
287 fn backtrace_syminfo(state
: *mut backtrace_state
,
288 addr
: libc
::uintptr_t
,
289 cb
: backtrace_syminfo_callback
,
290 error
: backtrace_error_callback
,
291 data
: *mut libc
::c_void
) -> libc
::c_int
;
292 fn backtrace_pcinfo(state
: *mut backtrace_state
,
293 addr
: libc
::uintptr_t
,
294 cb
: backtrace_full_callback
,
295 error
: backtrace_error_callback
,
296 data
: *mut libc
::c_void
) -> libc
::c_int
;
299 ////////////////////////////////////////////////////////////////////////
301 ////////////////////////////////////////////////////////////////////////
303 type FileLine
= (*const libc
::c_char
, libc
::c_int
);
305 extern fn error_cb(_data
: *mut libc
::c_void
, _msg
: *const libc
::c_char
,
306 _errnum
: libc
::c_int
) {
307 // do nothing for now
309 extern fn syminfo_cb(data
: *mut libc
::c_void
,
310 _pc
: libc
::uintptr_t
,
311 symname
: *const libc
::c_char
,
312 _symval
: libc
::uintptr_t
,
313 _symsize
: libc
::uintptr_t
) {
314 let slot
= data
as *mut *const libc
::c_char
;
315 unsafe { *slot = symname; }
317 extern fn pcinfo_cb(data
: *mut libc
::c_void
,
318 _pc
: libc
::uintptr_t
,
319 filename
: *const libc
::c_char
,
321 _function
: *const libc
::c_char
) -> libc
::c_int
{
322 if !filename
.is_null() {
323 let slot
= data
as *mut &mut [FileLine
];
324 let buffer
= unsafe {ptr::read(slot)}
;
326 // if the buffer is not full, add file:line to the buffer
327 // and adjust the buffer for next possible calls to pcinfo_cb.
328 if !buffer
.is_empty() {
329 buffer
[0] = (filename
, lineno
);
330 unsafe { ptr::write(slot, &mut buffer[1..]); }
337 // The libbacktrace API supports creating a state, but it does not
338 // support destroying a state. I personally take this to mean that a
339 // state is meant to be created and then live forever.
341 // I would love to register an at_exit() handler which cleans up this
342 // state, but libbacktrace provides no way to do so.
344 // With these constraints, this function has a statically cached state
345 // that is calculated the first time this is requested. Remember that
346 // backtracing all happens serially (one global lock).
348 // An additionally oddity in this function is that we initialize the
349 // filename via self_exe_name() to pass to libbacktrace. It turns out
350 // that on Linux libbacktrace seamlessly gets the filename of the
351 // current executable, but this fails on freebsd. by always providing
352 // it, we make sure that libbacktrace never has a reason to not look up
353 // the symbols. The libbacktrace API also states that the filename must
354 // be in "permanent memory", so we copy it to a static and then use the
355 // static as the pointer.
357 // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't
358 // tested if this is required or not.
359 unsafe fn init_state() -> *mut backtrace_state
{
360 static mut STATE
: *mut backtrace_state
= 0 as *mut backtrace_state
;
361 static mut LAST_FILENAME
: [libc
::c_char
; 256] = [0; 256];
362 if !STATE
.is_null() { return STATE }
363 let selfname
= if cfg
!(target_os
= "freebsd") ||
364 cfg
!(target_os
= "dragonfly") ||
365 cfg
!(target_os
= "bitrig") ||
366 cfg
!(target_os
= "openbsd") {
367 env
::current_exe().ok()
371 let filename
= match selfname
{
373 let bytes
= path
.as_os_str().as_bytes();
374 if bytes
.len() < LAST_FILENAME
.len() {
375 let i
= bytes
.iter();
376 for (slot
, val
) in LAST_FILENAME
.iter_mut().zip(i
) {
377 *slot
= *val
as libc
::c_char
;
379 LAST_FILENAME
.as_ptr()
386 STATE
= backtrace_create_state(filename
, 0, error_cb
,
391 ////////////////////////////////////////////////////////////////////////
393 ////////////////////////////////////////////////////////////////////////
395 // backtrace errors are currently swept under the rug, only I/O
396 // errors are reported
397 let state
= unsafe { init_state() }
;
399 return output(w
, idx
, addr
, None
)
401 let mut data
= ptr
::null();
402 let data_addr
= &mut data
as *mut *const libc
::c_char
;
404 backtrace_syminfo(state
, symaddr
as libc
::uintptr_t
,
405 syminfo_cb
, error_cb
,
406 data_addr
as *mut libc
::c_void
)
408 if ret
== 0 || data
.is_null() {
409 try
!(output(w
, idx
, addr
, None
));
411 try
!(output(w
, idx
, addr
, Some(unsafe { CStr::from_ptr(data).to_bytes() }
)));
414 // pcinfo may return an arbitrary number of file:line pairs,
415 // in the order of stack trace (i.e. inlined calls first).
416 // in order to avoid allocation, we stack-allocate a fixed size of entries.
417 const FILELINE_SIZE
: usize = 32;
418 let mut fileline_buf
= [(ptr
::null(), -1); FILELINE_SIZE
];
422 let mut fileline_win
: &mut [FileLine
] = &mut fileline_buf
;
423 let fileline_addr
= &mut fileline_win
as *mut &mut [FileLine
];
425 backtrace_pcinfo(state
, addr
as libc
::uintptr_t
,
427 fileline_addr
as *mut libc
::c_void
)
429 fileline_count
= FILELINE_SIZE
- fileline_win
.len();
432 for (i
, &(file
, line
)) in fileline_buf
[..fileline_count
].iter().enumerate() {
433 if file
.is_null() { continue; }
// just to be sure
434 let file
= unsafe { CStr::from_ptr(file).to_bytes() }
;
435 try
!(output_fileline(w
, file
, line
, i
== FILELINE_SIZE
- 1));
442 // Finally, after all that work above, we can emit a symbol.
443 fn output(w
: &mut Write
, idx
: isize, addr
: *mut libc
::c_void
,
444 s
: Option
<&[u8]>) -> io
::Result
<()> {
445 try
!(write
!(w
, " {:2}: {:2$?} - ", idx
, addr
, HEX_WIDTH
));
446 match s
.and_then(|s
| str::from_utf8(s
).ok()) {
447 Some(string
) => try
!(demangle(w
, string
)),
448 None
=> try
!(write
!(w
, "<unknown>")),
450 w
.write_all(&['
\n'
as u8])
454 fn output_fileline(w
: &mut Write
, file
: &[u8], line
: libc
::c_int
,
455 more
: bool
) -> io
::Result
<()> {
456 let file
= str::from_utf8(file
).unwrap_or("<unknown>");
457 // prior line: " ##: {:2$} - func"
458 try
!(write
!(w
, " {:3$}at {}:{}", "", file
, line
, HEX_WIDTH
));
460 try
!(write
!(w
, " <... and possibly more>"));
462 w
.write_all(&['
\n'
as u8])
465 /// Unwind library interface used for backtraces
467 /// Note that dead code is allowed as here are just bindings
468 /// iOS doesn't use all of them it but adding more
469 /// platform-specific configs pollutes the code too much
470 #[allow(non_camel_case_types)]
471 #[allow(non_snake_case)]
474 pub use self::_Unwind_Reason_Code
::*;
479 pub enum _Unwind_Reason_Code
{
481 _URC_FOREIGN_EXCEPTION_CAUGHT
= 1,
482 _URC_FATAL_PHASE2_ERROR
= 2,
483 _URC_FATAL_PHASE1_ERROR
= 3,
484 _URC_NORMAL_STOP
= 4,
485 _URC_END_OF_STACK
= 5,
486 _URC_HANDLER_FOUND
= 6,
487 _URC_INSTALL_CONTEXT
= 7,
488 _URC_CONTINUE_UNWIND
= 8,
489 _URC_FAILURE
= 9, // used only by ARM EABI
492 pub enum _Unwind_Context {}
494 pub type _Unwind_Trace_Fn
=
495 extern fn(ctx
: *mut _Unwind_Context
,
496 arg
: *mut libc
::c_void
) -> _Unwind_Reason_Code
;
499 // No native _Unwind_Backtrace on iOS
500 #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
501 pub fn _Unwind_Backtrace(trace
: _Unwind_Trace_Fn
,
502 trace_argument
: *mut libc
::c_void
)
503 -> _Unwind_Reason_Code
;
505 // available since GCC 4.2.0, should be fine for our purpose
506 #[cfg(all(not(all(target_os = "android", target_arch = "arm")),
507 not(all(target_os
= "linux", target_arch
= "arm"))))]
508 pub fn _Unwind_GetIPInfo(ctx
: *mut _Unwind_Context
,
509 ip_before_insn
: *mut libc
::c_int
)
512 #[cfg(all(not(target_os = "android"),
513 not(all(target_os
= "linux", target_arch
= "arm"))))]
514 pub fn _Unwind_FindEnclosingFunction(pc
: *mut libc
::c_void
)
515 -> *mut libc
::c_void
;
518 // On android, the function _Unwind_GetIP is a macro, and this is the
519 // expansion of the macro. This is all copy/pasted directly from the
520 // header file with the definition of _Unwind_GetIP.
521 #[cfg(any(all(target_os = "android", target_arch = "arm"),
522 all(target_os
= "linux", target_arch
= "arm")))]
523 pub unsafe fn _Unwind_GetIP(ctx
: *mut _Unwind_Context
) -> libc
::uintptr_t
{
525 enum _Unwind_VRS_Result
{
527 _UVRSR_NOT_IMPLEMENTED
= 1,
531 enum _Unwind_VRS_RegClass
{
539 enum _Unwind_VRS_DataRepresentation
{
548 type _Unwind_Word
= libc
::c_uint
;
550 fn _Unwind_VRS_Get(ctx
: *mut _Unwind_Context
,
551 klass
: _Unwind_VRS_RegClass
,
553 repr
: _Unwind_VRS_DataRepresentation
,
554 data
: *mut libc
::c_void
)
555 -> _Unwind_VRS_Result
;
558 let mut val
: _Unwind_Word
= 0;
559 let ptr
= &mut val
as *mut _Unwind_Word
;
560 let _
= _Unwind_VRS_Get(ctx
, _Unwind_VRS_RegClass
::_UVRSC_CORE
, 15,
561 _Unwind_VRS_DataRepresentation
::_UVRSD_UINT32
,
562 ptr
as *mut libc
::c_void
);
563 (val
& !1) as libc
::uintptr_t
566 // This function doesn't exist on Android or ARM/Linux, so make it same
568 #[cfg(any(all(target_os = "android", target_arch = "arm"),
569 all(target_os
= "linux", target_arch
= "arm")))]
570 pub unsafe fn _Unwind_GetIPInfo(ctx
: *mut _Unwind_Context
,
571 ip_before_insn
: *mut libc
::c_int
)
578 // This function also doesn't exist on Android or ARM/Linux, so make it
580 #[cfg(any(target_os = "android",
581 all(target_os
= "linux", target_arch
= "arm")))]
582 pub unsafe fn _Unwind_FindEnclosingFunction(pc
: *mut libc
::c_void
)