]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/backtrace.rs
rollup merge of #20348: frewsxcv/rm-reexports
[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)] // constants/fields aren't always used on all platforms
26
27 use c_str::CString;
28 use intrinsics;
29 use io::{IoResult, Writer};
30 use libc;
31 use mem;
32 use ops::Drop;
33 use option::Option::{Some, None};
34 use path::Path;
35 use result::Result::{Ok, Err};
36 use sync::{StaticMutex, MUTEX_INIT};
37 use slice::SliceExt;
38 use str::StrExt;
39 use dynamic_lib::DynamicLibrary;
40
41 use sys_common::backtrace::*;
42
43 #[allow(non_snake_case)]
44 extern "system" {
45     fn GetCurrentProcess() -> libc::HANDLE;
46     fn GetCurrentThread() -> libc::HANDLE;
47     fn RtlCaptureContext(ctx: *mut arch::CONTEXT);
48 }
49
50 type SymFromAddrFn =
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;
56 type SymCleanupFn =
57     extern "system" fn(libc::HANDLE) -> libc::BOOL;
58
59 type StackWalk64Fn =
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;
64
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;
69
70 #[repr(C)]
71 struct SYMBOL_INFO {
72     SizeOfStruct: libc::c_ulong,
73     TypeIndex: libc::c_ulong,
74     Reserved: [u64, ..2],
75     Index: libc::c_ulong,
76     Size: libc::c_ulong,
77     ModBase: u64,
78     Flags: libc::c_ulong,
79     Value: u64,
80     Address: u64,
81     Register: libc::c_ulong,
82     Scope: libc::c_ulong,
83     Tag: 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],
90 }
91
92
93 #[repr(C)]
94 enum ADDRESS_MODE {
95     AddrMode1616,
96     AddrMode1632,
97     AddrModeReal,
98     AddrModeFlat,
99 }
100
101 struct ADDRESS64 {
102     Offset: u64,
103     Segment: u16,
104     Mode: ADDRESS_MODE,
105 }
106
107 struct STACKFRAME64 {
108     AddrPC: ADDRESS64,
109     AddrReturn: ADDRESS64,
110     AddrFrame: ADDRESS64,
111     AddrStack: ADDRESS64,
112     AddrBStore: ADDRESS64,
113     FuncTableEntry: *mut libc::c_void,
114     Params: [u64, ..4],
115     Far: libc::BOOL,
116     Virtual: libc::BOOL,
117     Reserved: [u64, ..3],
118     KdHelp: KDHELP64,
119 }
120
121 struct KDHELP64 {
122     Thread: u64,
123     ThCallbackStack: libc::DWORD,
124     ThCallbackBStore: libc::DWORD,
125     NextCallback: libc::DWORD,
126     FramePointer: libc::DWORD,
127     KiCallUserMode: u64,
128     KeUserCallbackDispatcher: u64,
129     SystemRangeStart: u64,
130     KiUserExceptionDispatcher: u64,
131     StackBase: u64,
132     StackLimit: u64,
133     Reserved: [u64, ..5],
134 }
135
136 #[cfg(target_arch = "x86")]
137 mod arch {
138     use libc;
139
140     const MAXIMUM_SUPPORTED_EXTENSION: uint = 512;
141
142     #[repr(C)]
143     pub struct CONTEXT {
144         ContextFlags: libc::DWORD,
145         Dr0: libc::DWORD,
146         Dr1: libc::DWORD,
147         Dr2: libc::DWORD,
148         Dr3: libc::DWORD,
149         Dr6: libc::DWORD,
150         Dr7: libc::DWORD,
151         FloatSave: FLOATING_SAVE_AREA,
152         SegGs: libc::DWORD,
153         SegFs: libc::DWORD,
154         SegEs: libc::DWORD,
155         SegDs: libc::DWORD,
156         Edi: libc::DWORD,
157         Esi: libc::DWORD,
158         Ebx: libc::DWORD,
159         Edx: libc::DWORD,
160         Ecx: libc::DWORD,
161         Eax: libc::DWORD,
162         Ebp: libc::DWORD,
163         Eip: libc::DWORD,
164         SegCs: libc::DWORD,
165         EFlags: libc::DWORD,
166         Esp: libc::DWORD,
167         SegSs: libc::DWORD,
168         ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION],
169     }
170
171     #[repr(C)]
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,
182     }
183
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
193     }
194 }
195
196 #[cfg(target_arch = "x86_64")]
197 mod arch {
198     use libc::{c_longlong, c_ulonglong};
199     use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG};
200     use simd;
201
202     #[repr(C)]
203     pub struct CONTEXT {
204         _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte
205         P1Home: DWORDLONG,
206         P2Home: DWORDLONG,
207         P3Home: DWORDLONG,
208         P4Home: DWORDLONG,
209         P5Home: DWORDLONG,
210         P6Home: DWORDLONG,
211
212         ContextFlags: DWORD,
213         MxCsr: DWORD,
214
215         SegCs: WORD,
216         SegDs: WORD,
217         SegEs: WORD,
218         SegFs: WORD,
219         SegGs: WORD,
220         SegSs: WORD,
221         EFlags: DWORD,
222
223         Dr0: DWORDLONG,
224         Dr1: DWORDLONG,
225         Dr2: DWORDLONG,
226         Dr3: DWORDLONG,
227         Dr6: DWORDLONG,
228         Dr7: DWORDLONG,
229
230         Rax: DWORDLONG,
231         Rcx: DWORDLONG,
232         Rdx: DWORDLONG,
233         Rbx: DWORDLONG,
234         Rsp: DWORDLONG,
235         Rbp: DWORDLONG,
236         Rsi: DWORDLONG,
237         Rdi: DWORDLONG,
238         R8:  DWORDLONG,
239         R9:  DWORDLONG,
240         R10: DWORDLONG,
241         R11: DWORDLONG,
242         R12: DWORDLONG,
243         R13: DWORDLONG,
244         R14: DWORDLONG,
245         R15: DWORDLONG,
246
247         Rip: DWORDLONG,
248
249         FltSave: FLOATING_SAVE_AREA,
250
251         VectorRegister: [M128A, .. 26],
252         VectorControl: DWORDLONG,
253
254         DebugControl: DWORDLONG,
255         LastBranchToRip: DWORDLONG,
256         LastBranchFromRip: DWORDLONG,
257         LastExceptionToRip: DWORDLONG,
258         LastExceptionFromRip: DWORDLONG,
259     }
260
261     #[repr(C)]
262     pub struct M128A {
263         _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte
264         Low:  c_ulonglong,
265         High: c_longlong
266     }
267
268     #[repr(C)]
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
272     }
273
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
283     }
284 }
285
286 #[repr(C)]
287 struct Cleanup {
288     handle: libc::HANDLE,
289     SymCleanup: SymCleanupFn,
290 }
291
292 impl Drop for Cleanup {
293     fn drop(&mut self) { (self.SymCleanup)(self.handle); }
294 }
295
296 pub fn write(w: &mut Writer) -> IoResult<()> {
297     // According to windows documentation, all dbghelp functions are
298     // single-threaded.
299     static LOCK: StaticMutex = MUTEX_INIT;
300     let _g = LOCK.lock();
301
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)) {
306         Ok(lib) => lib,
307         Err(..) => return Ok(()),
308     };
309
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(())
314         }
315     }) }
316
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);
322
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);
330
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 };
335
336     // And now that we're done with all the setup, do the stack walking!
337     let mut i = 0i;
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 }
347
348         i += 1;
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;
356
357         let mut displacement = 0u64;
358         let ret = SymFromAddr(process, addr as u64, &mut displacement,
359                               &mut info);
360
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])),
368             }
369         }
370         try!(w.write(&['\n' as u8]));
371     }
372
373     Ok(())
374 }