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.
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.
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.
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
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.
25 #![allow(deprecated)] // dynamic_lib
33 use sync
::StaticMutex
;
35 use sys
::dynamic_lib
::DynamicLibrary
;
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(())
46 #[cfg(target_env = "msvc")]
47 #[path = "printing/msvc.rs"]
50 #[cfg(target_env = "gnu")]
51 #[path = "printing/gnu.rs"]
54 type SymInitializeFn
=
55 unsafe extern "system" fn(c
::HANDLE
, *mut c_void
,
58 unsafe extern "system" fn(c
::HANDLE
) -> c
::BOOL
;
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
;
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
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
92 SymCleanup
: SymCleanupFn
,
95 impl Drop
for Cleanup
{
97 unsafe { (self.SymCleanup)(self.handle); }
101 pub fn write(w
: &mut Write
) -> io
::Result
<()> {
102 // According to windows documentation, all dbghelp functions are
104 static LOCK
: StaticMutex
= StaticMutex
::new();
105 let _g
= LOCK
.lock();
107 let dbghelp
= match DynamicLibrary
::open("dbghelp.dll") {
109 Err(..) => return Ok(()),
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
);
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
);
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 }
;
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.
134 write
!(w
, "stack backtrace:\n")?
;
135 while StackWalk64(image
, process
, thread
, &mut frame
, &mut context
,
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 }
147 printing
::print(w
, i
, addr
- 1, process
, &dbghelp
)?
;