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