]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/backtrace.rs
auto merge of #21132 : sfackler/rust/wait_timeout, r=alexcrichton
[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 /// As always, windows has something very different than unix, we mainly want
11 /// to avoid having to depend too much on libunwind for windows.
12 ///
13 /// If you google around, you'll find a fair bit of references to built-in
14 /// functions to get backtraces on windows. It turns out that most of these are
15 /// in an external library called dbghelp. I was unable to find this library
16 /// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent
17 /// of it.
18 ///
19 /// You'll also find that there's a function called CaptureStackBackTrace
20 /// mentioned frequently (which is also easy to use), but sadly I didn't have a
21 /// copy of that function in my mingw install (maybe it was broken?). Instead,
22 /// this takes the route of using StackWalk64 in order to walk the stack.
23
24 use dynamic_lib::DynamicLibrary;
25 use ffi;
26 use core::ops::Index;
27 use intrinsics;
28 use io::{IoResult, Writer};
29 use libc;
30 use mem;
31 use ops::Drop;
32 use option::Option::{Some, None};
33 use path::Path;
34 use result::Result::{Ok, Err};
35 use slice::SliceExt;
36 use str::{self, StrExt};
37 use sync::{StaticMutex, MUTEX_INIT};
38
39 use sys_common::backtrace::*;
40
41 #[allow(non_snake_case)]
42 extern "system" {
43     fn GetCurrentProcess() -> libc::HANDLE;
44     fn GetCurrentThread() -> libc::HANDLE;
45     fn RtlCaptureContext(ctx: *mut arch::CONTEXT);
46 }
47
48 type SymFromAddrFn =
49     extern "system" fn(libc::HANDLE, u64, *mut u64,
50                        *mut SYMBOL_INFO) -> libc::BOOL;
51 type SymInitializeFn =
52     extern "system" fn(libc::HANDLE, *mut libc::c_void,
53                        libc::BOOL) -> libc::BOOL;
54 type SymCleanupFn =
55     extern "system" fn(libc::HANDLE) -> libc::BOOL;
56
57 type StackWalk64Fn =
58     extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE,
59                        *mut STACKFRAME64, *mut arch::CONTEXT,
60                        *mut libc::c_void, *mut libc::c_void,
61                        *mut libc::c_void, *mut libc::c_void) -> libc::BOOL;
62
63 const MAX_SYM_NAME: uint = 2000;
64 const IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c;
65 const IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200;
66 const IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664;
67
68 #[repr(C)]
69 struct SYMBOL_INFO {
70     SizeOfStruct: libc::c_ulong,
71     TypeIndex: libc::c_ulong,
72     Reserved: [u64; 2],
73     Index: libc::c_ulong,
74     Size: libc::c_ulong,
75     ModBase: u64,
76     Flags: libc::c_ulong,
77     Value: u64,
78     Address: u64,
79     Register: libc::c_ulong,
80     Scope: libc::c_ulong,
81     Tag: libc::c_ulong,
82     NameLen: libc::c_ulong,
83     MaxNameLen: libc::c_ulong,
84     // note that windows has this as 1, but it basically just means that
85     // the name is inline at the end of the struct. For us, we just bump
86     // the struct size up to MAX_SYM_NAME.
87     Name: [libc::c_char; MAX_SYM_NAME],
88 }
89
90
91 #[repr(C)]
92 enum ADDRESS_MODE {
93     AddrMode1616,
94     AddrMode1632,
95     AddrModeReal,
96     AddrModeFlat,
97 }
98
99 struct ADDRESS64 {
100     Offset: u64,
101     Segment: u16,
102     Mode: ADDRESS_MODE,
103 }
104
105 struct STACKFRAME64 {
106     AddrPC: ADDRESS64,
107     AddrReturn: ADDRESS64,
108     AddrFrame: ADDRESS64,
109     AddrStack: ADDRESS64,
110     AddrBStore: ADDRESS64,
111     FuncTableEntry: *mut libc::c_void,
112     Params: [u64; 4],
113     Far: libc::BOOL,
114     Virtual: libc::BOOL,
115     Reserved: [u64; 3],
116     KdHelp: KDHELP64,
117 }
118
119 struct KDHELP64 {
120     Thread: u64,
121     ThCallbackStack: libc::DWORD,
122     ThCallbackBStore: libc::DWORD,
123     NextCallback: libc::DWORD,
124     FramePointer: libc::DWORD,
125     KiCallUserMode: u64,
126     KeUserCallbackDispatcher: u64,
127     SystemRangeStart: u64,
128     KiUserExceptionDispatcher: u64,
129     StackBase: u64,
130     StackLimit: u64,
131     Reserved: [u64; 5],
132 }
133
134 #[cfg(target_arch = "x86")]
135 mod arch {
136     use libc;
137
138     const MAXIMUM_SUPPORTED_EXTENSION: uint = 512;
139
140     #[repr(C)]
141     pub struct CONTEXT {
142         ContextFlags: libc::DWORD,
143         Dr0: libc::DWORD,
144         Dr1: libc::DWORD,
145         Dr2: libc::DWORD,
146         Dr3: libc::DWORD,
147         Dr6: libc::DWORD,
148         Dr7: libc::DWORD,
149         FloatSave: FLOATING_SAVE_AREA,
150         SegGs: libc::DWORD,
151         SegFs: libc::DWORD,
152         SegEs: libc::DWORD,
153         SegDs: libc::DWORD,
154         Edi: libc::DWORD,
155         Esi: libc::DWORD,
156         Ebx: libc::DWORD,
157         Edx: libc::DWORD,
158         Ecx: libc::DWORD,
159         Eax: libc::DWORD,
160         Ebp: libc::DWORD,
161         Eip: libc::DWORD,
162         SegCs: libc::DWORD,
163         EFlags: libc::DWORD,
164         Esp: libc::DWORD,
165         SegSs: libc::DWORD,
166         ExtendedRegisters: [u8; MAXIMUM_SUPPORTED_EXTENSION],
167     }
168
169     #[repr(C)]
170     pub struct FLOATING_SAVE_AREA {
171         ControlWord: libc::DWORD,
172         StatusWord: libc::DWORD,
173         TagWord: libc::DWORD,
174         ErrorOffset: libc::DWORD,
175         ErrorSelector: libc::DWORD,
176         DataOffset: libc::DWORD,
177         DataSelector: libc::DWORD,
178         RegisterArea: [u8; 80],
179         Cr0NpxState: libc::DWORD,
180     }
181
182     pub fn init_frame(frame: &mut super::STACKFRAME64,
183                       ctx: &CONTEXT) -> libc::DWORD {
184         frame.AddrPC.Offset = ctx.Eip as u64;
185         frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat;
186         frame.AddrStack.Offset = ctx.Esp as u64;
187         frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat;
188         frame.AddrFrame.Offset = ctx.Ebp as u64;
189         frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat;
190         super::IMAGE_FILE_MACHINE_I386
191     }
192 }
193
194 #[cfg(target_arch = "x86_64")]
195 mod arch {
196     use libc::{c_longlong, c_ulonglong};
197     use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG};
198     use simd;
199
200     #[repr(C)]
201     pub struct CONTEXT {
202         _align_hack: [simd::u64x2; 0], // FIXME align on 16-byte
203         P1Home: DWORDLONG,
204         P2Home: DWORDLONG,
205         P3Home: DWORDLONG,
206         P4Home: DWORDLONG,
207         P5Home: DWORDLONG,
208         P6Home: DWORDLONG,
209
210         ContextFlags: DWORD,
211         MxCsr: DWORD,
212
213         SegCs: WORD,
214         SegDs: WORD,
215         SegEs: WORD,
216         SegFs: WORD,
217         SegGs: WORD,
218         SegSs: WORD,
219         EFlags: DWORD,
220
221         Dr0: DWORDLONG,
222         Dr1: DWORDLONG,
223         Dr2: DWORDLONG,
224         Dr3: DWORDLONG,
225         Dr6: DWORDLONG,
226         Dr7: DWORDLONG,
227
228         Rax: DWORDLONG,
229         Rcx: DWORDLONG,
230         Rdx: DWORDLONG,
231         Rbx: DWORDLONG,
232         Rsp: DWORDLONG,
233         Rbp: DWORDLONG,
234         Rsi: DWORDLONG,
235         Rdi: DWORDLONG,
236         R8:  DWORDLONG,
237         R9:  DWORDLONG,
238         R10: DWORDLONG,
239         R11: DWORDLONG,
240         R12: DWORDLONG,
241         R13: DWORDLONG,
242         R14: DWORDLONG,
243         R15: DWORDLONG,
244
245         Rip: DWORDLONG,
246
247         FltSave: FLOATING_SAVE_AREA,
248
249         VectorRegister: [M128A; 26],
250         VectorControl: DWORDLONG,
251
252         DebugControl: DWORDLONG,
253         LastBranchToRip: DWORDLONG,
254         LastBranchFromRip: DWORDLONG,
255         LastExceptionToRip: DWORDLONG,
256         LastExceptionFromRip: DWORDLONG,
257     }
258
259     #[repr(C)]
260     pub struct M128A {
261         _align_hack: [simd::u64x2; 0], // FIXME align on 16-byte
262         Low:  c_ulonglong,
263         High: c_longlong
264     }
265
266     #[repr(C)]
267     pub struct FLOATING_SAVE_AREA {
268         _align_hack: [simd::u64x2; 0], // FIXME align on 16-byte
269         _Dummy: [u8; 512] // FIXME: Fill this out
270     }
271
272     pub fn init_frame(frame: &mut super::STACKFRAME64,
273                       ctx: &CONTEXT) -> DWORD {
274         frame.AddrPC.Offset = ctx.Rip as u64;
275         frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat;
276         frame.AddrStack.Offset = ctx.Rsp as u64;
277         frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat;
278         frame.AddrFrame.Offset = ctx.Rbp as u64;
279         frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat;
280         super::IMAGE_FILE_MACHINE_AMD64
281     }
282 }
283
284 #[repr(C)]
285 struct Cleanup {
286     handle: libc::HANDLE,
287     SymCleanup: SymCleanupFn,
288 }
289
290 impl Drop for Cleanup {
291     fn drop(&mut self) { (self.SymCleanup)(self.handle); }
292 }
293
294 pub fn write(w: &mut Writer) -> IoResult<()> {
295     // According to windows documentation, all dbghelp functions are
296     // single-threaded.
297     static LOCK: StaticMutex = MUTEX_INIT;
298     let _g = unsafe { LOCK.lock() };
299
300     // Open up dbghelp.dll, we don't link to it explicitly because it can't
301     // always be found. Additionally, it's nice having fewer dependencies.
302     let path = Path::new("dbghelp.dll");
303     let lib = match DynamicLibrary::open(Some(&path)) {
304         Ok(lib) => lib,
305         Err(..) => return Ok(()),
306     };
307
308     macro_rules! sym{ ($e:expr, $t:ident) => (unsafe {
309         match lib.symbol($e) {
310             Ok(f) => mem::transmute::<*mut u8, $t>(f),
311             Err(..) => return Ok(())
312         }
313     }) }
314
315     // Fetch the symbols necessary from dbghelp.dll
316     let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn);
317     let SymInitialize = sym!("SymInitialize", SymInitializeFn);
318     let SymCleanup = sym!("SymCleanup", SymCleanupFn);
319     let StackWalk64 = sym!("StackWalk64", StackWalk64Fn);
320
321     // Allocate necessary structures for doing the stack walk
322     let process = unsafe { GetCurrentProcess() };
323     let thread = unsafe { GetCurrentThread() };
324     let mut context: arch::CONTEXT = unsafe { intrinsics::init() };
325     unsafe { RtlCaptureContext(&mut context); }
326     let mut frame: STACKFRAME64 = unsafe { intrinsics::init() };
327     let image = arch::init_frame(&mut frame, &context);
328
329     // Initialize this process's symbols
330     let ret = SymInitialize(process, 0 as *mut libc::c_void, libc::TRUE);
331     if ret != libc::TRUE { return Ok(()) }
332     let _c = Cleanup { handle: process, SymCleanup: SymCleanup };
333
334     // And now that we're done with all the setup, do the stack walking!
335     let mut i = 0i;
336     try!(write!(w, "stack backtrace:\n"));
337     while StackWalk64(image, process, thread, &mut frame, &mut context,
338                       0 as *mut libc::c_void,
339                       0 as *mut libc::c_void,
340                       0 as *mut libc::c_void,
341                       0 as *mut libc::c_void) == libc::TRUE{
342         let addr = frame.AddrPC.Offset;
343         if addr == frame.AddrReturn.Offset || addr == 0 ||
344            frame.AddrReturn.Offset == 0 { break }
345
346         i += 1;
347         try!(write!(w, "  {:2}: {:#2$x}", i, addr, HEX_WIDTH));
348         let mut info: SYMBOL_INFO = unsafe { intrinsics::init() };
349         info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong;
350         // the struct size in C.  the value is different to
351         // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
352         // due to struct alignment.
353         info.SizeOfStruct = 88;
354
355         let mut displacement = 0u64;
356         let ret = SymFromAddr(process, addr as u64, &mut displacement,
357                               &mut info);
358
359         if ret == libc::TRUE {
360             try!(write!(w, " - "));
361             let ptr = info.Name.as_ptr() as *const libc::c_char;
362             let bytes = unsafe { ffi::c_str_to_bytes(&ptr) };
363             match str::from_utf8(bytes) {
364                 Ok(s) => try!(demangle(w, s)),
365                 Err(..) => try!(w.write(&bytes[..(bytes.len()-1)])),
366             }
367         }
368         try!(w.write(&['\n' as u8]));
369     }
370
371     Ok(())
372 }