Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a fallback for stacktrace printing for older Windows versions. #50526

Merged
merged 6 commits into from
Jun 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
273 changes: 210 additions & 63 deletions src/libstd/sys/windows/backtrace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,110 +46,257 @@ mod printing;
#[path = "backtrace_gnu.rs"]
pub mod gnu;

pub use self::printing::{resolve_symname, foreach_symbol_fileline};
pub use self::printing::{foreach_symbol_fileline, resolve_symname};
use self::printing::{load_printing_fns_64, load_printing_fns_ex};

pub fn unwind_backtrace(frames: &mut [Frame])
-> io::Result<(usize, BacktraceContext)>
{
pub fn unwind_backtrace(frames: &mut [Frame]) -> io::Result<(usize, BacktraceContext)> {
let dbghelp = DynamicLibrary::open("dbghelp.dll")?;

// Fetch the symbols necessary from dbghelp.dll
let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn)?;
let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn)?;
let StackWalkEx = sym!(dbghelp, "StackWalkEx", StackWalkExFn)?;

// StackWalkEx might not be present and we'll fall back to StackWalk64
let sw_var = match sym!(dbghelp, "StackWalkEx", StackWalkExFn) {
Ok(StackWalkEx) => {
StackWalkVariant::StackWalkEx(StackWalkEx, load_printing_fns_ex(&dbghelp)?)
}
Err(e) => match sym!(dbghelp, "StackWalk64", StackWalk64Fn) {
Ok(StackWalk64) => {
StackWalkVariant::StackWalk64(StackWalk64, load_printing_fns_64(&dbghelp)?)
}
Err(..) => return Err(e),
},
};

// Allocate necessary structures for doing the stack walk
let process = unsafe { c::GetCurrentProcess() };
let thread = unsafe { c::GetCurrentThread() };
let mut context: c::CONTEXT = unsafe { mem::zeroed() };
unsafe { c::RtlCaptureContext(&mut context) };
let mut frame: c::STACKFRAME_EX = unsafe { mem::zeroed() };
frame.StackFrameSize = mem::size_of_val(&frame) as c::DWORD;
let image = init_frame(&mut frame, &context);

let backtrace_context = BacktraceContext {
handle: process,
SymCleanup,
StackWalkVariant: sw_var,
dbghelp,
};

// Initialize this process's symbols
let ret = unsafe { SymInitialize(process, ptr::null_mut(), c::TRUE) };
if ret != c::TRUE {
return Ok((0, backtrace_context))
return Ok((0, backtrace_context));
}

// And now that we're done with all the setup, do the stack walking!
match backtrace_context.StackWalkVariant {
StackWalkVariant::StackWalkEx(StackWalkEx, _) => {
set_frames(StackWalkEx, frames).map(|i| (i, backtrace_context))
}

StackWalkVariant::StackWalk64(StackWalk64, _) => {
set_frames(StackWalk64, frames).map(|i| (i, backtrace_context))
}
}
}

