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.
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 //! Simple backtrace functionality (to print on panic)
13 #![allow(non_camel_case_types)]
15 use io::{IoResult, Writer};
16 use iter::{Iterator, IteratorExt};
17 use option::{Some, None};
19 use result::{Ok, Err};
20 use str::{StrPrelude, from_str};
22 use unicode::char::UnicodeChar;
24 pub use self::imp::write;
26 // For now logging is turned off by default, and this function checks to see
27 // whether the magical environment variable is present to see if it's turned on.
28 pub fn log_enabled() -> bool {
29 static ENABLED: atomic::AtomicInt = atomic::INIT_ATOMIC_INT;
30 match ENABLED.load(atomic::SeqCst) {
36 let val = match os::getenv("RUST_BACKTRACE") {
40 ENABLED.store(val, atomic::SeqCst);
44 #[cfg(target_word_size = "64")] const HEX_WIDTH: uint = 18;
45 #[cfg(target_word_size = "32")] const HEX_WIDTH: uint = 10;
47 // All rust symbols are in theory lists of "::"-separated identifiers. Some
48 // assemblers, however, can't handle these characters in symbol names. To get
49 // around this, we use C++-style mangling. The mangling method is:
51 // 1. Prefix the symbol with "_ZN"
52 // 2. For each element of the path, emit the length plus the element
53 // 3. End the path with "E"
55 // For example, "_ZN4testE" => "test" and "_ZN3foo3bar" => "foo::bar".
57 // We're the ones printing our backtraces, so we can't rely on anything else to
58 // demangle our symbols. It's *much* nicer to look at demangled symbols, so
59 // this function is implemented to give us nice pretty output.
61 // Note that this demangler isn't quite as fancy as it could be. We have lots
62 // of other information in our symbols like hashes, version, type information,
63 // etc. Additionally, this doesn't handle glue symbols at all.
64 fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> {
65 // First validate the symbol. If it doesn't look like anything we're
66 // expecting, we just print it literally. Note that we must handle non-rust
67 // symbols because we could have any function in the backtrace.
69 if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") {
70 let mut chars = s.slice(3, s.len() - 1).chars();
75 i = i * 10 + c as uint - '0' as uint;
81 valid = chars.next().is_none();
83 } else if chars.by_ref().take(i - 1).count() != i - 1 {
91 // Alright, let's do this.
93 try!(writer.write_str(s));
95 let mut s = s.slice_from(3);
99 try!(writer.write_str("::"));
104 while rest.char_at(0).is_numeric() {
105 rest = rest.slice_from(1);
107 let i: uint = from_str(s.slice_to(s.len() - rest.len())).unwrap();
108 s = rest.slice_from(i);
109 rest = rest.slice_to(i);
110 while rest.len() > 0 {
111 if rest.starts_with("$") {
112 macro_rules! demangle(
113 ($($pat:expr => $demangled:expr),*) => ({
114 $(if rest.starts_with($pat) {
115 try!(writer.write_str($demangled));
116 rest = rest.slice_from($pat.len());
119 try!(writer.write_str(rest));
125 // see src/librustc/back/link.rs for these mappings
137 // in theory we can demangle any Unicode code point, but
138 // for simplicity we just catch the common ones.
145 let idx = match rest.find('$') {
149 try!(writer.write_str(rest.slice_to(idx)));
150 rest = rest.slice_from(idx);
159 /// Backtrace support built on libgcc with some extra OS-specific support
161 /// Some methods of getting a backtrace:
163 /// * The backtrace() functions on unix. It turns out this doesn't work very
164 /// well for green threads on OSX, and the address to symbol portion of it
165 /// suffers problems that are described below.
167 /// * Using libunwind. This is more difficult than it sounds because libunwind
168 /// isn't installed everywhere by default. It's also a bit of a hefty library,
169 /// so possibly not the best option. When testing, libunwind was excellent at
170 /// getting both accurate backtraces and accurate symbols across platforms.
171 /// This route was not chosen in favor of the next option, however.
173 /// * We're already using libgcc_s for exceptions in rust (triggering task
174 /// unwinding and running destructors on the stack), and it turns out that it
175 /// conveniently comes with a function that also gives us a backtrace. All of
176 /// these functions look like _Unwind_*, but it's not quite the full
177 /// repertoire of the libunwind API. Due to it already being in use, this was
178 /// the chosen route of getting a backtrace.
180 /// After choosing libgcc_s for backtraces, the sad part is that it will only
181 /// give us a stack trace of instruction pointers. Thankfully these instruction
182 /// pointers are accurate (they work for green and native threads), but it's
183 /// then up to us again to figure out how to translate these addresses to
184 /// symbols. As with before, we have a few options. Before, that, a little bit
185 /// of an interlude about symbols. This is my very limited knowledge about
186 /// symbol tables, and this information is likely slightly wrong, but the
187 /// general idea should be correct.
189 /// When talking about symbols, it's helpful to know a few things about where
190 /// symbols are located. Some symbols are located in the dynamic symbol table
191 /// of the executable which in theory means that they're available for dynamic
192 /// linking and lookup. Other symbols end up only in the local symbol table of
193 /// the file. This loosely corresponds to pub and priv functions in Rust.
195 /// Armed with this knowledge, we know that our solution for address to symbol
196 /// translation will need to consult both the local and dynamic symbol tables.
197 /// With that in mind, here's our options of translating an address to
200 /// * Use dladdr(). The original backtrace()-based idea actually uses dladdr()
201 /// behind the scenes to translate, and this is why backtrace() was not used.
202 /// Conveniently, this method works fantastically on OSX. It appears dladdr()
203 /// uses magic to consult the local symbol table, or we're putting everything
204 /// in the dynamic symbol table anyway. Regardless, for OSX, this is the
205 /// method used for translation. It's provided by the system and easy to do.o
207 /// Sadly, all other systems have a dladdr() implementation that does not
208 /// consult the local symbol table. This means that most functions are blank
209 /// because they don't have symbols. This means that we need another solution.
211 /// * Use unw_get_proc_name(). This is part of the libunwind api (not the
212 /// libgcc_s version of the libunwind api), but involves taking a dependency
213 /// to libunwind. We may pursue this route in the future if we bundle
214 /// libunwind, but libunwind was unwieldy enough that it was not chosen at
215 /// this time to provide this functionality.
217 /// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a
218 /// semi-reasonable solution. The stdlib already knows how to spawn processes,
219 /// so in theory it could invoke readelf, parse the output, and consult the
220 /// local/dynamic symbol tables from there. This ended up not getting chosen
221 /// due to the craziness of the idea plus the advent of the next option.
223 /// * Use `libbacktrace`. It turns out that this is a small library bundled in
224 /// the gcc repository which provides backtrace and symbol translation
225 /// functionality. All we really need from it is the backtrace functionality,
226 /// and we only really need this on everything that's not OSX, so this is the
227 /// chosen route for now.
229 /// In summary, the current situation uses libgcc_s to get a trace of stack
230 /// pointers, and we use dladdr() or libbacktrace to translate these addresses
231 /// to symbols. This is a bit of a hokey implementation as-is, but it works for
232 /// all unix platforms we support right now, so it at least gets the job done.
236 use io::{IoResult, Writer};
239 use option::{Some, None, Option};
240 use result::{Ok, Err};
241 use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
243 /// As always - iOS on arm uses SjLj exceptions and
244 /// _Unwind_Backtrace is even not available there. Still,
245 /// backtraces could be extracted using a backtrace function,
246 /// which thanks god is public
248 /// As mentioned in a huge comment block above, backtrace doesn't
249 /// play well with green threads, so while it is extremely nice
250 /// and simple to use it should be used only on iOS devices as the
251 /// only viable option.
252 #[cfg(all(target_os = "ios", target_arch = "arm"))]
254 pub fn write(w: &mut Writer) -> IoResult<()> {
255 use iter::{IteratorExt, range};
257 use slice::{SlicePrelude};
260 fn backtrace(buf: *mut *mut libc::c_void,
261 sz: libc::c_int) -> libc::c_int;
264 // while it doesn't requires lock for work as everything is
265 // local, it still displays much nicer backtraces when a
266 // couple of tasks panic simultaneously
267 static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;
268 let _g = unsafe { LOCK.lock() };
270 try!(writeln!(w, "stack backtrace:"));
271 // 100 lines should be enough
272 const SIZE: uint = 100;
273 let mut buf: [*mut libc::c_void, ..SIZE] = unsafe {mem::zeroed()};
274 let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as uint};
276 // skipping the first one as it is write itself
277 let iter = range(1, cnt).map(|i| {
278 print(w, i as int, buf[i])
280 result::fold(iter, (), |_, _| ())
283 #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
284 #[inline(never)] // if we know this is a function call, we can skip it when
286 pub fn write(w: &mut Writer) -> IoResult<()> {
291 writer: &'a mut (Writer+'a),
292 last_error: Option<IoError>,
295 // When using libbacktrace, we use some necessary global state, so we
296 // need to prevent more than one thread from entering this block. This
297 // is semi-reasonable in terms of printing anyway, and we know that all
298 // I/O done here is blocking I/O, not green I/O, so we don't have to
299 // worry about this being a native vs green mutex.
300 static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;
301 let _g = unsafe { LOCK.lock() };
303 try!(writeln!(w, "stack backtrace:"));
305 let mut cx = Context { writer: w, last_error: None, idx: 0 };
306 return match unsafe {
307 uw::_Unwind_Backtrace(trace_fn,
308 &mut cx as *mut Context as *mut libc::c_void)
310 uw::_URC_NO_REASON => {
311 match cx.last_error {
312 Some(err) => Err(err),
319 extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
320 arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
321 let cx: &mut Context = unsafe { mem::transmute(arg) };
322 let ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut libc::c_void };
323 // dladdr() on osx gets whiny when we use FindEnclosingFunction, and
324 // it appears to work fine without it, so we only use
325 // FindEnclosingFunction on non-osx platforms. In doing so, we get a
326 // slightly more accurate stack trace in the process.
328 // This is often because panic involves the last instruction of a
329 // function being "call std::rt::begin_unwind", with no ret
330 // instructions after it. This means that the return instruction
331 // pointer points *outside* of the calling function, and by
332 // unwinding it we go back to the original function.
333 let ip = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
336 unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
339 // Don't print out the first few frames (they're not user frames)
341 if cx.idx <= 0 { return uw::_URC_NO_REASON }
342 // Don't print ginormous backtraces
344 match write!(cx.writer, " ... <frames omitted>\n") {
346 Err(e) => { cx.last_error = Some(e); }
348 return uw::_URC_FAILURE
351 // Once we hit an error, stop trying to print more frames
352 if cx.last_error.is_some() { return uw::_URC_FAILURE }
354 match print(cx.writer, cx.idx, ip) {
356 Err(e) => { cx.last_error = Some(e); }
360 return uw::_URC_NO_REASON
364 #[cfg(any(target_os = "macos", target_os = "ios"))]
365 fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
369 dli_fname: *const libc::c_char,
370 dli_fbase: *mut libc::c_void,
371 dli_sname: *const libc::c_char,
372 dli_saddr: *mut libc::c_void,
375 fn dladdr(addr: *const libc::c_void,
376 info: *mut Dl_info) -> libc::c_int;
379 let mut info: Dl_info = unsafe { intrinsics::init() };
380 if unsafe { dladdr(addr as *const libc::c_void, &mut info) == 0 } {
381 output(w, idx,addr, None)
383 output(w, idx, addr, Some(unsafe {
384 CString::new(info.dli_sname, false)
389 #[cfg(not(any(target_os = "macos", target_os = "ios")))]
390 fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
391 use iter::IteratorExt;
393 use path::GenericPath;
396 use slice::{SlicePrelude};
398 ////////////////////////////////////////////////////////////////////////
399 // libbacktrace.h API
400 ////////////////////////////////////////////////////////////////////////
401 type backtrace_syminfo_callback =
402 extern "C" fn(data: *mut libc::c_void,
404 symname: *const libc::c_char,
405 symval: libc::uintptr_t,
406 symsize: libc::uintptr_t);
407 type backtrace_error_callback =
408 extern "C" fn(data: *mut libc::c_void,
409 msg: *const libc::c_char,
410 errnum: libc::c_int);
411 enum backtrace_state {}
412 #[link(name = "backtrace", kind = "static")]
417 fn backtrace_create_state(filename: *const libc::c_char,
418 threaded: libc::c_int,
419 error: backtrace_error_callback,
420 data: *mut libc::c_void)
421 -> *mut backtrace_state;
422 fn backtrace_syminfo(state: *mut backtrace_state,
423 addr: libc::uintptr_t,
424 cb: backtrace_syminfo_callback,
425 error: backtrace_error_callback,
426 data: *mut libc::c_void) -> libc::c_int;
429 ////////////////////////////////////////////////////////////////////////
431 ////////////////////////////////////////////////////////////////////////
433 extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
434 _errnum: libc::c_int) {
435 // do nothing for now
437 extern fn syminfo_cb(data: *mut libc::c_void,
438 _pc: libc::uintptr_t,
439 symname: *const libc::c_char,
440 _symval: libc::uintptr_t,
441 _symsize: libc::uintptr_t) {
442 let slot = data as *mut *const libc::c_char;
443 unsafe { *slot = symname; }
446 // The libbacktrace API supports creating a state, but it does not
447 // support destroying a state. I personally take this to mean that a
448 // state is meant to be created and then live forever.
450 // I would love to register an at_exit() handler which cleans up this
451 // state, but libbacktrace provides no way to do so.
453 // With these constraints, this function has a statically cached state
454 // that is calculated the first time this is requested. Remember that
455 // backtracing all happens serially (one global lock).
457 // An additionally oddity in this function is that we initialize the
458 // filename via self_exe_name() to pass to libbacktrace. It turns out
459 // that on Linux libbacktrace seamlessly gets the filename of the
460 // current executable, but this fails on freebsd. by always providing
461 // it, we make sure that libbacktrace never has a reason to not look up
462 // the symbols. The libbacktrace API also states that the filename must
463 // be in "permanent memory", so we copy it to a static and then use the
464 // static as the pointer.
466 // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't
467 // tested if this is required or not.
468 unsafe fn init_state() -> *mut backtrace_state {
469 static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state;
470 static mut LAST_FILENAME: [libc::c_char, ..256] = [0, ..256];
471 if !STATE.is_null() { return STATE }
472 let selfname = if cfg!(target_os = "freebsd") ||
473 cfg!(target_os = "dragonfly") {
478 let filename = match selfname {
480 let bytes = path.as_vec();
481 if bytes.len() < LAST_FILENAME.len() {
482 let i = bytes.iter();
483 for (slot, val) in LAST_FILENAME.iter_mut().zip(i) {
484 *slot = *val as libc::c_char;
486 LAST_FILENAME.as_ptr()
493 STATE = backtrace_create_state(filename, 0, error_cb,
498 ////////////////////////////////////////////////////////////////////////
500 ////////////////////////////////////////////////////////////////////////
502 // backtrace errors are currently swept under the rug, only I/O
503 // errors are reported
504 let state = unsafe { init_state() };
506 return output(w, idx, addr, None)
508 let mut data = 0 as *const libc::c_char;
509 let data_addr = &mut data as *mut *const libc::c_char;
511 backtrace_syminfo(state, addr as libc::uintptr_t,
512 syminfo_cb, error_cb,
513 data_addr as *mut libc::c_void)
515 if ret == 0 || data.is_null() {
516 output(w, idx, addr, None)
518 output(w, idx, addr, Some(unsafe { CString::new(data, false) }))
522 // Finally, after all that work above, we can emit a symbol.
523 fn output(w: &mut Writer, idx: int, addr: *mut libc::c_void,
524 s: Option<CString>) -> IoResult<()> {
525 try!(write!(w, " {:2}: {:2$} - ", idx, addr, super::HEX_WIDTH));
526 match s.as_ref().and_then(|c| c.as_str()) {
527 Some(string) => try!(super::demangle(w, string)),
528 None => try!(write!(w, "<unknown>")),
530 w.write(&['\n' as u8])
533 /// Unwind library interface used for backtraces
535 /// Note that the native libraries come from librustrt, not this
537 /// Note that dead code is allowed as here are just bindings
538 /// iOS doesn't use all of them it but adding more
539 /// platform-specific configs pollutes the code too much
540 #[allow(non_camel_case_types)]
541 #[allow(non_snake_case)]
544 pub use self::_Unwind_Reason_Code::*;
549 pub enum _Unwind_Reason_Code {
551 _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
552 _URC_FATAL_PHASE2_ERROR = 2,
553 _URC_FATAL_PHASE1_ERROR = 3,
554 _URC_NORMAL_STOP = 4,
555 _URC_END_OF_STACK = 5,
556 _URC_HANDLER_FOUND = 6,
557 _URC_INSTALL_CONTEXT = 7,
558 _URC_CONTINUE_UNWIND = 8,
559 _URC_FAILURE = 9, // used only by ARM EABI
562 pub enum _Unwind_Context {}
564 pub type _Unwind_Trace_Fn =
565 extern fn(ctx: *mut _Unwind_Context,
566 arg: *mut libc::c_void) -> _Unwind_Reason_Code;
569 // No native _Unwind_Backtrace on iOS
570 #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
571 pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
572 trace_argument: *mut libc::c_void)
573 -> _Unwind_Reason_Code;
575 #[cfg(all(not(target_os = "android"),
576 not(all(target_os = "linux", target_arch = "arm"))))]
577 pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
579 #[cfg(all(not(target_os = "android"),
580 not(all(target_os = "linux", target_arch = "arm"))))]
581 pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void)
582 -> *mut libc::c_void;
585 // On android, the function _Unwind_GetIP is a macro, and this is the
586 // expansion of the macro. This is all copy/pasted directly from the
587 // header file with the definition of _Unwind_GetIP.
588 #[cfg(any(target_os = "android",
589 all(target_os = "linux", target_arch = "arm")))]
590 pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
592 enum _Unwind_VRS_Result {
594 _UVRSR_NOT_IMPLEMENTED = 1,
598 enum _Unwind_VRS_RegClass {
606 enum _Unwind_VRS_DataRepresentation {
615 type _Unwind_Word = libc::c_uint;
617 fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context,
618 klass: _Unwind_VRS_RegClass,
620 repr: _Unwind_VRS_DataRepresentation,
621 data: *mut libc::c_void)
622 -> _Unwind_VRS_Result;
625 let mut val: _Unwind_Word = 0;
626 let ptr = &mut val as *mut _Unwind_Word;
627 let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15,
628 _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
629 ptr as *mut libc::c_void);
630 (val & !1) as libc::uintptr_t
633 // This function also doesn't exist on Android or ARM/Linux, so make it
635 #[cfg(any(target_os = "android",
636 all(target_os = "linux", target_arch = "arm")))]
637 pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void)
645 /// As always, windows has something very different than unix, we mainly want
646 /// to avoid having to depend too much on libunwind for windows.
648 /// If you google around, you'll find a fair bit of references to built-in
649 /// functions to get backtraces on windows. It turns out that most of these are
650 /// in an external library called dbghelp. I was unable to find this library
651 /// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent
654 /// You'll also find that there's a function called CaptureStackBackTrace
655 /// mentioned frequently (which is also easy to use), but sadly I didn't have a
656 /// copy of that function in my mingw install (maybe it was broken?). Instead,
657 /// this takes the route of using StackWalk64 in order to walk the stack.
659 #[allow(dead_code, non_snake_case)]
663 use io::{IoResult, Writer};
667 use option::{Some, None};
669 use result::{Ok, Err};
670 use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
671 use slice::SlicePrelude;
673 use dynamic_lib::DynamicLibrary;
675 #[allow(non_snake_case)]
677 fn GetCurrentProcess() -> libc::HANDLE;
678 fn GetCurrentThread() -> libc::HANDLE;
679 fn RtlCaptureContext(ctx: *mut arch::CONTEXT);
683 extern "system" fn(libc::HANDLE, u64, *mut u64,
684 *mut SYMBOL_INFO) -> libc::BOOL;
685 type SymInitializeFn =
686 extern "system" fn(libc::HANDLE, *mut libc::c_void,
687 libc::BOOL) -> libc::BOOL;
689 extern "system" fn(libc::HANDLE) -> libc::BOOL;
692 extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE,
693 *mut STACKFRAME64, *mut arch::CONTEXT,
694 *mut libc::c_void, *mut libc::c_void,
695 *mut libc::c_void, *mut libc::c_void) -> libc::BOOL;
697 const MAX_SYM_NAME: uint = 2000;
698 const IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c;
699 const IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200;
700 const IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664;
704 SizeOfStruct: libc::c_ulong,
705 TypeIndex: libc::c_ulong,
706 Reserved: [u64, ..2],
707 Index: libc::c_ulong,
710 Flags: libc::c_ulong,
713 Register: libc::c_ulong,
714 Scope: libc::c_ulong,
716 NameLen: libc::c_ulong,
717 MaxNameLen: libc::c_ulong,
718 // note that windows has this as 1, but it basically just means that
719 // the name is inline at the end of the struct. For us, we just bump
720 // the struct size up to MAX_SYM_NAME.
721 Name: [libc::c_char, ..MAX_SYM_NAME],
739 struct STACKFRAME64 {
741 AddrReturn: ADDRESS64,
742 AddrFrame: ADDRESS64,
743 AddrStack: ADDRESS64,
744 AddrBStore: ADDRESS64,
745 FuncTableEntry: *mut libc::c_void,
749 Reserved: [u64, ..3],
755 ThCallbackStack: libc::DWORD,
756 ThCallbackBStore: libc::DWORD,
757 NextCallback: libc::DWORD,
758 FramePointer: libc::DWORD,
760 KeUserCallbackDispatcher: u64,
761 SystemRangeStart: u64,
762 KiUserExceptionDispatcher: u64,
765 Reserved: [u64, ..5],
768 #[cfg(target_arch = "x86")]
772 const MAXIMUM_SUPPORTED_EXTENSION: uint = 512;
776 ContextFlags: libc::DWORD,
783 FloatSave: FLOATING_SAVE_AREA,
800 ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION],
804 pub struct FLOATING_SAVE_AREA {
805 ControlWord: libc::DWORD,
806 StatusWord: libc::DWORD,
807 TagWord: libc::DWORD,
808 ErrorOffset: libc::DWORD,
809 ErrorSelector: libc::DWORD,
810 DataOffset: libc::DWORD,
811 DataSelector: libc::DWORD,
812 RegisterArea: [u8, ..80],
813 Cr0NpxState: libc::DWORD,
816 pub fn init_frame(frame: &mut super::STACKFRAME64,
817 ctx: &CONTEXT) -> libc::DWORD {
818 frame.AddrPC.Offset = ctx.Eip as u64;
819 frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat;
820 frame.AddrStack.Offset = ctx.Esp as u64;
821 frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat;
822 frame.AddrFrame.Offset = ctx.Ebp as u64;
823 frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat;
824 super::IMAGE_FILE_MACHINE_I386
828 #[cfg(target_arch = "x86_64")]
830 use libc::{c_longlong, c_ulonglong};
831 use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG};
836 _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte
881 FltSave: FLOATING_SAVE_AREA,
883 VectorRegister: [M128A, .. 26],
884 VectorControl: DWORDLONG,
886 DebugControl: DWORDLONG,
887 LastBranchToRip: DWORDLONG,
888 LastBranchFromRip: DWORDLONG,
889 LastExceptionToRip: DWORDLONG,
890 LastExceptionFromRip: DWORDLONG,
895 _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte
901 pub struct FLOATING_SAVE_AREA {
902 _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte
903 _Dummy: [u8, ..512] // FIXME: Fill this out
906 pub fn init_frame(frame: &mut super::STACKFRAME64,
907 ctx: &CONTEXT) -> DWORD {
908 frame.AddrPC.Offset = ctx.Rip as u64;
909 frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat;
910 frame.AddrStack.Offset = ctx.Rsp as u64;
911 frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat;
912 frame.AddrFrame.Offset = ctx.Rbp as u64;
913 frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat;
914 super::IMAGE_FILE_MACHINE_AMD64
920 handle: libc::HANDLE,
921 SymCleanup: SymCleanupFn,
924 impl Drop for Cleanup {
925 fn drop(&mut self) { (self.SymCleanup)(self.handle); }
928 pub fn write(w: &mut Writer) -> IoResult<()> {
929 // According to windows documentation, all dbghelp functions are
931 static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;
932 let _g = unsafe { LOCK.lock() };
934 // Open up dbghelp.dll, we don't link to it explicitly because it can't
935 // always be found. Additionally, it's nice having fewer dependencies.
936 let path = Path::new("dbghelp.dll");
937 let lib = match DynamicLibrary::open(Some(&path)) {
939 Err(..) => return Ok(()),
942 macro_rules! sym( ($e:expr, $t:ident) => (unsafe {
943 match lib.symbol($e) {
944 Ok(f) => mem::transmute::<*mut u8, $t>(f),
945 Err(..) => return Ok(())
949 // Fetch the symbols necessary from dbghelp.dll
950 let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn);
951 let SymInitialize = sym!("SymInitialize", SymInitializeFn);
952 let SymCleanup = sym!("SymCleanup", SymCleanupFn);
953 let StackWalk64 = sym!("StackWalk64", StackWalk64Fn);
955 // Allocate necessary structures for doing the stack walk
956 let process = unsafe { GetCurrentProcess() };
957 let thread = unsafe { GetCurrentThread() };
958 let mut context: arch::CONTEXT = unsafe { intrinsics::init() };
959 unsafe { RtlCaptureContext(&mut context); }
960 let mut frame: STACKFRAME64 = unsafe { intrinsics::init() };
961 let image = arch::init_frame(&mut frame, &context);
963 // Initialize this process's symbols
964 let ret = SymInitialize(process, 0 as *mut libc::c_void, libc::TRUE);
965 if ret != libc::TRUE { return Ok(()) }
966 let _c = Cleanup { handle: process, SymCleanup: SymCleanup };
968 // And now that we're done with all the setup, do the stack walking!
970 try!(write!(w, "stack backtrace:\n"));
971 while StackWalk64(image, process, thread, &mut frame, &mut context,
972 0 as *mut libc::c_void,
973 0 as *mut libc::c_void,
974 0 as *mut libc::c_void,
975 0 as *mut libc::c_void) == libc::TRUE{
976 let addr = frame.AddrPC.Offset;
977 if addr == frame.AddrReturn.Offset || addr == 0 ||
978 frame.AddrReturn.Offset == 0 { break }
981 try!(write!(w, " {:2}: {:#2$x}", i, addr, super::HEX_WIDTH));
982 let mut info: SYMBOL_INFO = unsafe { intrinsics::init() };
983 info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong;
984 // the struct size in C. the value is different to
985 // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
986 // due to struct alignment.
987 info.SizeOfStruct = 88;
989 let mut displacement = 0u64;
990 let ret = SymFromAddr(process, addr as u64, &mut displacement,
993 if ret == libc::TRUE {
994 try!(write!(w, " - "));
995 let cstr = unsafe { CString::new(info.Name.as_ptr(), false) };
996 let bytes = cstr.as_bytes();
997 match cstr.as_str() {
998 Some(s) => try!(super::demangle(w, s)),
999 None => try!(w.write(bytes[..bytes.len()-1])),
1002 try!(w.write(&['\n' as u8]));
1012 macro_rules! t( ($a:expr, $b:expr) => ({
1013 let mut m = Vec::new();
1014 super::demangle(&mut m, $a).unwrap();
1015 assert_eq!(String::from_utf8(m).unwrap(), $b.to_string());
1021 t!("_ZN4testE", "test");
1022 t!("_ZN4test", "_ZN4test");
1023 t!("_ZN4test1a2bcE", "test::a::bc");
1027 fn demangle_dollars() {
1028 t!("_ZN4$UP$E", "Box");
1029 t!("_ZN8$UP$testE", "Boxtest");
1030 t!("_ZN8$UP$test4foobE", "Boxtest::foob");
1031 t!("_ZN8$x20test4foobE", " test::foob");
1035 fn demangle_many_dollars() {
1036 t!("_ZN12test$x20test4foobE", "test test::foob");
1037 t!("_ZN12test$UP$test4foobE", "testBoxtest::foob");