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 //! As always, windows has something very different than unix, we mainly want
12 //! to avoid having to depend too much on libunwind for windows.
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
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.
25 #![allow(dead_code, deprecated)]
29 use dynamic_lib::DynamicLibrary;
35 use sync::StaticMutex;
37 macro_rules! sym{ ($lib:expr, $e:expr, $t:ident) => (unsafe {
39 match lib.symbol($e) {
40 Ok(f) => $crate::mem::transmute::<*mut u8, $t>(f),
41 Err(..) => return Ok(())
45 #[cfg(target_env = "msvc")]
46 #[path = "printing/msvc.rs"]
49 #[cfg(target_env = "gnu")]
50 #[path = "printing/gnu.rs"]
53 #[allow(non_snake_case)]
55 fn GetCurrentProcess() -> libc::HANDLE;
56 fn GetCurrentThread() -> libc::HANDLE;
57 fn RtlCaptureContext(ctx: *mut arch::CONTEXT);
61 extern "system" fn(libc::HANDLE, u64, *mut u64,
62 *mut SYMBOL_INFO) -> libc::BOOL;
63 type SymGetLineFromAddr64Fn =
64 extern "system" fn(libc::HANDLE, u64, *mut u32,
65 *mut IMAGEHLP_LINE64) -> libc::BOOL;
66 type SymInitializeFn =
67 extern "system" fn(libc::HANDLE, *mut libc::c_void,
68 libc::BOOL) -> libc::BOOL;
70 extern "system" fn(libc::HANDLE) -> libc::BOOL;
73 extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE,
74 *mut STACKFRAME64, *mut arch::CONTEXT,
75 *mut libc::c_void, *mut libc::c_void,
76 *mut libc::c_void, *mut libc::c_void) -> libc::BOOL;
78 const MAX_SYM_NAME: usize = 2000;
79 const IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c;
80 const IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200;
81 const IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664;
85 SizeOfStruct: libc::c_ulong,
86 TypeIndex: libc::c_ulong,
94 Register: libc::c_ulong,
97 NameLen: libc::c_ulong,
98 MaxNameLen: libc::c_ulong,
99 // note that windows has this as 1, but it basically just means that
100 // the name is inline at the end of the struct. For us, we just bump
101 // the struct size up to MAX_SYM_NAME.
102 Name: [libc::c_char; MAX_SYM_NAME],
106 struct IMAGEHLP_LINE64 {
108 Key: *const libc::c_void,
110 Filename: *const libc::c_char,
128 pub struct STACKFRAME64 {
130 AddrReturn: ADDRESS64,
131 AddrFrame: ADDRESS64,
132 AddrStack: ADDRESS64,
133 AddrBStore: ADDRESS64,
134 FuncTableEntry: *mut libc::c_void,
144 ThCallbackStack: libc::DWORD,
145 ThCallbackBStore: libc::DWORD,
146 NextCallback: libc::DWORD,
147 FramePointer: libc::DWORD,
149 KeUserCallbackDispatcher: u64,
150 SystemRangeStart: u64,
151 KiUserExceptionDispatcher: u64,
157 #[cfg(target_arch = "x86")]
161 const MAXIMUM_SUPPORTED_EXTENSION: usize = 512;
165 ContextFlags: libc::DWORD,
172 FloatSave: FLOATING_SAVE_AREA,
189 ExtendedRegisters: [u8; MAXIMUM_SUPPORTED_EXTENSION],
193 pub struct FLOATING_SAVE_AREA {
194 ControlWord: libc::DWORD,
195 StatusWord: libc::DWORD,
196 TagWord: libc::DWORD,
197 ErrorOffset: libc::DWORD,
198 ErrorSelector: libc::DWORD,
199 DataOffset: libc::DWORD,
200 DataSelector: libc::DWORD,
201 RegisterArea: [u8; 80],
202 Cr0NpxState: libc::DWORD,
205 pub fn init_frame(frame: &mut super::STACKFRAME64,
206 ctx: &CONTEXT) -> libc::DWORD {
207 frame.AddrPC.Offset = ctx.Eip as u64;
208 frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat;
209 frame.AddrStack.Offset = ctx.Esp as u64;
210 frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat;
211 frame.AddrFrame.Offset = ctx.Ebp as u64;
212 frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat;
213 super::IMAGE_FILE_MACHINE_I386
217 #[cfg(target_arch = "x86_64")]
219 #![allow(deprecated)]
221 use libc::{c_longlong, c_ulonglong};
222 use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG};
227 _align_hack: [simd::u64x2; 0], // FIXME align on 16-byte
272 FltSave: FLOATING_SAVE_AREA,
274 VectorRegister: [M128A; 26],
275 VectorControl: DWORDLONG,
277 DebugControl: DWORDLONG,
278 LastBranchToRip: DWORDLONG,
279 LastBranchFromRip: DWORDLONG,
280 LastExceptionToRip: DWORDLONG,
281 LastExceptionFromRip: DWORDLONG,
286 _align_hack: [simd::u64x2; 0], // FIXME align on 16-byte
292 pub struct FLOATING_SAVE_AREA {
293 _align_hack: [simd::u64x2; 0], // FIXME align on 16-byte
294 _Dummy: [u8; 512] // FIXME: Fill this out
297 pub fn init_frame(frame: &mut super::STACKFRAME64,
298 ctx: &CONTEXT) -> DWORD {
299 frame.AddrPC.Offset = ctx.Rip as u64;
300 frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat;
301 frame.AddrStack.Offset = ctx.Rsp as u64;
302 frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat;
303 frame.AddrFrame.Offset = ctx.Rbp as u64;
304 frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat;
305 super::IMAGE_FILE_MACHINE_AMD64
310 handle: libc::HANDLE,
311 SymCleanup: SymCleanupFn,
314 impl Drop for Cleanup {
315 fn drop(&mut self) { (self.SymCleanup)(self.handle); }
318 pub fn write(w: &mut Write) -> io::Result<()> {
319 // According to windows documentation, all dbghelp functions are
321 static LOCK: StaticMutex = StaticMutex::new();
322 let _g = LOCK.lock();
324 // Open up dbghelp.dll, we don't link to it explicitly because it can't
325 // always be found. Additionally, it's nice having fewer dependencies.
326 let path = Path::new("dbghelp.dll");
327 let dbghelp = match DynamicLibrary::open(Some(&path)) {
329 Err(..) => return Ok(()),
332 // Fetch the symbols necessary from dbghelp.dll
333 let SymInitialize = sym!(&dbghelp, "SymInitialize", SymInitializeFn);
334 let SymCleanup = sym!(&dbghelp, "SymCleanup", SymCleanupFn);
335 let StackWalk64 = sym!(&dbghelp, "StackWalk64", StackWalk64Fn);
337 // Allocate necessary structures for doing the stack walk
338 let process = unsafe { GetCurrentProcess() };
339 let thread = unsafe { GetCurrentThread() };
340 let mut context: arch::CONTEXT = unsafe { intrinsics::init() };
341 unsafe { RtlCaptureContext(&mut context); }
342 let mut frame: STACKFRAME64 = unsafe { intrinsics::init() };
343 let image = arch::init_frame(&mut frame, &context);
345 // Initialize this process's symbols
346 let ret = SymInitialize(process, ptr::null_mut(), libc::TRUE);
347 if ret != libc::TRUE { return Ok(()) }
348 let _c = Cleanup { handle: process, SymCleanup: SymCleanup };
350 // And now that we're done with all the setup, do the stack walking!
351 // Start from -1 to avoid printing this stack frame, which will
352 // always be exactly the same.
354 try!(write!(w, "stack backtrace:\n"));
355 while StackWalk64(image, process, thread, &mut frame, &mut context,
359 ptr::null_mut()) == libc::TRUE{
360 let addr = frame.AddrPC.Offset;
361 if addr == frame.AddrReturn.Offset || addr == 0 ||
362 frame.AddrReturn.Offset == 0 { break }
367 try!(printing::print(w, i, addr-1, &dbghelp, process));