fn set_frames<W: StackWalker>(StackWalk: W, frames: &mut [Frame]) -> io::Result<usize> {
let process = unsafe { c::GetCurrentProcess() };
let thread = unsafe { c::GetCurrentProcess() };
let mut context: c::CONTEXT = unsafe { mem::zeroed() };
unsafe { c::RtlCaptureContext(&mut context) };
let mut frame = W::Item::new();
let image = frame.init(&context);

let mut i = 0;
unsafe {
while i < frames.len() &&
StackWalkEx(image, process, thread, &mut frame, &mut context,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0) == c::TRUE
{
let addr = (frame.AddrPC.Offset - 1) as *const u8;

frames[i] = Frame {
symbol_addr: addr,
exact_position: addr,
inline_context: frame.InlineFrameContext,
};
i += 1;
while i < frames.len()
&& StackWalk.walk(image, process, thread, &mut frame, &mut context) == c::TRUE
{
let addr = frame.get_addr();
frames[i] = Frame {
symbol_addr: addr,
exact_position: addr,
inline_context: 0,
};

i += 1
}
Ok(i)
}

type SymInitializeFn = unsafe extern "system" fn(c::HANDLE, *mut c_void, c::BOOL) -> c::BOOL;
type SymCleanupFn = unsafe extern "system" fn(c::HANDLE) -> c::BOOL;

type StackWalkExFn = unsafe extern "system" fn(
c::DWORD,
c::HANDLE,
c::HANDLE,
*mut c::STACKFRAME_EX,
*mut c::CONTEXT,
*mut c_void,
*mut c_void,
*mut c_void,
*mut c_void,
c::DWORD,
) -> c::BOOL;

type StackWalk64Fn = unsafe extern "system" fn(
c::DWORD,
c::HANDLE,
c::HANDLE,
*mut c::STACKFRAME64,
*mut c::CONTEXT,
*mut c_void,
*mut c_void,
*mut c_void,
*mut c_void,
) -> c::BOOL;

trait StackWalker {
type Item: StackFrame;

fn walk(&self, c::DWORD, c::HANDLE, c::HANDLE, &mut Self::Item, &mut c::CONTEXT) -> c::BOOL;
}

impl StackWalker for StackWalkExFn {
type Item = c::STACKFRAME_EX;
fn walk(
&self,
image: c::DWORD,
process: c::HANDLE,
thread: c::HANDLE,
frame: &mut Self::Item,
context: &mut c::CONTEXT,
) -> c::BOOL {
unsafe {
self(
image,
process,
thread,
frame,
context,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0,
)
}
}
}

Ok((i, backtrace_context))
impl StackWalker for StackWalk64Fn {
type Item = c::STACKFRAME64;
fn walk(
&self,
image: c::DWORD,
process: c::HANDLE,
thread: c::HANDLE,
frame: &mut Self::Item,
context: &mut c::CONTEXT,
) -> c::BOOL {
unsafe {
self(
image,
process,
thread,
frame,
context,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
)
}
}
}

type SymInitializeFn =
unsafe extern "system" fn(c::HANDLE, *mut c_void,
c::BOOL) -> c::BOOL;
type SymCleanupFn =
unsafe extern "system" fn(c::HANDLE) -> c::BOOL;

type StackWalkExFn =
unsafe extern "system" fn(c::DWORD, c::HANDLE, c::HANDLE,
*mut c::STACKFRAME_EX, *mut c::CONTEXT,
*mut c_void, *mut c_void,
*mut c_void, *mut c_void, c::DWORD) -> c::BOOL;

#[cfg(target_arch = "x86")]
fn init_frame(frame: &mut c::STACKFRAME_EX,
ctx: &c::CONTEXT) -> c::DWORD {
frame.AddrPC.Offset = ctx.Eip as u64;
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
frame.AddrStack.Offset = ctx.Esp as u64;
frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
frame.AddrFrame.Offset = ctx.Ebp as u64;
frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_I386
trait StackFrame {
fn new() -> Self;
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD;
fn get_addr(&self) -> *const u8;
}

#[cfg(target_arch = "x86_64")]
fn init_frame(frame: &mut c::STACKFRAME_EX,
ctx: &c::CONTEXT) -> c::DWORD {
frame.AddrPC.Offset = ctx.Rip as u64;
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
frame.AddrStack.Offset = ctx.Rsp as u64;
frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
frame.AddrFrame.Offset = ctx.Rbp as u64;
frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_AMD64
impl StackFrame for c::STACKFRAME_EX {
fn new() -> c::STACKFRAME_EX {
unsafe { mem::zeroed() }
}

#[cfg(target_arch = "x86")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Eip as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Esp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Ebp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_I386
}
#[cfg(target_arch = "x86_64")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Rip as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Rsp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Rbp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_AMD64
}

fn get_addr(&self) -> *const u8 {
(self.AddrPC.Offset - 1) as *const u8
}
}

impl StackFrame for c::STACKFRAME64 {
fn new() -> c::STACKFRAME64 {
unsafe { mem::zeroed() }
}

#[cfg(target_arch = "x86")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Eip as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Esp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Ebp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_I386
}
#[cfg(target_arch = "x86_64")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Rip as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Rsp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Rbp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_AMD64
}

fn get_addr(&self) -> *const u8 {
(self.AddrPC.Offset - 1) as *const u8
}
}

enum StackWalkVariant {
StackWalkEx(StackWalkExFn, printing::PrintingFnsEx),
StackWalk64(StackWalk64Fn, printing::PrintingFns64),
}

pub struct BacktraceContext {
handle: c::HANDLE,
SymCleanup: SymCleanupFn,
// Only used in printing for msvc and not gnu
// The gnu version is effectively a ZST dummy.
#[allow(dead_code)]
StackWalkVariant: StackWalkVariant,
// keeping DynamycLibrary loaded until its functions no longer needed
#[allow(dead_code)]
dbghelp: DynamicLibrary,
}

impl Drop for BacktraceContext {
fn drop(&mut self) {
unsafe { (self.SymCleanup)(self.handle); }
unsafe {
(self.SymCleanup)(self.handle);
}
}
}
14 changes: 14 additions & 0 deletions src/libstd/sys/windows/backtrace/printing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ mod printing;
#[cfg(target_env = "gnu")]
mod printing {
pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname};

// dummy functions to mirror those present in msvc version.
use sys::dynamic_lib::DynamicLibrary;
use io;
pub struct PrintingFnsEx {}
pub struct PrintingFns64 {}
pub fn load_printing_fns_ex(_: &DynamicLibrary) -> io::Result<PrintingFnsEx> {
Ok(PrintingFnsEx{})
}
pub fn load_printing_fns_64(_: &DynamicLibrary) -> io::Result<PrintingFns64> {
Ok(PrintingFns64{})
}
}

pub use self::printing::{foreach_symbol_fileline, resolve_symname};
pub use self::printing::{load_printing_fns_ex, load_printing_fns_64,
PrintingFnsEx, PrintingFns64};
Loading