#[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);
+ }
}
}
use ffi::CStr;
use io;
-use libc::{c_ulong, c_char};
+use libc::{c_char, c_ulong};
use mem;
-use sys::c;
use sys::backtrace::BacktraceContext;
+use sys::backtrace::StackWalkVariant;
+use sys::c;
+use sys::dynamic_lib::DynamicLibrary;
use sys_common::backtrace::Frame;
+// Structs holding printing functions and loaders for them
+// Two versions depending on whether dbghelp.dll has StackWalkEx or not
+// (the former being in newer Windows versions, the older being in Win7 and before)
+pub struct PrintingFnsEx {
+ resolve_symname: SymFromInlineContextFn,
+ sym_get_line: SymGetLineFromInlineContextFn,
+}
+pub struct PrintingFns64 {
+ resolve_symname: SymFromAddrFn,
+ sym_get_line: SymGetLineFromAddr64Fn,
+}
+
+pub fn load_printing_fns_ex(dbghelp: &DynamicLibrary) -> io::Result<PrintingFnsEx> {
+ Ok(PrintingFnsEx {
+ resolve_symname: sym!(dbghelp, "SymFromInlineContext", SymFromInlineContextFn)?,
+ sym_get_line: sym!(
+ dbghelp,
+ "SymGetLineFromInlineContext",
+ SymGetLineFromInlineContextFn
+ )?,
+ })
+}
+pub fn load_printing_fns_64(dbghelp: &DynamicLibrary) -> io::Result<PrintingFns64> {
+ Ok(PrintingFns64 {
+ resolve_symname: sym!(dbghelp, "SymFromAddr", SymFromAddrFn)?,
+ sym_get_line: sym!(dbghelp, "SymGetLineFromAddr64", SymGetLineFromAddr64Fn)?,
+ })
+}
+
+type SymFromAddrFn =
+ unsafe extern "system" fn(c::HANDLE, u64, *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;
type SymFromInlineContextFn =
- unsafe extern "system" fn(c::HANDLE, u64, c::ULONG,
- *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;
-type SymGetLineFromInlineContextFn =
- unsafe extern "system" fn(c::HANDLE, u64, c::ULONG,
- u64, *mut c::DWORD, *mut c::IMAGEHLP_LINE64) -> c::BOOL;
+ unsafe extern "system" fn(c::HANDLE, u64, c::ULONG, *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;
+
+type SymGetLineFromAddr64Fn =
+ unsafe extern "system" fn(c::HANDLE, u64, *mut u32, *mut c::IMAGEHLP_LINE64) -> c::BOOL;
+type SymGetLineFromInlineContextFn = unsafe extern "system" fn(
+ c::HANDLE,
+ u64,
+ c::ULONG,
+ u64,
+ *mut c::DWORD,
+ *mut c::IMAGEHLP_LINE64,
+) -> c::BOOL;
/// Converts a pointer to symbol to its string value.
-pub fn resolve_symname<F>(frame: Frame,
- callback: F,
- context: &BacktraceContext) -> io::Result<()>
- where F: FnOnce(Option<&str>) -> io::Result<()>
+pub fn resolve_symname<F>(frame: Frame, callback: F, context: &BacktraceContext) -> io::Result<()>
+where
+ F: FnOnce(Option<&str>) -> io::Result<()>,
{
- let SymFromInlineContext = sym!(&context.dbghelp,
- "SymFromInlineContext",
- SymFromInlineContextFn)?;
+ match context.StackWalkVariant {
+ StackWalkVariant::StackWalkEx(_, ref fns) => resolve_symname_internal(
+ |process: c::HANDLE,
+ symbol_address: u64,
+ inline_context: c::ULONG,
+ info: *mut c::SYMBOL_INFO| unsafe {
+ let mut displacement = 0u64;
+ (fns.resolve_symname)(
+ process,
+ symbol_address,
+ inline_context,
+ &mut displacement,
+ info,
+ )
+ },
+ frame,
+ callback,
+ context,
+ ),
+ StackWalkVariant::StackWalk64(_, ref fns) => resolve_symname_internal(
+ |process: c::HANDLE,
+ symbol_address: u64,
+ _inline_context: c::ULONG,
+ info: *mut c::SYMBOL_INFO| unsafe {
+ let mut displacement = 0u64;
+ (fns.resolve_symname)(process, symbol_address, &mut displacement, info)
+ },
+ frame,
+ callback,
+ context,
+ ),
+ }
+}
+fn resolve_symname_internal<F, R>(
+ mut symbol_resolver: R,
+ frame: Frame,
+ callback: F,
+ context: &BacktraceContext,
+) -> io::Result<()>
+where
+ F: FnOnce(Option<&str>) -> io::Result<()>,
+ R: FnMut(c::HANDLE, u64, c::ULONG, *mut c::SYMBOL_INFO) -> c::BOOL,
+{
unsafe {
let mut info: c::SYMBOL_INFO = mem::zeroed();
info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
// due to struct alignment.
info.SizeOfStruct = 88;
- let mut displacement = 0u64;
- let ret = SymFromInlineContext(context.handle,
- frame.symbol_addr as u64,
- frame.inline_context,
- &mut displacement,
- &mut info);
- let valid_range = if ret == c::TRUE &&
- frame.symbol_addr as usize >= info.Address as usize {
+ let ret = symbol_resolver(
+ context.handle,
+ frame.symbol_addr as u64,
+ frame.inline_context,
+ &mut info,
+ );
+ let valid_range = if ret == c::TRUE && frame.symbol_addr as usize >= info.Address as usize {
if info.Size != 0 {
(frame.symbol_addr as usize) < info.Address as usize + info.Size as usize
} else {
}
}
-pub fn foreach_symbol_fileline<F>(frame: Frame,
- mut f: F,
- context: &BacktraceContext)
- -> io::Result<bool>
- where F: FnMut(&[u8], u32) -> io::Result<()>
+pub fn foreach_symbol_fileline<F>(
+ frame: Frame,
+ callback: F,
+ context: &BacktraceContext,
+) -> io::Result<bool>
+where
+ F: FnMut(&[u8], u32) -> io::Result<()>,
{
- let SymGetLineFromInlineContext = sym!(&context.dbghelp,
- "SymGetLineFromInlineContext",
- SymGetLineFromInlineContextFn)?;
+ match context.StackWalkVariant {
+ StackWalkVariant::StackWalkEx(_, ref fns) => foreach_symbol_fileline_iternal(
+ |process: c::HANDLE,
+ frame_address: u64,
+ inline_context: c::ULONG,
+ line: *mut c::IMAGEHLP_LINE64| unsafe {
+ let mut displacement = 0u32;
+ (fns.sym_get_line)(
+ process,
+ frame_address,
+ inline_context,
+ 0,
+ &mut displacement,
+ line,
+ )
+ },
+ frame,
+ callback,
+ context,
+ ),
+ StackWalkVariant::StackWalk64(_, ref fns) => foreach_symbol_fileline_iternal(
+ |process: c::HANDLE,
+ frame_address: u64,
+ _inline_context: c::ULONG,
+ line: *mut c::IMAGEHLP_LINE64| unsafe {
+ let mut displacement = 0u32;
+ (fns.sym_get_line)(process, frame_address, &mut displacement, line)
+ },
+ frame,
+ callback,
+ context,
+ ),
+ }
+}
+fn foreach_symbol_fileline_iternal<F, G>(
+ mut line_getter: G,
+ frame: Frame,
+ mut callback: F,
+ context: &BacktraceContext,
+) -> io::Result<bool>
+where
+ F: FnMut(&[u8], u32) -> io::Result<()>,
+ G: FnMut(c::HANDLE, u64, c::ULONG, *mut c::IMAGEHLP_LINE64) -> c::BOOL,
+{
unsafe {
let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;
- let mut displacement = 0u32;
- let ret = SymGetLineFromInlineContext(context.handle,
- frame.exact_position as u64,
- frame.inline_context,
- 0,
- &mut displacement,
- &mut line);
+ let ret = line_getter(
+ context.handle,
+ frame.exact_position as u64,
+ frame.inline_context,
+ &mut line,
+ );
if ret == c::TRUE {
let name = CStr::from_ptr(line.Filename).to_bytes();
- f(name, line.LineNumber as u32)?;
+ callback(name, line.LineNumber as u32)?;
}
Ok(false)
}