Browse Source
* Preserve full native stack traces in errors This commit builds on #759 by performing a few refactorings: * The `backtrace` crate is updated to 0.3.42 which incorporates the Windows-specific stack-walking code, so that's no longer needed. * A full `backtrace::Backtrace` type is held in a trap at all times. * The trap structures in the `wasmtime-*` internal crates were refactored a bit to preserve more information and deal with raw values rather than converting between various types and strings. * The `wasmtime::Trap` type has been updated with these various changes. Eventually I think we'll want to likely render full stack traces (and/or partial wasm ones) into error messages, but for now that's left as-is and we can always improve it later. I suspect the most relevant thing we need to do is to implement function name symbolication for wasm functions first, and then afterwards we can incorporate native function names! * Fix some test suite assertionspull/825/head
Alex Crichton
5 years ago
committed by
GitHub
12 changed files with 89 additions and 239 deletions
@ -1,148 +0,0 @@ |
|||
//! Backtrace object and utilities.
|
|||
|
|||
use crate::jit_function_registry; |
|||
use std::sync::Arc; |
|||
|
|||
/// Information about backtrace frame.
|
|||
#[derive(Debug, Copy, Clone, PartialEq, Eq)] |
|||
pub struct BacktraceFrame { |
|||
pc: usize, |
|||
} |
|||
|
|||
impl Default for BacktraceFrame { |
|||
fn default() -> Self { |
|||
Self { pc: 0 } |
|||
} |
|||
} |
|||
|
|||
impl BacktraceFrame { |
|||
/// Current PC or IP pointer for the frame.
|
|||
pub fn pc(&self) -> usize { |
|||
self.pc |
|||
} |
|||
/// Additinal frame information.
|
|||
pub fn tag(&self) -> Option<Arc<jit_function_registry::JITFunctionTag>> { |
|||
jit_function_registry::find(self.pc) |
|||
} |
|||
} |
|||
|
|||
const BACKTRACE_LIMIT: usize = 32; |
|||
|
|||
/// Backtrace during WebAssembly trap.
|
|||
#[derive(Copy, Clone, PartialEq, Eq)] |
|||
pub struct Backtrace { |
|||
len: usize, |
|||
frames: [BacktraceFrame; BACKTRACE_LIMIT], |
|||
} |
|||
|
|||
impl Backtrace { |
|||
fn new() -> Self { |
|||
Self { |
|||
len: 0, |
|||
frames: [Default::default(); BACKTRACE_LIMIT], |
|||
} |
|||
} |
|||
fn full(&self) -> bool { |
|||
self.len >= BACKTRACE_LIMIT |
|||
} |
|||
fn push(&mut self, frame: BacktraceFrame) { |
|||
assert!(self.len < BACKTRACE_LIMIT); |
|||
self.frames[self.len] = frame; |
|||
self.len += 1; |
|||
} |
|||
/// Amount of the backtrace frames.
|
|||
pub fn len(&self) -> usize { |
|||
self.len |
|||
} |
|||
} |
|||
|
|||
impl std::ops::Index<usize> for Backtrace { |
|||
type Output = BacktraceFrame; |
|||
fn index(&self, index: usize) -> &Self::Output { |
|||
assert!(index < self.len); |
|||
&self.frames[index] |
|||
} |
|||
} |
|||
|
|||
impl std::fmt::Debug for Backtrace { |
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|||
writeln!(f, "Backtrace![")?; |
|||
for i in 0..self.len() { |
|||
let frame = &self.frames[i]; |
|||
writeln!(f, " {:x}: {:?}", frame.pc(), frame.tag())?; |
|||
} |
|||
write!(f, "]")?; |
|||
Ok(()) |
|||
} |
|||
} |
|||
|
|||
#[cfg(not(all(target_os = "windows", target_arch = "x86_64")))] |
|||
fn capture_stack<F>(mut f: F) |
|||
where |
|||
F: FnMut(usize) -> bool, |
|||
{ |
|||
use backtrace::trace; |
|||
trace(|frame| { |
|||
let pc = frame.ip() as usize; |
|||
f(pc) |
|||
}); |
|||
} |
|||
|
|||
#[cfg(all(target_os = "windows", target_arch = "x86_64"))] |
|||
fn capture_stack<F>(mut f: F) |
|||
where |
|||
F: FnMut(usize) -> bool, |
|||
{ |
|||
use std::mem::MaybeUninit; |
|||
use std::ptr; |
|||
use winapi::um::winnt::{ |
|||
RtlCaptureContext, RtlLookupFunctionEntry, RtlVirtualUnwind, CONTEXT, UNW_FLAG_NHANDLER, |
|||
}; |
|||
|
|||
#[repr(C, align(16))] |
|||
struct WrappedContext(CONTEXT); |
|||
|
|||
unsafe { |
|||
let mut ctx = WrappedContext(MaybeUninit::uninit().assume_init()); |
|||
RtlCaptureContext(&mut ctx.0); |
|||
let mut unwind_history_table = MaybeUninit::zeroed().assume_init(); |
|||
while ctx.0.Rip != 0 { |
|||
let cont = f(ctx.0.Rip as usize); |
|||
if !cont { |
|||
break; |
|||
} |
|||
|
|||
let mut image_base: u64 = 0; |
|||
let mut handler_data: *mut core::ffi::c_void = ptr::null_mut(); |
|||
let mut establisher_frame: u64 = 0; |
|||
let rf = RtlLookupFunctionEntry(ctx.0.Rip, &mut image_base, &mut unwind_history_table); |
|||
if rf.is_null() { |
|||
ctx.0.Rip = ptr::read(ctx.0.Rsp as *const u64); |
|||
ctx.0.Rsp += 8; |
|||
} else { |
|||
RtlVirtualUnwind( |
|||
UNW_FLAG_NHANDLER, |
|||
image_base, |
|||
ctx.0.Rip, |
|||
rf, |
|||
&mut ctx.0, |
|||
&mut handler_data, |
|||
&mut establisher_frame, |
|||
ptr::null_mut(), |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// Returns current backtrace. Only registered wasmtime functions will be listed.
|
|||
pub fn get_backtrace() -> Backtrace { |
|||
let mut frames = Backtrace::new(); |
|||
capture_stack(|pc| { |
|||
if let Some(_) = jit_function_registry::find(pc) { |
|||
frames.push(BacktraceFrame { pc }); |
|||
} |
|||
!frames.full() |
|||
}); |
|||
frames |
|||
} |
Loading…
Reference in new issue