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