]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/backtrace.rs
Auto merge of #29303 - petrochenkov:unistrimp, r=eddyb
[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, deprecated)]
26
27 use io::prelude::*;
28
29 use dynamic_lib::DynamicLibrary;
30 use intrinsics;
31 use io;
32 use libc;
33 use path::Path;
34 use ptr;
35 use sync::StaticMutex;
36
37 macro_rules! sym{ ($lib:expr, $e:expr, $t:ident) => (unsafe {
38     let lib = $lib;
39     match lib.symbol($e) {
40         Ok(f) => $crate::mem::transmute::<*mut u8, $t>(f),
41         Err(..) => return Ok(())
42     }
43 }) }
44
45 #[cfg(target_env = "msvc")]
46 #[path = "printing/msvc.rs"]
47 mod printing;
48
49 #[cfg(target_env = "gnu")]
50 #[path = "printing/gnu.rs"]
51 mod printing;
52
53 #[allow(non_snake_case)]
54 extern "system" {
55     fn GetCurrentProcess() -> libc::HANDLE;
56     fn GetCurrentThread() -> libc::HANDLE;
57     fn RtlCaptureContext(ctx: *mut arch::CONTEXT);
58 }
59
60 type SymFromAddrFn =
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;
69 type SymCleanupFn =
70     extern "system" fn(libc::HANDLE) -> libc::BOOL;
71
72 type StackWalk64Fn =
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;
77
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;
82
83 #[repr(C)]
84 struct SYMBOL_INFO {
85     SizeOfStruct: libc::c_ulong,
86     TypeIndex: libc::c_ulong,
87     Reserved: [u64; 2],
88     Index: libc::c_ulong,
89     Size: libc::c_ulong,
90     ModBase: u64,
91     Flags: libc::c_ulong,
92     Value: u64,
93     Address: u64,
94     Register: libc::c_ulong,
95     Scope: libc::c_ulong,
96     Tag: 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],
103 }
104
105 #[repr(C)]
106 struct IMAGEHLP_LINE64 {
107     SizeOfStruct: u32,
108     Key: *const libc::c_void,
109     LineNumber: u32,
110     Filename: *const libc::c_char,
111     Address: u64,
112 }
113
114 #[repr(C)]
115 enum ADDRESS_MODE {
116     AddrMode1616,
117     AddrMode1632,
118     AddrModeReal,
119     AddrModeFlat,
120 }
121
122 struct ADDRESS64 {
123     Offset: u64,
124     Segment: u16,
125     Mode: ADDRESS_MODE,
126 }
127
128 pub struct STACKFRAME64 {
129     AddrPC: ADDRESS64,
130     AddrReturn: ADDRESS64,
131     AddrFrame: ADDRESS64,
132     AddrStack: ADDRESS64,
133     AddrBStore: ADDRESS64,
134     FuncTableEntry: *mut libc::c_void,
135     Params: [u64; 4],
136     Far: libc::BOOL,
137     Virtual: libc::BOOL,
138     Reserved: [u64; 3],
139     KdHelp: KDHELP64,
140 }
141
142 struct KDHELP64 {
143     Thread: u64,
144     ThCallbackStack: libc::DWORD,
145     ThCallbackBStore: libc::DWORD,
146     NextCallback: libc::DWORD,
147     FramePointer: libc::DWORD,
148     KiCallUserMode: u64,
149     KeUserCallbackDispatcher: u64,
150     SystemRangeStart: u64,
151     KiUserExceptionDispatcher: u64,
152     StackBase: u64,
153     StackLimit: u64,
154     Reserved: [u64; 5],
155 }
156
157 #[cfg(target_arch = "x86")]
158 mod arch {
159     use libc;
160
161     const MAXIMUM_SUPPORTED_EXTENSION: usize = 512;
162
163     #[repr(C)]
164     pub struct CONTEXT {
165         ContextFlags: libc::DWORD,
166         Dr0: libc::DWORD,
167         Dr1: libc::DWORD,
168         Dr2: libc::DWORD,
169         Dr3: libc::DWORD,
170         Dr6: libc::DWORD,
171         Dr7: libc::DWORD,
172         FloatSave: FLOATING_SAVE_AREA,
173         SegGs: libc::DWORD,
174         SegFs: libc::DWORD,
175         SegEs: libc::DWORD,
176         SegDs: libc::DWORD,
177         Edi: libc::DWORD,
178         Esi: libc::DWORD,
179         Ebx: libc::DWORD,
180         Edx: libc::DWORD,
181         Ecx: libc::DWORD,
182         Eax: libc::DWORD,
183         Ebp: libc::DWORD,
184         Eip: libc::DWORD,
185         SegCs: libc::DWORD,
186         EFlags: libc::DWORD,
187         Esp: libc::DWORD,
188         SegSs: libc::DWORD,
189         ExtendedRegisters: [u8; MAXIMUM_SUPPORTED_EXTENSION],
190     }
191
192     #[repr(C)]
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,
203     }
204
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
214     }
215 }
216
217 #[cfg(target_arch = "x86_64")]
218 mod arch {
219     #![allow(deprecated)]
220
221     use libc::{c_longlong, c_ulonglong};
222     use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG};
223     use simd;
224
225     #[repr(C)]
226     pub struct CONTEXT {
227         _align_hack: [simd::u64x2; 0], // FIXME align on 16-byte
228         P1Home: DWORDLONG,
229         P2Home: DWORDLONG,
230         P3Home: DWORDLONG,
231         P4Home: DWORDLONG,
232         P5Home: DWORDLONG,
233         P6Home: DWORDLONG,
234
235         ContextFlags: DWORD,
236         MxCsr: DWORD,
237
238         SegCs: WORD,
239         SegDs: WORD,
240         SegEs: WORD,
241         SegFs: WORD,
242         SegGs: WORD,
243         SegSs: WORD,
244         EFlags: DWORD,
245
246         Dr0: DWORDLONG,
247         Dr1: DWORDLONG,
248         Dr2: DWORDLONG,
249         Dr3: DWORDLONG,
250         Dr6: DWORDLONG,
251         Dr7: DWORDLONG,
252
253         Rax: DWORDLONG,
254         Rcx: DWORDLONG,
255         Rdx: DWORDLONG,
256         Rbx: DWORDLONG,
257         Rsp: DWORDLONG,
258         Rbp: DWORDLONG,
259         Rsi: DWORDLONG,
260         Rdi: DWORDLONG,
261         R8:  DWORDLONG,
262         R9:  DWORDLONG,
263         R10: DWORDLONG,
264         R11: DWORDLONG,
265         R12: DWORDLONG,
266         R13: DWORDLONG,
267         R14: DWORDLONG,
268         R15: DWORDLONG,
269
270         Rip: DWORDLONG,
271
272         FltSave: FLOATING_SAVE_AREA,
273
274         VectorRegister: [M128A; 26],
275         VectorControl: DWORDLONG,
276
277         DebugControl: DWORDLONG,
278         LastBranchToRip: DWORDLONG,
279         LastBranchFromRip: DWORDLONG,
280         LastExceptionToRip: DWORDLONG,
281         LastExceptionFromRip: DWORDLONG,
282     }
283
284     #[repr(C)]
285     pub struct M128A {
286         _align_hack: [simd::u64x2; 0], // FIXME align on 16-byte
287         Low:  c_ulonglong,
288         High: c_longlong
289     }
290
291     #[repr(C)]
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
295     }
296
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
306     }
307 }
308
309 struct Cleanup {
310     handle: libc::HANDLE,
311     SymCleanup: SymCleanupFn,
312 }
313
314 impl Drop for Cleanup {
315     fn drop(&mut self) { (self.SymCleanup)(self.handle); }
316 }
317
318 pub fn write(w: &mut Write) -> io::Result<()> {
319     // According to windows documentation, all dbghelp functions are
320     // single-threaded.
321     static LOCK: StaticMutex = StaticMutex::new();
322     let _g = LOCK.lock();
323
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)) {
328         Ok(lib) => lib,
329         Err(..) => return Ok(()),
330     };
331
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);
336
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);
344
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 };
349
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.
353     let mut i = -1;
354     try!(write!(w, "stack backtrace:\n"));
355     while StackWalk64(image, process, thread, &mut frame, &mut context,
356                       ptr::null_mut(),
357                       ptr::null_mut(),
358                       ptr::null_mut(),
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 }
363
364         i += 1;
365
366         if i >= 0 {
367             try!(printing::print(w, i, addr-1, &dbghelp, process));
368         }
369     }
370
371     Ok(())
372 }