Preserve full native stack traces in errors (#823)

* 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 assertions
This commit is contained in:
Alex Crichton
2020-01-15 15:30:17 -06:00
committed by GitHub
parent b8e4354efc
commit e7e08f162d
12 changed files with 89 additions and 239 deletions

View File

@@ -1,11 +1,12 @@
//! WebAssembly trap handling, which is built on top of the lower-level
//! signalhandling mechanisms.
use crate::backtrace::{get_backtrace, Backtrace};
use crate::trap_registry::get_trap_registry;
use crate::trap_registry::TrapDescription;
use crate::vmcontext::{VMContext, VMFunctionBody};
use backtrace::Backtrace;
use std::cell::Cell;
use std::fmt;
use std::ptr;
use wasmtime_environ::ir;
@@ -19,7 +20,7 @@ extern "C" {
}
thread_local! {
static RECORDED_TRAP: Cell<Option<(TrapDescription, Backtrace)>> = Cell::new(None);
static RECORDED_TRAP: Cell<Option<Trap>> = Cell::new(None);
static JMP_BUF: Cell<*const u8> = Cell::new(ptr::null());
static RESET_GUARD_PAGE: Cell<bool> = Cell::new(false);
}
@@ -44,26 +45,26 @@ pub extern "C" fn CheckIfTrapAtAddress(_pc: *const u8) -> i8 {
pub extern "C" fn RecordTrap(pc: *const u8, reset_guard_page: bool) {
// TODO: please see explanation in CheckIfTrapAtAddress.
let registry = get_trap_registry();
let trap_desc = registry
.get_trap(pc as usize)
.unwrap_or_else(|| TrapDescription {
source_loc: ir::SourceLoc::default(),
trap_code: ir::TrapCode::StackOverflow,
});
let trap_backtrace = get_backtrace();
let trap = Trap {
desc: registry
.get_trap(pc as usize)
.unwrap_or_else(|| TrapDescription {
source_loc: ir::SourceLoc::default(),
trap_code: ir::TrapCode::StackOverflow,
}),
backtrace: Backtrace::new_unresolved(),
};
if reset_guard_page {
RESET_GUARD_PAGE.with(|v| v.set(true));
}
RECORDED_TRAP.with(|data| {
assert_eq!(
data.get(),
None,
let prev = data.replace(Some(trap));
assert!(
prev.is_none(),
"Only one trap per thread can be recorded at a moment!"
);
data.set(Some((trap_desc, trap_backtrace)))
});
}
@@ -113,21 +114,31 @@ fn reset_guard_page() {}
/// Stores trace message with backtrace.
#[derive(Debug)]
pub struct TrapMessageAndStack(pub String, pub Backtrace);
pub struct Trap {
/// What sort of trap happened, as well as where in the original wasm module
/// it happened.
pub desc: TrapDescription,
/// Native stack backtrace at the time the trap occurred
pub backtrace: Backtrace,
}
fn trap_message_and_stack() -> TrapMessageAndStack {
let trap_desc = RECORDED_TRAP
.with(|data| data.replace(None))
.expect("trap_message must be called after trap occurred");
TrapMessageAndStack(
format!(
impl fmt::Display for Trap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"wasm trap: {}, source location: {}",
trap_code_to_expected_string(trap_desc.0.trap_code),
trap_desc.0.source_loc,
),
trap_desc.1,
)
trap_code_to_expected_string(self.desc.trap_code),
self.desc.source_loc
)
}
}
impl std::error::Error for Trap {}
fn last_trap() -> Trap {
RECORDED_TRAP
.with(|data| data.replace(None))
.expect("trap_message must be called after trap occurred")
}
fn trap_code_to_expected_string(trap_code: ir::TrapCode) -> String {
@@ -156,9 +167,9 @@ pub unsafe extern "C" fn wasmtime_call_trampoline(
vmctx: *mut VMContext,
callee: *const VMFunctionBody,
values_vec: *mut u8,
) -> Result<(), TrapMessageAndStack> {
) -> Result<(), Trap> {
if WasmtimeCallTrampoline(vmctx as *mut u8, callee, values_vec) == 0 {
Err(trap_message_and_stack())
Err(last_trap())
} else {
Ok(())
}
@@ -170,9 +181,9 @@ pub unsafe extern "C" fn wasmtime_call_trampoline(
pub unsafe extern "C" fn wasmtime_call(
vmctx: *mut VMContext,
callee: *const VMFunctionBody,
) -> Result<(), TrapMessageAndStack> {
) -> Result<(), Trap> {
if WasmtimeCall(vmctx as *mut u8, callee) == 0 {
Err(trap_message_and_stack())
Err(last_trap())
} else {
Ok(())
}