(index<- ) ./libstd/rt/backtrace.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
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 //! Simple backtrace functionality (to print on failure)
12
13 #![allow(non_camel_case_types)]
14
15 use char::Char;
16 use container::Container;
17 use from_str::from_str;
18 use io::{IoResult, Writer};
19 use iter::Iterator;
20 use option::{Some, None};
21 use os;
22 use result::{Ok, Err};
23 use str::StrSlice;
24 use sync::atomics;
25
26 pub use self::imp::write;
27
28 // For now logging is turned off by default, and this function checks to see
29 // whether the magical environment variable is present to see if it's turned on.
30 pub fn log_enabled() -> bool {
31 static mut ENABLED: atomics::AtomicInt = atomics::INIT_ATOMIC_INT;
32 unsafe {
33 match ENABLED.load(atomics::SeqCst) {
34 1 => return false,
35 2 => return true,
36 _ => {}
37 }
38 }
39
40 let val = match os::getenv("RUST_BACKTRACE") {
41 Some(..) => 2,
42 None => 1,
43 };
44 unsafe { ENABLED.store(val, atomics::SeqCst); }
45 val == 2
46 }
47
48 #[cfg(target_word_size = "64")] static HEX_WIDTH: uint = 18;
49 #[cfg(target_word_size = "32")] static HEX_WIDTH: uint = 10;
50
51 // All rust symbols are in theory lists of "::"-separated identifiers. Some
52 // assemblers, however, can't handle these characters in symbol names. To get
53 // around this, we use C++-style mangling. The mangling method is:
54 //
55 // 1. Prefix the symbol with "_ZN"
56 // 2. For each element of the path, emit the length plus the element
57 // 3. End the path with "E"
58 //
59 // For example, "_ZN4testE" => "test" and "_ZN3foo3bar" => "foo::bar".
60 //
61 // We're the ones printing our backtraces, so we can't rely on anything else to
62 // demangle our symbols. It's *much* nicer to look at demangled symbols, so
63 // this function is implemented to give us nice pretty output.
64 //
65 // Note that this demangler isn't quite as fancy as it could be. We have lots
66 // of other information in our symbols like hashes, version, type information,
67 // etc. Additionally, this doesn't handle glue symbols at all.
68 fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> {
69 // First validate the symbol. If it doesn't look like anything we're
70 // expecting, we just print it literally. Note that we must handle non-rust
71 // symbols because we could have any function in the backtrace.
72 let mut valid = true;
73 if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") {
74 let mut chars = s.slice(3, s.len() - 1).chars();
75 while valid {
76 let mut i = 0;
77 for c in chars {
78 if c.is_digit() {
79 i = i * 10 + c as uint - '0' as uint;
80 } else {
81 break
82 }
83 }
84 if i == 0 {
85 valid = chars.next().is_none();
86 break
87 } else if chars.by_ref().take(i - 1).len() != i - 1 {
88 valid = false;
89 }
90 }
91 } else {
92 valid = false;
93 }
94
95 // Alright, let's do this.
96 if !valid {
97 try!(writer.write_str(s));
98 } else {
99 let mut s = s.slice_from(3);
100 let mut first = true;
101 while s.len() > 1 {
102 if !first {
103 try!(writer.write_str("::"));
104 } else {
105 first = false;
106 }
107 let mut rest = s;
108 while rest.char_at(0).is_digit() {
109 rest = rest.slice_from(1);
110 }
111 let i: uint = from_str(s.slice_to(s.len() - rest.len())).unwrap();
112 s = rest.slice_from(i);
113 rest = rest.slice_to(i);
114 while rest.len() > 0 {
115 if rest.starts_with("$") {
116 macro_rules! demangle(
117 ($($pat:expr => $demangled:expr),*) => ({
118 $(if rest.starts_with($pat) {
119 try!(writer.write_str($demangled));
120 rest = rest.slice_from($pat.len());
121 } else)*
122 {
123 try!(writer.write_str(rest));
124 break;
125 }
126
127 })
128 )
129 // see src/librustc/back/link.rs for these mappings
130 demangle! (
131 "$SP$" => "@",
132 "$UP$" => "Box",
133 "$RP$" => "*",
134 "$BP$" => "&",
135 "$LT$" => "<",
136 "$GT$" => ">",
137 "$LP$" => "(",
138 "$RP$" => ")",
139 "$C$" => ",",
140
141 // in theory we can demangle any unicode code point, but
142 // for simplicity we just catch the common ones.
143 "$x20" => " ",
144 "$x27" => "'",
145 "$x5b" => "[",
146 "$x5d" => "]"
147 )
148 } else {
149 let idx = match rest.find('$') {
150 None => rest.len(),
151 Some(i) => i,
152 };
153 try!(writer.write_str(rest.slice_to(idx)));
154 rest = rest.slice_from(idx);
155 }
156 }
157 }
158 }
159
160 Ok(())
161 }
162
163 /// Backtrace support built on libgcc with some extra OS-specific support
164 ///
165 /// Some methods of getting a backtrace:
166 ///
167 /// * The backtrace() functions on unix. It turns out this doesn't work very
168 /// well for green threads on OSX, and the address to symbol portion of it
169 /// suffers problems that are described below.
170 ///
171 /// * Using libunwind. This is more difficult than it sounds because libunwind
172 /// isn't installed everywhere by default. It's also a bit of a hefty library,
173 /// so possibly not the best option. When testing, libunwind was excellent at
174 /// getting both accurate backtraces and accurate symbols across platforms.
175 /// This route was not chosen in favor of the next option, however.
176 ///
177 /// * We're already using libgcc_s for exceptions in rust (triggering task
178 /// unwinding and running destructors on the stack), and it turns out that it
179 /// conveniently comes with a function that also gives us a backtrace. All of
180 /// these functions look like _Unwind_*, but it's not quite the full
181 /// repertoire of the libunwind API. Due to it already being in use, this was
182 /// the chosen route of getting a backtrace.
183 ///
184 /// After choosing libgcc_s for backtraces, the sad part is that it will only
185 /// give us a stack trace of instruction pointers. Thankfully these instruction
186 /// pointers are accurate (they work for green and native threads), but it's
187 /// then up to us again to figure out how to translate these addresses to
188 /// symbols. As with before, we have a few options. Before, that, a little bit
189 /// of an interlude about symbols. This is my very limited knowledge about
190 /// symbol tables, and this information is likely slightly wrong, but the
191 /// general idea should be correct.
192 ///
193 /// When talking about symbols, it's helpful to know a few things about where
194 /// symbols are located. Some symbols are located in the dynamic symbol table
195 /// of the executable which in theory means that they're available for dynamic
196 /// linking and lookup. Other symbols end up only in the local symbol table of
197 /// the file. This loosely corresponds to pub and priv functions in Rust.
198 ///
199 /// Armed with this knowledge, we know that our solution for address to symbol
200 /// translation will need to consult both the local and dynamic symbol tables.
201 /// With that in mind, here's our options of translating an address to
202 /// a symbol.
203 ///
204 /// * Use dladdr(). The original backtrace()-based idea actually uses dladdr()
205 /// behind the scenes to translate, and this is why backtrace() was not used.
206 /// Conveniently, this method works fantastically on OSX. It appears dladdr()
207 /// uses magic to consult the local symbol table, or we're putting everything
208 /// in the dynamic symbol table anyway. Regardless, for OSX, this is the
209 /// method used for translation. It's provided by the system and easy to do.o
210 ///
211 /// Sadly, all other systems have a dladdr() implementation that does not
212 /// consult the local symbol table. This means that most functions are blank
213 /// because they don't have symbols. This means that we need another solution.
214 ///
215 /// * Use unw_get_proc_name(). This is part of the libunwind api (not the
216 /// libgcc_s version of the libunwind api), but involves taking a dependency
217 /// to libunwind. We may pursue this route in the future if we bundle
218 /// libunwind, but libunwind was unwieldy enough that it was not chosen at
219 /// this time to provide this functionality.
220 ///
221 /// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a
222 /// semi-reasonable solution. The stdlib already knows how to spawn processes,
223 /// so in theory it could invoke readelf, parse the output, and consult the
224 /// local/dynamic symbol tables from there. This ended up not getting chosen
225 /// due to the craziness of the idea plus the advent of the next option.
226 ///
227 /// * Use `libbacktrace`. It turns out that this is a small library bundled in
228 /// the gcc repository which provides backtrace and symbol translation
229 /// functionality. All we really need from it is the backtrace functionality,
230 /// and we only really need this on everything that's not OSX, so this is the
231 /// chosen route for now.
232 ///
233 /// In summary, the current situation uses libgcc_s to get a trace of stack
234 /// pointers, and we use dladdr() or libbacktrace to translate these addresses
235 /// to symbols. This is a bit of a hokey implementation as-is, but it works for
236 /// all unix platforms we support right now, so it at least gets the job done.
237 #[cfg(unix)]
238 mod imp {
239 use c_str::CString;
240 use cast;
241 use io::{IoResult, IoError, Writer};
242 use libc;
243 use option::{Some, None, Option};
244 use result::{Ok, Err};
245 use unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
246 use uw = rt::libunwind;
247
248 struct Context<'a> {
249 idx: int,
250 writer: &'a mut Writer,
251 last_error: Option<IoError>,
252 }
253
254 #[inline(never)] // if we know this is a function call, we can skip it when
255 // tracing
256 pub fn write(w: &mut Writer) -> IoResult<()> {
257 // When using libbacktrace, we use some necessary global state, so we
258 // need to prevent more than one thread from entering this block. This
259 // is semi-reasonable in terms of printing anyway, and we know that all
260 // I/O done here is blocking I/O, not green I/O, so we don't have to
261 // worry about this being a native vs green mutex.
262 static mut LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;
263 let _g = unsafe { LOCK.lock() };
264
265 try!(writeln!(w, "stack backtrace:"));
266
267 let mut cx = Context { writer: w, last_error: None, idx: 0 };
268 return match unsafe {
269 uw::_Unwind_Backtrace(trace_fn,
270 &mut cx as *mut Context as *libc::c_void)
271 } {
272 uw::_URC_NO_REASON => {
273 match cx.last_error {
274 Some(err) => Err(err),
275 None => Ok(())
276 }
277 }
278 _ => Ok(()),
279 };
280
281 extern fn trace_fn(ctx: *uw::_Unwind_Context,
282 arg: *libc::c_void) -> uw::_Unwind_Reason_Code {
283 let cx: &mut Context = unsafe { cast::transmute(arg) };
284 let ip = unsafe { uw::_Unwind_GetIP(ctx) as *libc::c_void };
285 // dladdr() on osx gets whiny when we use FindEnclosingFunction, and
286 // it appears to work fine without it, so we only use
287 // FindEnclosingFunction on non-osx platforms. In doing so, we get a
288 // slightly more accurate stack trace in the process.
289 //
290 // This is often because failure involves the last instruction of a
291 // function being "call std::rt::begin_unwind", with no ret
292 // instructions after it. This means that the return instruction
293 // pointer points *outside* of the calling function, and by
294 // unwinding it we go back to the original function.
295 let ip = if cfg!(target_os = "macos") {
296 ip
297 } else {
298 unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
299 };
300
301 // Don't print out the first few frames (they're not user frames)
302 cx.idx += 1;
303 if cx.idx <= 0 { return uw::_URC_NO_REASON }
304 // Don't print ginormous backtraces
305 if cx.idx > 100 {
306 match write!(cx.writer, " ... <frames omitted>\n") {
307 Ok(()) => {}
308 Err(e) => { cx.last_error = Some(e); }
309 }
310 return uw::_URC_FAILURE
311 }
312
313 // Once we hit an error, stop trying to print more frames
314 if cx.last_error.is_some() { return uw::_URC_FAILURE }
315
316 match print(cx.writer, cx.idx, ip) {
317 Ok(()) => {}
318 Err(e) => { cx.last_error = Some(e); }
319 }
320
321 // keep going
322 return uw::_URC_NO_REASON
323 }
324 }
325
326 #[cfg(target_os = "macos")]
327 fn print(w: &mut Writer, idx: int, addr: *libc::c_void) -> IoResult<()> {
328 use intrinsics;
329 struct Dl_info {
330 dli_fname: *libc::c_char,
331 dli_fbase: *libc::c_void,
332 dli_sname: *libc::c_char,
333 dli_saddr: *libc::c_void,
334 }
335 extern {
336 fn dladdr(addr: *libc::c_void,
337 info: *mut Dl_info) -> libc::c_int;
338 }
339
340 let mut info: Dl_info = unsafe { intrinsics::init() };
341 if unsafe { dladdr(addr, &mut info) == 0 } {
342 output(w, idx,addr, None)
343 } else {
344 output(w, idx, addr, Some(unsafe {
345 CString::new(info.dli_sname, false)
346 }))
347 }
348 }
349
350 #[cfg(not(target_os = "macos"))]
351 fn print(w: &mut Writer, idx: int, addr: *libc::c_void) -> IoResult<()> {
352 use container::Container;
353 use iter::Iterator;
354 use os;
355 use path::GenericPath;
356 use ptr::RawPtr;
357 use ptr;
358 use slice::{ImmutableVector, MutableVector};
359
360 ////////////////////////////////////////////////////////////////////////
361 // libbacktrace.h API
362 ////////////////////////////////////////////////////////////////////////
363 type backtrace_syminfo_callback =
364 extern "C" fn(data: *mut libc::c_void,
365 pc: libc::uintptr_t,
366 symname: *libc::c_char,
367 symval: libc::uintptr_t,
368 symsize: libc::uintptr_t);
369 type backtrace_error_callback =
370 extern "C" fn(data: *mut libc::c_void,
371 msg: *libc::c_char,
372 errnum: libc::c_int);
373 enum backtrace_state {}
374 #[link(name = "backtrace", kind = "static")]
375 extern {
376 fn backtrace_create_state(filename: *libc::c_char,
377 threaded: libc::c_int,
378 error: backtrace_error_callback,
379 data: *mut libc::c_void)
380 -> *mut backtrace_state;
381 fn backtrace_syminfo(state: *mut backtrace_state,
382 addr: libc::uintptr_t,
383 cb: backtrace_syminfo_callback,
384 error: backtrace_error_callback,
385 data: *mut libc::c_void) -> libc::c_int;
386 }
387
388 ////////////////////////////////////////////////////////////////////////
389 // helper callbacks
390 ////////////////////////////////////////////////////////////////////////
391
392 extern fn error_cb(_data: *mut libc::c_void, _msg: *libc::c_char,
393 _errnum: libc::c_int) {
394 // do nothing for now
395 }
396 extern fn syminfo_cb(data: *mut libc::c_void,
397 _pc: libc::uintptr_t,
398 symname: *libc::c_char,
399 _symval: libc::uintptr_t,
400 _symsize: libc::uintptr_t) {
401 let slot = data as *mut *libc::c_char;
402 unsafe { *slot = symname; }
403 }
404
405 // The libbacktrace API supports creating a state, but it does not
406 // support destroying a state. I personally take this to mean that a
407 // state is meant to be created and then live forever.
408 //
409 // I would love to register an at_exit() handler which cleans up this
410 // state, but libbacktrace provides no way to do so.
411 //
412 // With these constraints, this function has a statically cached state
413 // that is calculated the first time this is requested. Remember that
414 // backtracing all happens serially (one global lock).
415 //
416 // An additionally oddity in this function is that we initialize the
417 // filename via self_exe_name() to pass to libbacktrace. It turns out
418 // that on linux libbacktrace seamlessly gets the filename of the
419 // current executable, but this fails on freebsd. by always providing
420 // it, we make sure that libbacktrace never has a reason to not look up
421 // the symbols. The libbacktrace API also states that the filename must
422 // be in "permanent memory", so we copy it to a static and then use the
423 // static as the pointer.
424 unsafe fn init_state() -> *mut backtrace_state {
425 static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state;
426 static mut LAST_FILENAME: [libc::c_char, ..256] = [0, ..256];
427 if !STATE.is_null() { return STATE }
428 let selfname = if cfg!(target_os = "freebsd") {
429 os::self_exe_name()
430 } else {
431 None
432 };
433 let filename = match selfname {
434 Some(path) => {
435 let bytes = path.as_vec();
436 if bytes.len() < LAST_FILENAME.len() {
437 let i = bytes.iter();
438 for (slot, val) in LAST_FILENAME.mut_iter().zip(i) {
439 *slot = *val as libc::c_char;
440 }
441 LAST_FILENAME.as_ptr()
442 } else {
443 ptr::null()
444 }
445 }
446 None => ptr::null(),
447 };
448 STATE = backtrace_create_state(filename, 0, error_cb,
449 ptr::mut_null());
450 return STATE
451 }
452
453 ////////////////////////////////////////////////////////////////////////
454 // translation
455 ////////////////////////////////////////////////////////////////////////
456
457 // backtrace errors are currently swept under the rug, only I/O
458 // errors are reported
459 let state = unsafe { init_state() };
460 if state.is_null() {
461 return output(w, idx, addr, None)
462 }
463 let mut data = 0 as *libc::c_char;
464 let data_addr = &mut data as *mut *libc::c_char;
465 let ret = unsafe {
466 backtrace_syminfo(state, addr as libc::uintptr_t,
467 syminfo_cb, error_cb,
468 data_addr as *mut libc::c_void)
469 };
470 if ret == 0 || data.is_null() {
471 output(w, idx, addr, None)
472 } else {
473 output(w, idx, addr, Some(unsafe { CString::new(data, false) }))
474 }
475 }
476
477 // Finally, after all that work above, we can emit a symbol.
478 fn output(w: &mut Writer, idx: int, addr: *libc::c_void,
479 s: Option<CString>) -> IoResult<()> {
480 try!(write!(w, " {:2}: {:2$} - ", idx, addr, super::HEX_WIDTH));
481 match s.as_ref().and_then(|c| c.as_str()) {
482 Some(string) => try!(super::demangle(w, string)),
483 None => try!(write!(w, "<unknown>")),
484 }
485 w.write(['\n' as u8])
486 }
487 }
488
489 /// As always, windows has something very different than unix, we mainly want
490 /// to avoid having to depend too much on libunwind for windows.
491 ///
492 /// If you google around, you'll find a fair bit of references to built-in
493 /// functions to get backtraces on windows. It turns out that most of these are
494 /// in an external library called dbghelp. I was unable to find this library
495 /// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent
496 /// of it.
497 ///
498 /// You'll also find that there's a function called CaptureStackBackTrace
499 /// mentioned frequently (which is also easy to use), but sadly I didn't have a
500 /// copy of that function in my mingw install (maybe it was broken?). Instead,
501 /// this takes the route of using StackWalk64 in order to walk the stack.
502 #[cfg(windows)]
503 #[allow(dead_code, uppercase_variables)]
504 mod imp {
505 use c_str::CString;
506 use container::Container;
507 use io::{IoResult, Writer};
508 use iter::Iterator;
509 use libc;
510 use mem;
511 use ops::Drop;
512 use option::{Some, None};
513 use path::Path;
514 use result::{Ok, Err};
515 use str::StrSlice;
516 use unstable::dynamic_lib::DynamicLibrary;
517 use intrinsics;
518 use unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
519 use slice::ImmutableVector;
520
521 extern "system" {
522 fn GetCurrentProcess() -> libc::HANDLE;
523 fn GetCurrentThread() -> libc::HANDLE;
524 fn RtlCaptureContext(ctx: *mut arch::CONTEXT);
525 }
526
527 type SymFromAddrFn =
528 extern "system" fn(libc::HANDLE, u64, *mut u64,
529 *mut SYMBOL_INFO) -> libc::BOOL;
530 type SymInitializeFn =
531 extern "system" fn(libc::HANDLE, *libc::c_void,
532 libc::BOOL) -> libc::BOOL;
533 type SymCleanupFn =
534 extern "system" fn(libc::HANDLE) -> libc::BOOL;
535
536 type StackWalk64Fn =
537 extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE,
538 *mut STACKFRAME64, *mut arch::CONTEXT,
539 *libc::c_void, *libc::c_void,
540 *libc::c_void, *libc::c_void) -> libc::BOOL;
541
542 static MAX_SYM_NAME: uint = 2000;
543 static IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c;
544 static IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200;
545 static IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664;
546
547 #[packed]
548 struct SYMBOL_INFO {
549 SizeOfStruct: libc::c_ulong,
550 TypeIndex: libc::c_ulong,
551 Reserved: [u64, ..2],
552 Index: libc::c_ulong,
553 Size: libc::c_ulong,
554 ModBase: u64,
555 Flags: libc::c_ulong,
556 Value: u64,
557 Address: u64,
558 Register: libc::c_ulong,
559 Scope: libc::c_ulong,
560 Tag: libc::c_ulong,
561 NameLen: libc::c_ulong,
562 MaxNameLen: libc::c_ulong,
563 // note that windows has this as 1, but it basically just means that
564 // the name is inline at the end of the struct. For us, we just bump
565 // the struct size up to MAX_SYM_NAME.
566 Name: [libc::c_char, ..MAX_SYM_NAME],
567 }
568
569 #[repr(C)]
570 enum ADDRESS_MODE {
571 AddrMode1616,
572 AddrMode1632,
573 AddrModeReal,
574 AddrModeFlat,
575 }
576
577 struct ADDRESS64 {
578 Offset: u64,
579 Segment: u16,
580 Mode: ADDRESS_MODE,
581 }
582
583 struct STACKFRAME64 {
584 AddrPC: ADDRESS64,
585 AddrReturn: ADDRESS64,
586 AddrFrame: ADDRESS64,
587 AddrStack: ADDRESS64,
588 AddrBStore: ADDRESS64,
589 FuncTableEntry: *libc::c_void,
590 Params: [u64, ..4],
591 Far: libc::BOOL,
592 Virtual: libc::BOOL,
593 Reserved: [u64, ..3],
594 KdHelp: KDHELP64,
595 }
596
597 struct KDHELP64 {
598 Thread: u64,
599 ThCallbackStack: libc::DWORD,
600 ThCallbackBStore: libc::DWORD,
601 NextCallback: libc::DWORD,
602 FramePointer: libc::DWORD,
603 KiCallUserMode: u64,
604 KeUserCallbackDispatcher: u64,
605 SystemRangeStart: u64,
606 KiUserExceptionDispatcher: u64,
607 StackBase: u64,
608 StackLimit: u64,
609 Reserved: [u64, ..5],
610 }
611
612 #[cfg(target_arch = "x86")]
613 mod arch {
614 use libc;
615
616 static MAXIMUM_SUPPORTED_EXTENSION: uint = 512;
617
618 pub struct CONTEXT {
619 ContextFlags: libc::DWORD,
620 Dr0: libc::DWORD,
621 Dr1: libc::DWORD,
622 Dr2: libc::DWORD,
623 Dr3: libc::DWORD,
624 Dr6: libc::DWORD,
625 Dr7: libc::DWORD,
626 FloatSave: FLOATING_SAVE_AREA,
627 SegGs: libc::DWORD,
628 SegFs: libc::DWORD,
629 SegEs: libc::DWORD,
630 SegDs: libc::DWORD,
631 Edi: libc::DWORD,
632 Esi: libc::DWORD,
633 Ebx: libc::DWORD,
634 Edx: libc::DWORD,
635 Ecx: libc::DWORD,
636 Eax: libc::DWORD,
637 Ebp: libc::DWORD,
638 Eip: libc::DWORD,
639 SegCs: libc::DWORD,
640 EFlags: libc::DWORD,
641 Esp: libc::DWORD,
642 SegSs: libc::DWORD,
643 ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION],
644 }
645
646 pub struct FLOATING_SAVE_AREA {
647 ControlWord: libc::DWORD,
648 StatusWord: libc::DWORD,
649 TagWord: libc::DWORD,
650 ErrorOffset: libc::DWORD,
651 ErrorSelector: libc::DWORD,
652 DataOffset: libc::DWORD,
653 DataSelector: libc::DWORD,
654 RegisterArea: [u8, ..80],
655 Cr0NpxState: libc::DWORD,
656 }
657
658 pub fn init_frame(frame: &mut super::STACKFRAME64,
659 ctx: &CONTEXT) -> libc::DWORD {
660 frame.AddrPC.Offset = ctx.Eip as u64;
661 frame.AddrPC.Mode = super::AddrModeFlat;
662 frame.AddrStack.Offset = ctx.Esp as u64;
663 frame.AddrStack.Mode = super::AddrModeFlat;
664 frame.AddrFrame.Offset = ctx.Ebp as u64;
665 frame.AddrFrame.Mode = super::AddrModeFlat;
666 super::IMAGE_FILE_MACHINE_I386
667 }
668 }
669
670 #[cfg(target_arch = "x86_64")]
671 mod arch {
672 use libc::{c_longlong, c_ulonglong};
673 use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG};
674
675 pub struct CONTEXT {
676 P1Home: DWORDLONG,
677 P2Home: DWORDLONG,
678 P3Home: DWORDLONG,
679 P4Home: DWORDLONG,
680 P5Home: DWORDLONG,
681 P6Home: DWORDLONG,
682
683 ContextFlags: DWORD,
684 MxCsr: DWORD,
685
686 SegCs: WORD,
687 SegDs: WORD,
688 SegEs: WORD,
689 SegFs: WORD,
690 SegGs: WORD,
691 SegSs: WORD,
692 EFlags: DWORD,
693
694 Dr0: DWORDLONG,
695 Dr1: DWORDLONG,
696 Dr2: DWORDLONG,
697 Dr3: DWORDLONG,
698 Dr6: DWORDLONG,
699 Dr7: DWORDLONG,
700
701 Rax: DWORDLONG,
702 Rcx: DWORDLONG,
703 Rdx: DWORDLONG,
704 Rbx: DWORDLONG,
705 Rsp: DWORDLONG,
706 Rbp: DWORDLONG,
707 Rsi: DWORDLONG,
708 Rdi: DWORDLONG,
709 R8: DWORDLONG,
710 R9: DWORDLONG,
711 R10: DWORDLONG,
712 R11: DWORDLONG,
713 R12: DWORDLONG,
714 R13: DWORDLONG,
715 R14: DWORDLONG,
716 R15: DWORDLONG,
717
718 Rip: DWORDLONG,
719
720 FltSave: FLOATING_SAVE_AREA,
721
722 VectorRegister: [M128A, .. 26],
723 VectorControl: DWORDLONG,
724
725 DebugControl: DWORDLONG,
726 LastBranchToRip: DWORDLONG,
727 LastBranchFromRip: DWORDLONG,
728 LastExceptionToRip: DWORDLONG,
729 LastExceptionFromRip: DWORDLONG,
730 }
731
732 pub struct M128A {
733 Low: c_ulonglong,
734 High: c_longlong
735 }
736
737 pub struct FLOATING_SAVE_AREA {
738 _Dummy: [u8, ..512] // FIXME: Fill this out
739 }
740
741 pub fn init_frame(frame: &mut super::STACKFRAME64,
742 ctx: &CONTEXT) -> DWORD {
743 frame.AddrPC.Offset = ctx.Rip as u64;
744 frame.AddrPC.Mode = super::AddrModeFlat;
745 frame.AddrStack.Offset = ctx.Rsp as u64;
746 frame.AddrStack.Mode = super::AddrModeFlat;
747 frame.AddrFrame.Offset = ctx.Rbp as u64;
748 frame.AddrFrame.Mode = super::AddrModeFlat;
749 super::IMAGE_FILE_MACHINE_AMD64
750 }
751 }
752
753 struct Cleanup {
754 handle: libc::HANDLE,
755 SymCleanup: SymCleanupFn,
756 }
757
758 impl Drop for Cleanup {
759 fn drop(&mut self) { (self.SymCleanup)(self.handle); }
760 }
761
762 pub fn write(w: &mut Writer) -> IoResult<()> {
763 // According to windows documentation, all dbghelp functions are
764 // single-threaded.
765 static mut LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;
766 let _g = unsafe { LOCK.lock() };
767
768 // Open up dbghelp.dll, we don't link to it explicitly because it can't
769 // always be found. Additionally, it's nice having fewer dependencies.
770 let path = Path::new("dbghelp.dll");
771 let lib = match DynamicLibrary::open(Some(&path)) {
772 Ok(lib) => lib,
773 Err(..) => return Ok(()),
774 };
775
776 macro_rules! sym( ($e:expr, $t:ident) => (
777 match unsafe { lib.symbol::<$t>($e) } {
778 Ok(f) => f,
779 Err(..) => return Ok(())
780 }
781 ) )
782
783 // Fetch the symbols necessary from dbghelp.dll
784 let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn);
785 let SymInitialize = sym!("SymInitialize", SymInitializeFn);
786 let SymCleanup = sym!("SymCleanup", SymCleanupFn);
787 let StackWalk64 = sym!("StackWalk64", StackWalk64Fn);
788
789 // Allocate necessary structures for doing the stack walk
790 let process = unsafe { GetCurrentProcess() };
791 let thread = unsafe { GetCurrentThread() };
792 let mut context: arch::CONTEXT = unsafe { intrinsics::init() };
793 unsafe { RtlCaptureContext(&mut context); }
794 let mut frame: STACKFRAME64 = unsafe { intrinsics::init() };
795 let image = arch::init_frame(&mut frame, &context);
796
797 // Initialize this process's symbols
798 let ret = SymInitialize(process, 0 as *libc::c_void, libc::TRUE);
799 if ret != libc::TRUE { return Ok(()) }
800 let _c = Cleanup { handle: process, SymCleanup: SymCleanup };
801
802 // And now that we're done with all the setup, do the stack walking!
803 let mut i = 0;
804 try!(write!(w, "stack backtrace:\n"));
805 while StackWalk64(image, process, thread, &mut frame, &mut context,
806 0 as *libc::c_void, 0 as *libc::c_void,
807 0 as *libc::c_void, 0 as *libc::c_void) == libc::TRUE{
808 let addr = frame.AddrPC.Offset;
809 if addr == frame.AddrReturn.Offset || addr == 0 ||
810 frame.AddrReturn.Offset == 0 { break }
811
812 i += 1;
813 try!(write!(w, " {:2}: {:#2$x}", i, addr, super::HEX_WIDTH));
814 let mut info: SYMBOL_INFO = unsafe { intrinsics::init() };
815 info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong;
816 info.SizeOfStruct = (mem::size_of::<SYMBOL_INFO>() -
817 info.Name.len() + 1) as libc::c_ulong;
818
819 let mut displacement = 0u64;
820 let ret = SymFromAddr(process, addr as u64, &mut displacement,
821 &mut info);
822
823 if ret == libc::TRUE {
824 try!(write!(w, " - "));
825 let cstr = unsafe { CString::new(info.Name.as_ptr(), false) };
826 let bytes = cstr.as_bytes();
827 match cstr.as_str() {
828 Some(s) => try!(super::demangle(w, s)),
829 None => try!(w.write(bytes.slice_to(bytes.len() - 1))),
830 }
831 }
832 try!(w.write(['\n' as u8]));
833 }
834
835 Ok(())
836 }
837 }
838
839 #[cfg(test)]
840 mod test {
841 use prelude::*;
842 use io::MemWriter;
843 use str;
844
845 macro_rules! t( ($a:expr, $b:expr) => ({
846 let mut m = MemWriter::new();
847 super::demangle(&mut m, $a).unwrap();
848 assert_eq!(str::from_utf8(m.unwrap().as_slice()).unwrap().to_owned(), $b.to_owned());
849 }) )
850
851 #[test]
852 fn demangle() {
853 t!("test", "test");
854 t!("_ZN4testE", "test");
855 t!("_ZN4test", "_ZN4test");
856 t!("_ZN4test1a2bcE", "test::a::bc");
857 }
858
859 #[test]
860 fn demangle_dollars() {
861 t!("_ZN4$UP$E", "Box");
862 t!("_ZN8$UP$testE", "Boxtest");
863 t!("_ZN8$UP$test4foobE", "Boxtest::foob");
864 t!("_ZN8$x20test4foobE", " test::foob");
865 }
866
867 #[test]
868 fn demangle_many_dollars() {
869 t!("_ZN12test$x20test4foobE", "test test::foob");
870 t!("_ZN12test$UP$test4foobE", "testBoxtest::foob");
871 }
872 }
libstd/rt/backtrace.rs:373:8-373:8 -enum- definition:
enum backtrace_state {}
#[link(name = "backtrace", kind = "static")]
extern {
references:- 5423: // static as the pointer.
424: unsafe fn init_state() -> *mut backtrace_state {
425: static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state;
426: static mut LAST_FILENAME: [libc::c_char, ..256] = [0, ..256];
libstd/rt/backtrace.rs:369:8-369:8 -NK_AS_STR_TODO- definition:
type backtrace_error_callback =
extern "C" fn(data: *mut libc::c_void,
msg: *libc::c_char,
references:- 2377: threaded: libc::c_int,
378: error: backtrace_error_callback,
379: data: *mut libc::c_void)
--
383: cb: backtrace_syminfo_callback,
384: error: backtrace_error_callback,
385: data: *mut libc::c_void) -> libc::c_int;
libstd/rt/backtrace.rs:392:8-392:8 -fn- definition:
extern fn error_cb(_data: *mut libc::c_void, _msg: *libc::c_char,
_errnum: libc::c_int) {
// do nothing for now
references:- 2447: };
448: STATE = backtrace_create_state(filename, 0, error_cb,
449: ptr::mut_null());
--
466: backtrace_syminfo(state, addr as libc::uintptr_t,
467: syminfo_cb, error_cb,
468: data_addr as *mut libc::c_void)
libstd/rt/backtrace.rs:29:81-29:81 -fn- definition:
// whether the magical environment variable is present to see if it's turned on.
pub fn log_enabled() -> bool {
static mut ENABLED: atomics::AtomicInt = atomics::INIT_ATOMIC_INT;
references:- 4libstd/rt/unwind.rs:
383: rterrln!("failed at '{}', {}:{}", msg_s, file, line);
384: if backtrace::log_enabled() {
385: let mut err = ::rt::util::Stderr;
--
425: file, line);
426: if backtrace::log_enabled() {
427: let mut err = ::rt::util::Stderr;
--
444: // printed if logging was enabled).
445: if !backtrace::log_enabled() {
446: let mut err = ::rt::util::Stderr;
libstd/rt/backtrace.rs:478:4-478:4 -fn- definition:
fn output(w: &mut Writer, idx: int, addr: *libc::c_void,
s: Option<CString>) -> IoResult<()> {
try!(write!(w, " {:2}: {:2$} - ", idx, addr, super::HEX_WIDTH));
references:- 3472: } else {
473: output(w, idx, addr, Some(unsafe { CString::new(data, false) }))
474: }
libstd/rt/backtrace.rs:248:4-248:4 -struct- definition:
struct Context<'a> {
idx: int,
writer: &'a mut Writer,
references:- 3267: let mut cx = Context { writer: w, last_error: None, idx: 0 };
268: return match unsafe {
--
282: arg: *libc::c_void) -> uw::_Unwind_Reason_Code {
283: let cx: &mut Context = unsafe { cast::transmute(arg) };
284: let ip = unsafe { uw::_Unwind_GetIP(ctx) as *libc::c_void };
libstd/rt/backtrace.rs:256:4-256:4 -fn- definition:
pub fn write(w: &mut Writer) -> IoResult<()> {
// When using libbacktrace, we use some necessary global state, so we
// need to prevent more than one thread from entering this block. This
references:- 5libstd/rt/util.rs:
153: let mut err = Stderr;
154: let _err = ::rt::backtrace::write(&mut err);
155: }
libstd/rt/unwind.rs:
446: let mut err = ::rt::util::Stderr;
447: let _err = backtrace::write(&mut err);
448: }