| // Copyright 2014 The Rust Project Developers. See the COPYRIGHT |
| // file at the top-level directory of this distribution and at |
| // http://rust-lang.org/COPYRIGHT. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| /// As always, windows has something very different than unix, we mainly want |
| /// to avoid having to depend too much on libunwind for windows. |
| /// |
| /// If you google around, you'll find a fair bit of references to built-in |
| /// functions to get backtraces on windows. It turns out that most of these are |
| /// in an external library called dbghelp. I was unable to find this library |
| /// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent |
| /// of it. |
| /// |
| /// You'll also find that there's a function called CaptureStackBackTrace |
| /// mentioned frequently (which is also easy to use), but sadly I didn't have a |
| /// copy of that function in my mingw install (maybe it was broken?). Instead, |
| /// this takes the route of using StackWalk64 in order to walk the stack. |
| |
| use c_str::CString; |
| use intrinsics; |
| use io::{IoResult, Writer}; |
| use libc; |
| use mem; |
| use ops::Drop; |
| use option::{Some, None}; |
| use path::Path; |
| use result::{Ok, Err}; |
| use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; |
| use slice::SliceExt; |
| use str::StrPrelude; |
| use dynamic_lib::DynamicLibrary; |
| |
| use sys_common::backtrace::*; |
| |
| #[allow(non_snake_case)] |
| extern "system" { |
| fn GetCurrentProcess() -> libc::HANDLE; |
| fn GetCurrentThread() -> libc::HANDLE; |
| fn RtlCaptureContext(ctx: *mut arch::CONTEXT); |
| } |
| |
| type SymFromAddrFn = |
| extern "system" fn(libc::HANDLE, u64, *mut u64, |
| *mut SYMBOL_INFO) -> libc::BOOL; |
| type SymInitializeFn = |
| extern "system" fn(libc::HANDLE, *mut libc::c_void, |
| libc::BOOL) -> libc::BOOL; |
| type SymCleanupFn = |
| extern "system" fn(libc::HANDLE) -> libc::BOOL; |
| |
| type StackWalk64Fn = |
| extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE, |
| *mut STACKFRAME64, *mut arch::CONTEXT, |
| *mut libc::c_void, *mut libc::c_void, |
| *mut libc::c_void, *mut libc::c_void) -> libc::BOOL; |
| |
| const MAX_SYM_NAME: uint = 2000; |
| const IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c; |
| const IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200; |
| const IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664; |
| |
| #[repr(C)] |
| struct SYMBOL_INFO { |
| SizeOfStruct: libc::c_ulong, |
| TypeIndex: libc::c_ulong, |
| Reserved: [u64, ..2], |
| Index: libc::c_ulong, |
| Size: libc::c_ulong, |
| ModBase: u64, |
| Flags: libc::c_ulong, |
| Value: u64, |
| Address: u64, |
| Register: libc::c_ulong, |
| Scope: libc::c_ulong, |
| Tag: libc::c_ulong, |
| NameLen: libc::c_ulong, |
| MaxNameLen: libc::c_ulong, |
| // note that windows has this as 1, but it basically just means that |
| // the name is inline at the end of the struct. For us, we just bump |
| // the struct size up to MAX_SYM_NAME. |
| Name: [libc::c_char, ..MAX_SYM_NAME], |
| } |
| |
| |
| #[repr(C)] |
| enum ADDRESS_MODE { |
| AddrMode1616, |
| AddrMode1632, |
| AddrModeReal, |
| AddrModeFlat, |
| } |
| |
| struct ADDRESS64 { |
| Offset: u64, |
| Segment: u16, |
| Mode: ADDRESS_MODE, |
| } |
| |
| struct STACKFRAME64 { |
| AddrPC: ADDRESS64, |
| AddrReturn: ADDRESS64, |
| AddrFrame: ADDRESS64, |
| AddrStack: ADDRESS64, |
| AddrBStore: ADDRESS64, |
| FuncTableEntry: *mut libc::c_void, |
| Params: [u64, ..4], |
| Far: libc::BOOL, |
| Virtual: libc::BOOL, |
| Reserved: [u64, ..3], |
| KdHelp: KDHELP64, |
| } |
| |
| struct KDHELP64 { |
| Thread: u64, |
| ThCallbackStack: libc::DWORD, |
| ThCallbackBStore: libc::DWORD, |
| NextCallback: libc::DWORD, |
| FramePointer: libc::DWORD, |
| KiCallUserMode: u64, |
| KeUserCallbackDispatcher: u64, |
| SystemRangeStart: u64, |
| KiUserExceptionDispatcher: u64, |
| StackBase: u64, |
| StackLimit: u64, |
| Reserved: [u64, ..5], |
| } |
| |
| #[cfg(target_arch = "x86")] |
| mod arch { |
| use libc; |
| |
| const MAXIMUM_SUPPORTED_EXTENSION: uint = 512; |
| |
| #[repr(C)] |
| pub struct CONTEXT { |
| ContextFlags: libc::DWORD, |
| Dr0: libc::DWORD, |
| Dr1: libc::DWORD, |
| Dr2: libc::DWORD, |
| Dr3: libc::DWORD, |
| Dr6: libc::DWORD, |
| Dr7: libc::DWORD, |
| FloatSave: FLOATING_SAVE_AREA, |
| SegGs: libc::DWORD, |
| SegFs: libc::DWORD, |
| SegEs: libc::DWORD, |
| SegDs: libc::DWORD, |
| Edi: libc::DWORD, |
| Esi: libc::DWORD, |
| Ebx: libc::DWORD, |
| Edx: libc::DWORD, |
| Ecx: libc::DWORD, |
| Eax: libc::DWORD, |
| Ebp: libc::DWORD, |
| Eip: libc::DWORD, |
| SegCs: libc::DWORD, |
| EFlags: libc::DWORD, |
| Esp: libc::DWORD, |
| SegSs: libc::DWORD, |
| ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION], |
| } |
| |
| #[repr(C)] |
| pub struct FLOATING_SAVE_AREA { |
| ControlWord: libc::DWORD, |
| StatusWord: libc::DWORD, |
| TagWord: libc::DWORD, |
| ErrorOffset: libc::DWORD, |
| ErrorSelector: libc::DWORD, |
| DataOffset: libc::DWORD, |
| DataSelector: libc::DWORD, |
| RegisterArea: [u8, ..80], |
| Cr0NpxState: libc::DWORD, |
| } |
| |
| pub fn init_frame(frame: &mut super::STACKFRAME64, |
| ctx: &CONTEXT) -> libc::DWORD { |
| frame.AddrPC.Offset = ctx.Eip as u64; |
| frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; |
| frame.AddrStack.Offset = ctx.Esp as u64; |
| frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; |
| frame.AddrFrame.Offset = ctx.Ebp as u64; |
| frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; |
| super::IMAGE_FILE_MACHINE_I386 |
| } |
| } |
| |
| #[cfg(target_arch = "x86_64")] |
| mod arch { |
| use libc::{c_longlong, c_ulonglong}; |
| use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG}; |
| use simd; |
| |
| #[repr(C)] |
| pub struct CONTEXT { |
| _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte |
| P1Home: DWORDLONG, |
| P2Home: DWORDLONG, |
| P3Home: DWORDLONG, |
| P4Home: DWORDLONG, |
| P5Home: DWORDLONG, |
| P6Home: DWORDLONG, |
| |
| ContextFlags: DWORD, |
| MxCsr: DWORD, |
| |
| SegCs: WORD, |
| SegDs: WORD, |
| SegEs: WORD, |
| SegFs: WORD, |
| SegGs: WORD, |
| SegSs: WORD, |
| EFlags: DWORD, |
| |
| Dr0: DWORDLONG, |
| Dr1: DWORDLONG, |
| Dr2: DWORDLONG, |
| Dr3: DWORDLONG, |
| Dr6: DWORDLONG, |
| Dr7: DWORDLONG, |
| |
| Rax: DWORDLONG, |
| Rcx: DWORDLONG, |
| Rdx: DWORDLONG, |
| Rbx: DWORDLONG, |
| Rsp: DWORDLONG, |
| Rbp: DWORDLONG, |
| Rsi: DWORDLONG, |
| Rdi: DWORDLONG, |
| R8: DWORDLONG, |
| R9: DWORDLONG, |
| R10: DWORDLONG, |
| R11: DWORDLONG, |
| R12: DWORDLONG, |
| R13: DWORDLONG, |
| R14: DWORDLONG, |
| R15: DWORDLONG, |
| |
| Rip: DWORDLONG, |
| |
| FltSave: FLOATING_SAVE_AREA, |
| |
| VectorRegister: [M128A, .. 26], |
| VectorControl: DWORDLONG, |
| |
| DebugControl: DWORDLONG, |
| LastBranchToRip: DWORDLONG, |
| LastBranchFromRip: DWORDLONG, |
| LastExceptionToRip: DWORDLONG, |
| LastExceptionFromRip: DWORDLONG, |
| } |
| |
| #[repr(C)] |
| pub struct M128A { |
| _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte |
| Low: c_ulonglong, |
| High: c_longlong |
| } |
| |
| #[repr(C)] |
| pub struct FLOATING_SAVE_AREA { |
| _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte |
| _Dummy: [u8, ..512] // FIXME: Fill this out |
| } |
| |
| pub fn init_frame(frame: &mut super::STACKFRAME64, |
| ctx: &CONTEXT) -> DWORD { |
| frame.AddrPC.Offset = ctx.Rip as u64; |
| frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; |
| frame.AddrStack.Offset = ctx.Rsp as u64; |
| frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; |
| frame.AddrFrame.Offset = ctx.Rbp as u64; |
| frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; |
| super::IMAGE_FILE_MACHINE_AMD64 |
| } |
| } |
| |
| #[repr(C)] |
| struct Cleanup { |
| handle: libc::HANDLE, |
| SymCleanup: SymCleanupFn, |
| } |
| |
| impl Drop for Cleanup { |
| fn drop(&mut self) { (self.SymCleanup)(self.handle); } |
| } |
| |
| pub fn write(w: &mut Writer) -> IoResult<()> { |
| // According to windows documentation, all dbghelp functions are |
| // single-threaded. |
| static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; |
| let _g = unsafe { LOCK.lock() }; |
| |
| // Open up dbghelp.dll, we don't link to it explicitly because it can't |
| // always be found. Additionally, it's nice having fewer dependencies. |
| let path = Path::new("dbghelp.dll"); |
| let lib = match DynamicLibrary::open(Some(&path)) { |
| Ok(lib) => lib, |
| Err(..) => return Ok(()), |
| }; |
| |
| macro_rules! sym( ($e:expr, $t:ident) => (unsafe { |
| match lib.symbol($e) { |
| Ok(f) => mem::transmute::<*mut u8, $t>(f), |
| Err(..) => return Ok(()) |
| } |
| }) ) |
| |
| // Fetch the symbols necessary from dbghelp.dll |
| let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn); |
| let SymInitialize = sym!("SymInitialize", SymInitializeFn); |
| let SymCleanup = sym!("SymCleanup", SymCleanupFn); |
| let StackWalk64 = sym!("StackWalk64", StackWalk64Fn); |
| |
| // Allocate necessary structures for doing the stack walk |
| let process = unsafe { GetCurrentProcess() }; |
| let thread = unsafe { GetCurrentThread() }; |
| let mut context: arch::CONTEXT = unsafe { intrinsics::init() }; |
| unsafe { RtlCaptureContext(&mut context); } |
| let mut frame: STACKFRAME64 = unsafe { intrinsics::init() }; |
| let image = arch::init_frame(&mut frame, &context); |
| |
| // Initialize this process's symbols |
| let ret = SymInitialize(process, 0 as *mut libc::c_void, libc::TRUE); |
| if ret != libc::TRUE { return Ok(()) } |
| let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; |
| |
| // And now that we're done with all the setup, do the stack walking! |
| let mut i = 0i; |
| try!(write!(w, "stack backtrace:\n")); |
| while StackWalk64(image, process, thread, &mut frame, &mut context, |
| 0 as *mut libc::c_void, |
| 0 as *mut libc::c_void, |
| 0 as *mut libc::c_void, |
| 0 as *mut libc::c_void) == libc::TRUE{ |
| let addr = frame.AddrPC.Offset; |
| if addr == frame.AddrReturn.Offset || addr == 0 || |
| frame.AddrReturn.Offset == 0 { break } |
| |
| i += 1; |
| try!(write!(w, " {:2}: {:#2$x}", i, addr, HEX_WIDTH)); |
| let mut info: SYMBOL_INFO = unsafe { intrinsics::init() }; |
| info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong; |
| // the struct size in C. the value is different to |
| // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81) |
| // due to struct alignment. |
| info.SizeOfStruct = 88; |
| |
| let mut displacement = 0u64; |
| let ret = SymFromAddr(process, addr as u64, &mut displacement, |
| &mut info); |
| |
| if ret == libc::TRUE { |
| try!(write!(w, " - ")); |
| let cstr = unsafe { CString::new(info.Name.as_ptr(), false) }; |
| let bytes = cstr.as_bytes(); |
| match cstr.as_str() { |
| Some(s) => try!(demangle(w, s)), |
| None => try!(w.write(bytes[..bytes.len()-1])), |
| } |
| } |
| try!(w.write(&['\n' as u8])); |
| } |
| |
| Ok(()) |
| } |