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)] // constants/fields aren't always used on all platforms
29 use io::{IoResult, Writer};
33 use option::Option::{Some, None};
35 use result::Result::{Ok, Err};
36 use sync::{StaticMutex, MUTEX_INIT};
39 use dynamic_lib::DynamicLibrary;
41 use sys_common::backtrace::*;
43 #[allow(non_snake_case)]
45 fn GetCurrentProcess() -> libc::HANDLE;
46 fn GetCurrentThread() -> libc::HANDLE;
47 fn RtlCaptureContext(ctx: *mut arch::CONTEXT);
51 extern "system" fn(libc::HANDLE, u64, *mut u64,
52 *mut SYMBOL_INFO) -> libc::BOOL;
53 type SymInitializeFn =
54 extern "system" fn(libc::HANDLE, *mut libc::c_void,
55 libc::BOOL) -> libc::BOOL;
57 extern "system" fn(libc::HANDLE) -> libc::BOOL;
60 extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE,
61 *mut STACKFRAME64, *mut arch::CONTEXT,
62 *mut libc::c_void, *mut libc::c_void,
63 *mut libc::c_void, *mut libc::c_void) -> libc::BOOL;
65 const MAX_SYM_NAME: uint = 2000;
66 const IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c;
67 const IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200;
68 const IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664;
72 SizeOfStruct: libc::c_ulong,
73 TypeIndex: libc::c_ulong,
81 Register: libc::c_ulong,
84 NameLen: libc::c_ulong,
85 MaxNameLen: libc::c_ulong,
86 // note that windows has this as 1, but it basically just means that
87 // the name is inline at the end of the struct. For us, we just bump
88 // the struct size up to MAX_SYM_NAME.
89 Name: [libc::c_char, ..MAX_SYM_NAME],
107 struct STACKFRAME64 {
109 AddrReturn: ADDRESS64,
110 AddrFrame: ADDRESS64,
111 AddrStack: ADDRESS64,
112 AddrBStore: ADDRESS64,
113 FuncTableEntry: *mut libc::c_void,
117 Reserved: [u64, ..3],
123 ThCallbackStack: libc::DWORD,
124 ThCallbackBStore: libc::DWORD,
125 NextCallback: libc::DWORD,
126 FramePointer: libc::DWORD,
128 KeUserCallbackDispatcher: u64,
129 SystemRangeStart: u64,
130 KiUserExceptionDispatcher: u64,
133 Reserved: [u64, ..5],
136 #[cfg(target_arch = "x86")]
140 const MAXIMUM_SUPPORTED_EXTENSION: uint = 512;
144 ContextFlags: libc::DWORD,
151 FloatSave: FLOATING_SAVE_AREA,
168 ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION],
172 pub struct FLOATING_SAVE_AREA {
173 ControlWord: libc::DWORD,
174 StatusWord: libc::DWORD,
175 TagWord: libc::DWORD,
176 ErrorOffset: libc::DWORD,
177 ErrorSelector: libc::DWORD,
178 DataOffset: libc::DWORD,
179 DataSelector: libc::DWORD,
180 RegisterArea: [u8, ..80],
181 Cr0NpxState: libc::DWORD,
184 pub fn init_frame(frame: &mut super::STACKFRAME64,
185 ctx: &CONTEXT) -> libc::DWORD {
186 frame.AddrPC.Offset = ctx.Eip as u64;
187 frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat;
188 frame.AddrStack.Offset = ctx.Esp as u64;
189 frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat;
190 frame.AddrFrame.Offset = ctx.Ebp as u64;
191 frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat;
192 super::IMAGE_FILE_MACHINE_I386
196 #[cfg(target_arch = "x86_64")]
198 use libc::{c_longlong, c_ulonglong};
199 use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG};
204 _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte
249 FltSave: FLOATING_SAVE_AREA,
251 VectorRegister: [M128A, .. 26],
252 VectorControl: DWORDLONG,
254 DebugControl: DWORDLONG,
255 LastBranchToRip: DWORDLONG,
256 LastBranchFromRip: DWORDLONG,
257 LastExceptionToRip: DWORDLONG,
258 LastExceptionFromRip: DWORDLONG,
263 _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte
269 pub struct FLOATING_SAVE_AREA {
270 _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte
271 _Dummy: [u8, ..512] // FIXME: Fill this out
274 pub fn init_frame(frame: &mut super::STACKFRAME64,
275 ctx: &CONTEXT) -> DWORD {
276 frame.AddrPC.Offset = ctx.Rip as u64;
277 frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat;
278 frame.AddrStack.Offset = ctx.Rsp as u64;
279 frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat;
280 frame.AddrFrame.Offset = ctx.Rbp as u64;
281 frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat;
282 super::IMAGE_FILE_MACHINE_AMD64
288 handle: libc::HANDLE,
289 SymCleanup: SymCleanupFn,
292 impl Drop for Cleanup {
293 fn drop(&mut self) { (self.SymCleanup)(self.handle); }
296 pub fn write(w: &mut Writer) -> IoResult<()> {
297 // According to windows documentation, all dbghelp functions are
299 static LOCK: StaticMutex = MUTEX_INIT;
300 let _g = LOCK.lock();
302 // Open up dbghelp.dll, we don't link to it explicitly because it can't
303 // always be found. Additionally, it's nice having fewer dependencies.
304 let path = Path::new("dbghelp.dll");
305 let lib = match DynamicLibrary::open(Some(&path)) {
307 Err(..) => return Ok(()),
310 macro_rules! sym{ ($e:expr, $t:ident) => (unsafe {
311 match lib.symbol($e) {
312 Ok(f) => mem::transmute::<*mut u8, $t>(f),
313 Err(..) => return Ok(())
317 // Fetch the symbols necessary from dbghelp.dll
318 let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn);
319 let SymInitialize = sym!("SymInitialize", SymInitializeFn);
320 let SymCleanup = sym!("SymCleanup", SymCleanupFn);
321 let StackWalk64 = sym!("StackWalk64", StackWalk64Fn);
323 // Allocate necessary structures for doing the stack walk
324 let process = unsafe { GetCurrentProcess() };
325 let thread = unsafe { GetCurrentThread() };
326 let mut context: arch::CONTEXT = unsafe { intrinsics::init() };
327 unsafe { RtlCaptureContext(&mut context); }
328 let mut frame: STACKFRAME64 = unsafe { intrinsics::init() };
329 let image = arch::init_frame(&mut frame, &context);
331 // Initialize this process's symbols
332 let ret = SymInitialize(process, 0 as *mut libc::c_void, libc::TRUE);
333 if ret != libc::TRUE { return Ok(()) }
334 let _c = Cleanup { handle: process, SymCleanup: SymCleanup };
336 // And now that we're done with all the setup, do the stack walking!
338 try!(write!(w, "stack backtrace:\n"));
339 while StackWalk64(image, process, thread, &mut frame, &mut context,
340 0 as *mut libc::c_void,
341 0 as *mut libc::c_void,
342 0 as *mut libc::c_void,
343 0 as *mut libc::c_void) == libc::TRUE{
344 let addr = frame.AddrPC.Offset;
345 if addr == frame.AddrReturn.Offset || addr == 0 ||
346 frame.AddrReturn.Offset == 0 { break }
349 try!(write!(w, " {:2}: {:#2$x}", i, addr, HEX_WIDTH));
350 let mut info: SYMBOL_INFO = unsafe { intrinsics::init() };
351 info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong;
352 // the struct size in C. the value is different to
353 // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
354 // due to struct alignment.
355 info.SizeOfStruct = 88;
357 let mut displacement = 0u64;
358 let ret = SymFromAddr(process, addr as u64, &mut displacement,
361 if ret == libc::TRUE {
362 try!(write!(w, " - "));
363 let cstr = unsafe { CString::new(info.Name.as_ptr(), false) };
364 let bytes = cstr.as_bytes();
365 match cstr.as_str() {
366 Some(s) => try!(demangle(w, s)),
367 None => try!(w.write(bytes[..bytes.len()-1])),
370 try!(w.write(&['\n' as u8]));