blob: 26b395a69b2b1aa90f123be40038a70334d7ba0c [file] [log] [blame]
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(bad_style)]
use std::mem;
use winapi::ctypes::*;
use winapi::shared::minwindef::*;
use winapi::um::processthreadsapi;
use winapi::um::winnt::{self, CONTEXT};
use winapi::um::dbghelp;
use winapi::um::dbghelp::*;
pub struct Frame {
inner: STACKFRAME64,
}
impl Frame {
pub fn ip(&self) -> *mut c_void {
self.inner.AddrPC.Offset as *mut _
}
pub fn symbol_address(&self) -> *mut c_void {
self.ip()
}
}
#[inline(always)]
pub fn trace(cb: &mut FnMut(&super::Frame) -> bool) {
// According to windows documentation, all dbghelp functions are
// single-threaded.
let _g = ::lock::lock();
unsafe {
// Allocate necessary structures for doing the stack walk
let process = processthreadsapi::GetCurrentProcess();
let thread = processthreadsapi::GetCurrentThread();
// The CONTEXT structure needs to be aligned on a 16-byte boundary for
// 64-bit Windows, but currently we don't have a way to express that in
// Rust. Allocations are generally aligned to 16-bytes, though, so we
// box this up.
let mut context = Box::new(mem::zeroed::<CONTEXT>());
winnt::RtlCaptureContext(&mut *context);
let mut frame = super::Frame {
inner: Frame { inner: mem::zeroed() },
};
let image = init_frame(&mut frame.inner.inner, &context);
// Initialize this process's symbols
let _c = ::dbghelp_init();
// And now that we're done with all the setup, do the stack walking!
while dbghelp::StackWalk64(image as DWORD,
process,
thread,
&mut frame.inner.inner,
&mut *context as *mut _ as *mut _,
None,
Some(dbghelp::SymFunctionTableAccess64),
Some(dbghelp::SymGetModuleBase64),
None) == TRUE {
if frame.inner.inner.AddrPC.Offset == frame.inner.inner.AddrReturn.Offset ||
frame.inner.inner.AddrPC.Offset == 0 ||
frame.inner.inner.AddrReturn.Offset == 0 {
break
}
if !cb(&frame) {
break
}
}
}
}
#[cfg(target_arch = "x86_64")]
fn init_frame(frame: &mut STACKFRAME64, ctx: &CONTEXT) -> WORD {
frame.AddrPC.Offset = ctx.Rip as u64;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrStack.Offset = ctx.Rsp as u64;
frame.AddrStack.Mode = AddrModeFlat;
frame.AddrFrame.Offset = ctx.Rbp as u64;
frame.AddrFrame.Mode = AddrModeFlat;
winnt::IMAGE_FILE_MACHINE_AMD64
}
#[cfg(target_arch = "x86")]
fn init_frame(frame: &mut STACKFRAME64, ctx: &CONTEXT) -> WORD {
frame.AddrPC.Offset = ctx.Eip as u64;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrStack.Offset = ctx.Esp as u64;
frame.AddrStack.Mode = AddrModeFlat;
frame.AddrFrame.Offset = ctx.Ebp as u64;
frame.AddrFrame.Mode = AddrModeFlat;
winnt::IMAGE_FILE_MACHINE_I386
}