]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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. | |
85aaf69f SL |
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 | ||
b039eaaf | 25 | #![allow(dead_code, deprecated)] |
1a4d82fc | 26 | |
c34b1796 AL |
27 | use io::prelude::*; |
28 | ||
1a4d82fc | 29 | use dynamic_lib::DynamicLibrary; |
c34b1796 | 30 | use io; |
92a42be0 SL |
31 | use libc::c_void; |
32 | use mem; | |
c34b1796 | 33 | use path::Path; |
85aaf69f | 34 | use ptr; |
62682a34 | 35 | use sync::StaticMutex; |
92a42be0 | 36 | use sys::c; |
1a4d82fc | 37 | |
e9174d1e SL |
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; | |
1a4d82fc | 53 | |
1a4d82fc | 54 | type SymFromAddrFn = |
92a42be0 SL |
55 | extern "system" fn(c::HANDLE, u64, *mut u64, |
56 | *mut c::SYMBOL_INFO) -> c::BOOL; | |
e9174d1e | 57 | type SymGetLineFromAddr64Fn = |
92a42be0 SL |
58 | extern "system" fn(c::HANDLE, u64, *mut u32, |
59 | *mut c::IMAGEHLP_LINE64) -> c::BOOL; | |
1a4d82fc | 60 | type SymInitializeFn = |
92a42be0 SL |
61 | extern "system" fn(c::HANDLE, *mut c_void, |
62 | c::BOOL) -> c::BOOL; | |
1a4d82fc | 63 | type SymCleanupFn = |
92a42be0 | 64 | extern "system" fn(c::HANDLE) -> c::BOOL; |
1a4d82fc JJ |
65 | |
66 | type StackWalk64Fn = | |
92a42be0 SL |
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; | |
1a4d82fc JJ |
71 | |
72 | #[cfg(target_arch = "x86")] | |
92a42be0 SL |
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 | |
1a4d82fc JJ |
82 | } |
83 | ||
84 | #[cfg(target_arch = "x86_64")] | |
92a42be0 SL |
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 | |
1a4d82fc JJ |
94 | } |
95 | ||
1a4d82fc | 96 | struct Cleanup { |
92a42be0 | 97 | handle: c::HANDLE, |
1a4d82fc JJ |
98 | SymCleanup: SymCleanupFn, |
99 | } | |
100 | ||
101 | impl Drop for Cleanup { | |
102 | fn drop(&mut self) { (self.SymCleanup)(self.handle); } | |
103 | } | |
104 | ||
c34b1796 | 105 | pub fn write(w: &mut Write) -> io::Result<()> { |
1a4d82fc JJ |
106 | // According to windows documentation, all dbghelp functions are |
107 | // single-threaded. | |
62682a34 | 108 | static LOCK: StaticMutex = StaticMutex::new(); |
85aaf69f | 109 | let _g = LOCK.lock(); |
1a4d82fc JJ |
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"); | |
e9174d1e | 114 | let dbghelp = match DynamicLibrary::open(Some(&path)) { |
1a4d82fc JJ |
115 | Ok(lib) => lib, |
116 | Err(..) => return Ok(()), | |
117 | }; | |
118 | ||
1a4d82fc | 119 | // Fetch the symbols necessary from dbghelp.dll |
e9174d1e SL |
120 | let SymInitialize = sym!(&dbghelp, "SymInitialize", SymInitializeFn); |
121 | let SymCleanup = sym!(&dbghelp, "SymCleanup", SymCleanupFn); | |
122 | let StackWalk64 = sym!(&dbghelp, "StackWalk64", StackWalk64Fn); | |
1a4d82fc JJ |
123 | |
124 | // Allocate necessary structures for doing the stack walk | |
92a42be0 SL |
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); | |
1a4d82fc JJ |
131 | |
132 | // Initialize this process's symbols | |
92a42be0 SL |
133 | let ret = SymInitialize(process, ptr::null_mut(), c::TRUE); |
134 | if ret != c::TRUE { return Ok(()) } | |
1a4d82fc JJ |
135 | let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; |
136 | ||
137 | // And now that we're done with all the setup, do the stack walking! | |
e9174d1e SL |
138 | // Start from -1 to avoid printing this stack frame, which will |
139 | // always be exactly the same. | |
140 | let mut i = -1; | |
1a4d82fc JJ |
141 | try!(write!(w, "stack backtrace:\n")); |
142 | while StackWalk64(image, process, thread, &mut frame, &mut context, | |
85aaf69f SL |
143 | ptr::null_mut(), |
144 | ptr::null_mut(), | |
145 | ptr::null_mut(), | |
92a42be0 | 146 | ptr::null_mut()) == c::TRUE { |
1a4d82fc JJ |
147 | let addr = frame.AddrPC.Offset; |
148 | if addr == frame.AddrReturn.Offset || addr == 0 || | |
149 | frame.AddrReturn.Offset == 0 { break } | |
150 | ||
151 | i += 1; | |
e9174d1e SL |
152 | |
153 | if i >= 0 { | |
154 | try!(printing::print(w, i, addr-1, &dbghelp, process)); | |
1a4d82fc | 155 | } |
1a4d82fc JJ |
156 | } |
157 | ||
158 | Ok(()) | |
159 | } |