Add a fallback for stacktrace printing for older Windows versions.
PR #47252 switched stack inspection functions of dbghelp.dll to their newer alternatives that also capture inlined context. Unfortunately, said new alternatives are not present in older dbghelp.dll versions. In particular Windows 7 at the time of writing has dbghelp.dll version 6.1.7601 from 2010, that lacks StackWalkEx and friends. Fixes #50138
This commit is contained in:
parent
e3bf634e06
commit
d39c66bf4f
@ -48,24 +48,21 @@ pub mod gnu;
|
||||
|
||||
pub use self::printing::{resolve_symname, foreach_symbol_fileline};
|
||||
|
||||
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 ResStackWalkEx = sym!(dbghelp, "StackWalkEx", StackWalkExFn);
|
||||
let ResStackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn);
|
||||
|
||||
// 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,
|
||||
@ -76,49 +73,117 @@ pub fn unwind_backtrace(frames: &mut [Frame])
|
||||
// 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!
|
||||
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;
|
||||
match (ResStackWalkEx, ResStackWalk64) {
|
||||
(Ok(StackWalkEx), _) => {
|
||||
let mut frame: c::STACKFRAME_EX = unsafe { mem::zeroed() };
|
||||
frame.StackFrameSize = mem::size_of_val(&frame) as c::DWORD;
|
||||
let image = init_frame_ex(&mut frame, &context);
|
||||
|
||||
frames[i] = Frame {
|
||||
symbol_addr: addr,
|
||||
exact_position: addr,
|
||||
inline_context: frame.InlineFrameContext,
|
||||
};
|
||||
i += 1;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((i, backtrace_context))
|
||||
}
|
||||
}
|
||||
(_, Ok(StackWalk64)) => {
|
||||
let mut frame: c::STACKFRAME64 = unsafe { mem::zeroed() };
|
||||
let image = init_frame_64(&mut frame, &context);
|
||||
|
||||
Ok((i, backtrace_context))
|
||||
// Start from -1 to avoid printing this stack frame, which will
|
||||
// always be exactly the same.
|
||||
let mut i = 0;
|
||||
unsafe {
|
||||
while i < frames.len()
|
||||
&& StackWalk64(
|
||||
image,
|
||||
process,
|
||||
thread,
|
||||
&mut frame,
|
||||
&mut context,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
) == c::TRUE
|
||||
{
|
||||
let addr = frame.AddrPC.Offset;
|
||||
if addr == frame.AddrReturn.Offset || addr == 0 || frame.AddrReturn.Offset == 0
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
frames[i] = Frame {
|
||||
symbol_addr: (addr - 1) as *const u8,
|
||||
exact_position: (addr - 1) as *const u8,
|
||||
inline_context: 0,
|
||||
};
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((i, backtrace_context))
|
||||
}
|
||||
(Err(e), _) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
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 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 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;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
fn init_frame(frame: &mut c::STACKFRAME_EX,
|
||||
ctx: &c::CONTEXT) -> c::DWORD {
|
||||
fn init_frame_ex(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;
|
||||
@ -129,8 +194,29 @@ fn init_frame(frame: &mut c::STACKFRAME_EX,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn init_frame(frame: &mut c::STACKFRAME_EX,
|
||||
ctx: &c::CONTEXT) -> c::DWORD {
|
||||
fn init_frame_ex(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
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
fn init_frame_64(frame: &mut c::STACKFRAME64, 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
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn init_frame_64(frame: &mut c::STACKFRAME64, 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;
|
||||
|
@ -10,88 +10,209 @@
|
||||
|
||||
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::c;
|
||||
use sys_common::backtrace::Frame;
|
||||
|
||||
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 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;
|
||||
|
||||
type SymFromAddrFn =
|
||||
unsafe extern "system" fn(c::HANDLE, u64, *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;
|
||||
|
||||
/// 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 (
|
||||
sym!(
|
||||
&context.dbghelp,
|
||||
"SymFromInlineContext",
|
||||
SymFromInlineContextFn
|
||||
),
|
||||
sym!(&context.dbghelp, "SymFromAddr", SymFromAddrFn),
|
||||
) {
|
||||
(Ok(SymFromInlineContext), _) => unsafe {
|
||||
let mut info: c::SYMBOL_INFO = mem::zeroed();
|
||||
info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
|
||||
// the struct size in C. the value is different to
|
||||
// `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
|
||||
// due to struct alignment.
|
||||
info.SizeOfStruct = 88;
|
||||
|
||||
unsafe {
|
||||
let mut info: c::SYMBOL_INFO = mem::zeroed();
|
||||
info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
|
||||
// the struct size in C. the value is different to
|
||||
// `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
|
||||
// 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 {
|
||||
if info.Size != 0 {
|
||||
(frame.symbol_addr as usize) < info.Address as usize + info.Size as usize
|
||||
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 {
|
||||
if info.Size != 0 {
|
||||
(frame.symbol_addr as usize) < info.Address as usize + info.Size as usize
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let symname = if valid_range {
|
||||
let ptr = info.Name.as_ptr() as *const c_char;
|
||||
CStr::from_ptr(ptr).to_str().ok()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let symname = if valid_range {
|
||||
let ptr = info.Name.as_ptr() as *const c_char;
|
||||
CStr::from_ptr(ptr).to_str().ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
callback(symname)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn foreach_symbol_fileline<F>(frame: Frame,
|
||||
mut f: F,
|
||||
context: &BacktraceContext)
|
||||
-> io::Result<bool>
|
||||
where F: FnMut(&[u8], u32) -> io::Result<()>
|
||||
None
|
||||
};
|
||||
callback(symname)
|
||||
{
|
||||
let SymGetLineFromInlineContext = sym!(&context.dbghelp,
|
||||
"SymGetLineFromInlineContext",
|
||||
SymGetLineFromInlineContextFn)?;
|
||||
match (
|
||||
sym!(
|
||||
&context.dbghelp,
|
||||
"SymFromInlineContext",
|
||||
SymFromInlineContextFn
|
||||
),
|
||||
sym!(&context.dbghelp, "SymFromAddr", SymFromAddrFn),
|
||||
) {
|
||||
(Ok(SymFromInlineContext), _) => unsafe {
|
||||
let mut info: c::SYMBOL_INFO = mem::zeroed();
|
||||
info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
|
||||
// the struct size in C. the value is different to
|
||||
// `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
|
||||
// due to struct alignment.
|
||||
info.SizeOfStruct = 88;
|
||||
|
||||
unsafe {
|
||||
let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
|
||||
line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;
|
||||
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 {
|
||||
if info.Size != 0 {
|
||||
(frame.symbol_addr as usize) < info.Address as usize + info.Size as usize
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let symname = if valid_range {
|
||||
let ptr = info.Name.as_ptr() as *const c_char;
|
||||
CStr::from_ptr(ptr).to_str().ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
callback(symname)
|
||||
},
|
||||
(_, Ok(SymFromAddr)) => unsafe {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
callback(symname)
|
||||
},
|
||||
(_, Ok(SymFromAddr)) => unsafe {
|
||||
let mut info: c::SYMBOL_INFO = mem::zeroed();
|
||||
info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
|
||||
// the struct size in C. the value is different to
|
||||
// `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
|
||||
// due to struct alignment.
|
||||
info.SizeOfStruct = 88;
|
||||
|
||||
let mut displacement = 0u32;
|
||||
let ret = SymGetLineFromInlineContext(context.handle,
|
||||
frame.exact_position as u64,
|
||||
frame.inline_context,
|
||||
0,
|
||||
&mut displacement,
|
||||
&mut line);
|
||||
if ret == c::TRUE {
|
||||
let name = CStr::from_ptr(line.Filename).to_bytes();
|
||||
f(name, line.LineNumber as u32)?;
|
||||
}
|
||||
Ok(false)
|
||||
let mut displacement = 0u64;
|
||||
let ret = SymFromAddr(
|
||||
context.handle,
|
||||
frame.symbol_addr as u64,
|
||||
&mut displacement,
|
||||
&mut info,
|
||||
);
|
||||
|
||||
let symname = if ret == c::TRUE {
|
||||
let ptr = info.Name.as_ptr() as *const c_char;
|
||||
CStr::from_ptr(ptr).to_str().ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
callback(symname)
|
||||
},
|
||||
(Err(e), _) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn foreach_symbol_fileline<F>(
|
||||
frame: Frame,
|
||||
mut f: F,
|
||||
context: &BacktraceContext,
|
||||
) -> io::Result<bool>
|
||||
where
|
||||
F: FnMut(&[u8], u32) -> io::Result<()>,
|
||||
{
|
||||
match (
|
||||
sym!(
|
||||
&context.dbghelp,
|
||||
"SymGetLineFromInlineContext",
|
||||
SymGetLineFromInlineContextFn
|
||||
),
|
||||
sym!(
|
||||
&context.dbghelp,
|
||||
"SymGetLineFromAddr64",
|
||||
SymGetLineFromAddr64Fn
|
||||
),
|
||||
) {
|
||||
(Ok(SymGetLineFromInlineContext), _) => 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,
|
||||
);
|
||||
if ret == c::TRUE {
|
||||
let name = CStr::from_ptr(line.Filename).to_bytes();
|
||||
f(name, line.LineNumber as u32)?;
|
||||
}
|
||||
Ok(false)
|
||||
},
|
||||
(_, Ok(SymGetLineFromAddr64)) => 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 = SymGetLineFromAddr64(
|
||||
context.handle,
|
||||
frame.exact_position as u64,
|
||||
&mut displacement,
|
||||
&mut line,
|
||||
);
|
||||
if ret == c::TRUE {
|
||||
let name = CStr::from_ptr(line.Filename).to_bytes();
|
||||
f(name, line.LineNumber as u32)?;
|
||||
}
|
||||
Ok(false)
|
||||
},
|
||||
(Err(e), _) => Err(e),
|
||||
}
|
||||
}
|
||||
|
@ -635,6 +635,22 @@ pub struct STACKFRAME_EX {
|
||||
pub InlineFrameContext: DWORD,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[cfg(feature = "backtrace")]
|
||||
pub struct STACKFRAME64 {
|
||||
pub AddrPC: ADDRESS64,
|
||||
pub AddrReturn: ADDRESS64,
|
||||
pub AddrFrame: ADDRESS64,
|
||||
pub AddrStack: ADDRESS64,
|
||||
pub AddrBStore: ADDRESS64,
|
||||
pub FuncTableEntry: *mut c_void,
|
||||
pub Params: [u64; 4],
|
||||
pub Far: BOOL,
|
||||
pub Virtual: BOOL,
|
||||
pub Reserved: [u64; 3],
|
||||
pub KdHelp: KDHELP64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[cfg(feature = "backtrace")]
|
||||
pub struct KDHELP64 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user