]> git.proxmox.com Git - rustc.git/blob - src/libstd/sys/windows/backtrace.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.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(deprecated)] // dynamic_lib
26
27 use io::prelude::*;
28
29 use io;
30 use libc::c_void;
31 use mem;
32 use ptr;
33 use sync::StaticMutex;
34 use sys::c;
35 use sys::dynamic_lib::DynamicLibrary;
36
37 macro_rules! sym {
38 ($lib:expr, $e:expr, $t:ident) => (
39 match $lib.symbol($e) {
40 Ok(f) => $crate::mem::transmute::<usize, $t>(f),
41 Err(..) => return Ok(())
42 }
43 )
44 }
45
46 #[cfg(target_env = "msvc")]
47 #[path = "printing/msvc.rs"]
48 mod printing;
49
50 #[cfg(target_env = "gnu")]
51 #[path = "printing/gnu.rs"]
52 mod printing;
53
54 type SymInitializeFn =
55 unsafe extern "system" fn(c::HANDLE, *mut c_void,
56 c::BOOL) -> c::BOOL;
57 type SymCleanupFn =
58 unsafe extern "system" fn(c::HANDLE) -> c::BOOL;
59
60 type StackWalk64Fn =
61 unsafe extern "system" fn(c::DWORD, c::HANDLE, c::HANDLE,
62 *mut c::STACKFRAME64, *mut c::CONTEXT,
63 *mut c_void, *mut c_void,
64 *mut c_void, *mut c_void) -> c::BOOL;
65
66 #[cfg(target_arch = "x86")]
67 pub fn init_frame(frame: &mut c::STACKFRAME64,
68 ctx: &c::CONTEXT) -> c::DWORD {
69 frame.AddrPC.Offset = ctx.Eip as u64;
70 frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
71 frame.AddrStack.Offset = ctx.Esp as u64;
72 frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
73 frame.AddrFrame.Offset = ctx.Ebp as u64;
74 frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
75 c::IMAGE_FILE_MACHINE_I386
76 }
77
78 #[cfg(target_arch = "x86_64")]
79 pub fn init_frame(frame: &mut c::STACKFRAME64,
80 ctx: &c::CONTEXT) -> c::DWORD {
81 frame.AddrPC.Offset = ctx.Rip as u64;
82 frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
83 frame.AddrStack.Offset = ctx.Rsp as u64;
84 frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
85 frame.AddrFrame.Offset = ctx.Rbp as u64;
86 frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
87 c::IMAGE_FILE_MACHINE_AMD64
88 }
89
90 struct Cleanup {
91 handle: c::HANDLE,
92 SymCleanup: SymCleanupFn,
93 }
94
95 impl Drop for Cleanup {
96 fn drop(&mut self) {
97 unsafe { (self.SymCleanup)(self.handle); }
98 }
99 }
100
101 pub fn write(w: &mut Write) -> io::Result<()> {
102 // According to windows documentation, all dbghelp functions are
103 // single-threaded.
104 static LOCK: StaticMutex = StaticMutex::new();
105 let _g = LOCK.lock();
106
107 let dbghelp = match DynamicLibrary::open("dbghelp.dll") {
108 Ok(lib) => lib,
109 Err(..) => return Ok(()),
110 };
111 unsafe {
112 // Fetch the symbols necessary from dbghelp.dll
113 let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn);
114 let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn);
115 let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn);
116
117 // Allocate necessary structures for doing the stack walk
118 let process = c::GetCurrentProcess();
119 let thread = c::GetCurrentThread();
120 let mut context: c::CONTEXT = mem::zeroed();
121 c::RtlCaptureContext(&mut context);
122 let mut frame: c::STACKFRAME64 = mem::zeroed();
123 let image = init_frame(&mut frame, &context);
124
125 // Initialize this process's symbols
126 let ret = SymInitialize(process, ptr::null_mut(), c::TRUE);
127 if ret != c::TRUE { return Ok(()) }
128 let _c = Cleanup { handle: process, SymCleanup: SymCleanup };
129
130 // And now that we're done with all the setup, do the stack walking!
131 // Start from -1 to avoid printing this stack frame, which will
132 // always be exactly the same.
133 let mut i = -1;
134 write!(w, "stack backtrace:\n")?;
135 while StackWalk64(image, process, thread, &mut frame, &mut context,
136 ptr::null_mut(),
137 ptr::null_mut(),
138 ptr::null_mut(),
139 ptr::null_mut()) == c::TRUE {
140 let addr = frame.AddrPC.Offset;
141 if addr == frame.AddrReturn.Offset || addr == 0 ||
142 frame.AddrReturn.Offset == 0 { break }
143
144 i += 1;
145
146 if i >= 0 {
147 printing::print(w, i, addr - 1, process, &dbghelp)?;
148 }
149 }
150
151 Ok(())
152 }
153 }