]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/backtrace.rs
Auto merge of #29725 - aturon:lang-features, r=huonw
[rust.git] / src / libstd / sys / windows / 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 //! As always, windows has something very different than unix, we mainly want
12 //! to avoid having to depend too much on libunwind for windows.
13 //!
14 //! If you google around, you'll find a fair bit of references to built-in
15 //! functions to get backtraces on windows. It turns out that most of these are
16 //! in an external library called dbghelp. I was unable to find this library
17 //! via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent
18 //! of it.
19 //!
20 //! You'll also find that there's a function called CaptureStackBackTrace
21 //! mentioned frequently (which is also easy to use), but sadly I didn't have a
22 //! copy of that function in my mingw install (maybe it was broken?). Instead,
23 //! this takes the route of using StackWalk64 in order to walk the stack.
24
25 #![allow(dead_code, deprecated)]
26
27 use io::prelude::*;
28
29 use dynamic_lib::DynamicLibrary;
30 use io;
31 use libc::c_void;
32 use mem;
33 use path::Path;
34 use ptr;
35 use sync::StaticMutex;
36 use sys::c;
37
38 macro_rules! sym{ ($lib:expr, $e:expr, $t:ident) => (unsafe {
39     let lib = $lib;
40     match lib.symbol($e) {
41         Ok(f) => $crate::mem::transmute::<*mut u8, $t>(f),
42         Err(..) => return Ok(())
43     }
44 }) }
45
46 #[cfg(target_env = "msvc")]
47 #[path = "printing/msvc.rs"]
48 mod printing;
49
50 #[cfg(target_env = "gnu")]
51 #[path = "printing/gnu.rs"]
52 mod printing;
53
54 type SymFromAddrFn =
55     extern "system" fn(c::HANDLE, u64, *mut u64,
56                        *mut c::SYMBOL_INFO) -> c::BOOL;
57 type SymGetLineFromAddr64Fn =
58     extern "system" fn(c::HANDLE, u64, *mut u32,
59                        *mut c::IMAGEHLP_LINE64) -> c::BOOL;
60 type SymInitializeFn =
61     extern "system" fn(c::HANDLE, *mut c_void,
62                        c::BOOL) -> c::BOOL;
63 type SymCleanupFn =
64     extern "system" fn(c::HANDLE) -> c::BOOL;
65
66 type StackWalk64Fn =
67     extern "system" fn(c::DWORD, c::HANDLE, c::HANDLE,
68                        *mut c::STACKFRAME64, *mut c::CONTEXT,
69                        *mut c_void, *mut c_void,
70                        *mut c_void, *mut c_void) -> c::BOOL;
71
72 #[cfg(target_arch = "x86")]
73 pub fn init_frame(frame: &mut c::STACKFRAME64,
74                   ctx: &c::CONTEXT) -> c::DWORD {
75     frame.AddrPC.Offset = ctx.Eip as u64;
76     frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
77     frame.AddrStack.Offset = ctx.Esp as u64;
78     frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
79     frame.AddrFrame.Offset = ctx.Ebp as u64;
80     frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
81     c::IMAGE_FILE_MACHINE_I386
82 }
83
84 #[cfg(target_arch = "x86_64")]
85 pub fn init_frame(frame: &mut c::STACKFRAME64,
86                   ctx: &c::CONTEXT) -> c::DWORD {
87     frame.AddrPC.Offset = ctx.Rip as u64;
88     frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
89     frame.AddrStack.Offset = ctx.Rsp as u64;
90     frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
91     frame.AddrFrame.Offset = ctx.Rbp as u64;
92     frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
93     c::IMAGE_FILE_MACHINE_AMD64
94 }
95
96 struct Cleanup {
97     handle: c::HANDLE,
98     SymCleanup: SymCleanupFn,
99 }
100
101 impl Drop for Cleanup {
102     fn drop(&mut self) { (self.SymCleanup)(self.handle); }
103 }
104
105 pub fn write(w: &mut Write) -> io::Result<()> {
106     // According to windows documentation, all dbghelp functions are
107     // single-threaded.
108     static LOCK: StaticMutex = StaticMutex::new();
109     let _g = LOCK.lock();
110
111     // Open up dbghelp.dll, we don't link to it explicitly because it can't
112     // always be found. Additionally, it's nice having fewer dependencies.
113     let path = Path::new("dbghelp.dll");
114     let dbghelp = match DynamicLibrary::open(Some(&path)) {
115         Ok(lib) => lib,
116         Err(..) => return Ok(()),
117     };
118
119     // Fetch the symbols necessary from dbghelp.dll
120     let SymInitialize = sym!(&dbghelp, "SymInitialize", SymInitializeFn);
121     let SymCleanup = sym!(&dbghelp, "SymCleanup", SymCleanupFn);
122     let StackWalk64 = sym!(&dbghelp, "StackWalk64", StackWalk64Fn);
123
124     // Allocate necessary structures for doing the stack walk
125     let process = unsafe { c::GetCurrentProcess() };
126     let thread = unsafe { c::GetCurrentThread() };
127     let mut context: c::CONTEXT = unsafe { mem::zeroed() };
128     unsafe { c::RtlCaptureContext(&mut context); }
129     let mut frame: c::STACKFRAME64 = unsafe { mem::zeroed() };
130     let image = init_frame(&mut frame, &context);
131
132     // Initialize this process's symbols
133     let ret = SymInitialize(process, ptr::null_mut(), c::TRUE);
134     if ret != c::TRUE { return Ok(()) }
135     let _c = Cleanup { handle: process, SymCleanup: SymCleanup };
136
137     // And now that we're done with all the setup, do the stack walking!
138     // Start from -1 to avoid printing this stack frame, which will
139     // always be exactly the same.
140     let mut i = -1;
141     try!(write!(w, "stack backtrace:\n"));
142     while StackWalk64(image, process, thread, &mut frame, &mut context,
143                       ptr::null_mut(),
144                       ptr::null_mut(),
145                       ptr::null_mut(),
146                       ptr::null_mut()) == c::TRUE {
147         let addr = frame.AddrPC.Offset;
148         if addr == frame.AddrReturn.Offset || addr == 0 ||
149            frame.AddrReturn.Offset == 0 { break }
150
151         i += 1;
152
153         if i >= 0 {
154             try!(printing::print(w, i, addr-1, &dbghelp, process));
155         }
156     }
157
158     Ok(())
159 }