Expose precise offset information in wasmtime::FrameInfo (#1495)
* Consolidate trap/frame information This commit removes `TrapRegistry` in favor of consolidating this information in the `FRAME_INFO` we already have in the `wasmtime` crate. This allows us to keep information generally in one place and have one canonical location for "map this PC to some original wasm stuff". The intent for this is to next update with enough information to go from a program counter to a position in the original wasm file. * Expose module offset information in `FrameInfo` This commit implements functionality for `FrameInfo`, the wasm stack trace of a `Trap`, to return the module/function offset. This allows knowing the precise wasm location of each stack frame, instead of only the main trap itself. The intention here is to provide more visibility into the wasm source when something traps, so you know precisely where calls were and where traps were, in order to assist in debugging. Eventually we might use this information for mapping back to native source languages as well (given sufficient debug information). This change makes a previously-optional artifact of compilation always computed on the cranelift side of things. This `ModuleAddressMap` is then propagated to the same store of information other frame information is stored within. This also removes the need for passing a `SourceLoc` with wasm traps or to wasm trap creation, since the backtrace's wasm frames will be able to infer their own `SourceLoc` from the relevant program counters.
This commit is contained in:
@@ -14,7 +14,6 @@ use crate::vmcontext::{
|
||||
VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex,
|
||||
VMTableDefinition, VMTableImport, VMTrampoline,
|
||||
};
|
||||
use crate::TrapRegistration;
|
||||
use crate::{ExportFunction, ExportGlobal, ExportMemory, ExportTable};
|
||||
use memoffset::offset_of;
|
||||
use more_asserts::assert_lt;
|
||||
@@ -111,10 +110,6 @@ pub(crate) struct Instance {
|
||||
/// Handler run when `SIGBUS`, `SIGFPE`, `SIGILL`, or `SIGSEGV` are caught by the instance thread.
|
||||
pub(crate) signal_handler: Cell<Option<Box<SignalHandler>>>,
|
||||
|
||||
/// Handle to our registration of traps so signals know what trap to return
|
||||
/// when a segfault/sigill happens.
|
||||
pub(crate) trap_registration: TrapRegistration,
|
||||
|
||||
/// Additional context used by compiled wasm code. This field is last, and
|
||||
/// represents a dynamically-sized array that extends beyond the nominal
|
||||
/// end of the struct (similar to a flexible array member).
|
||||
@@ -603,7 +598,6 @@ impl Instance {
|
||||
dst: u32,
|
||||
src: u32,
|
||||
len: u32,
|
||||
source_loc: ir::SourceLoc,
|
||||
) -> Result<(), Trap> {
|
||||
// https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-init
|
||||
|
||||
@@ -619,7 +613,7 @@ impl Instance {
|
||||
.map_or(true, |n| n as usize > elem.len())
|
||||
|| dst.checked_add(len).map_or(true, |m| m > table.size())
|
||||
{
|
||||
return Err(Trap::wasm(source_loc, ir::TrapCode::TableOutOfBounds));
|
||||
return Err(Trap::wasm(ir::TrapCode::TableOutOfBounds));
|
||||
}
|
||||
|
||||
// TODO(#983): investigate replacing this get/set loop with a `memcpy`.
|
||||
@@ -654,7 +648,6 @@ impl Instance {
|
||||
dst: u32,
|
||||
src: u32,
|
||||
len: u32,
|
||||
source_loc: ir::SourceLoc,
|
||||
) -> Result<(), Trap> {
|
||||
// https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy
|
||||
|
||||
@@ -667,7 +660,7 @@ impl Instance {
|
||||
.checked_add(len)
|
||||
.map_or(true, |m| m as usize > memory.current_length)
|
||||
{
|
||||
return Err(Trap::wasm(source_loc, ir::TrapCode::HeapOutOfBounds));
|
||||
return Err(Trap::wasm(ir::TrapCode::HeapOutOfBounds));
|
||||
}
|
||||
|
||||
let dst = usize::try_from(dst).unwrap();
|
||||
@@ -691,14 +684,13 @@ impl Instance {
|
||||
dst: u32,
|
||||
src: u32,
|
||||
len: u32,
|
||||
source_loc: ir::SourceLoc,
|
||||
) -> Result<(), Trap> {
|
||||
let import = self.imported_memory(memory_index);
|
||||
unsafe {
|
||||
let foreign_instance = (&*import.vmctx).instance();
|
||||
let foreign_memory = &*import.from;
|
||||
let foreign_index = foreign_instance.memory_index(foreign_memory);
|
||||
foreign_instance.defined_memory_copy(foreign_index, dst, src, len, source_loc)
|
||||
foreign_instance.defined_memory_copy(foreign_index, dst, src, len)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -713,7 +705,6 @@ impl Instance {
|
||||
dst: u32,
|
||||
val: u32,
|
||||
len: u32,
|
||||
source_loc: ir::SourceLoc,
|
||||
) -> Result<(), Trap> {
|
||||
let memory = self.memory(memory_index);
|
||||
|
||||
@@ -721,7 +712,7 @@ impl Instance {
|
||||
.checked_add(len)
|
||||
.map_or(true, |m| m as usize > memory.current_length)
|
||||
{
|
||||
return Err(Trap::wasm(source_loc, ir::TrapCode::HeapOutOfBounds));
|
||||
return Err(Trap::wasm(ir::TrapCode::HeapOutOfBounds));
|
||||
}
|
||||
|
||||
let dst = isize::try_from(dst).unwrap();
|
||||
@@ -748,14 +739,13 @@ impl Instance {
|
||||
dst: u32,
|
||||
val: u32,
|
||||
len: u32,
|
||||
source_loc: ir::SourceLoc,
|
||||
) -> Result<(), Trap> {
|
||||
let import = self.imported_memory(memory_index);
|
||||
unsafe {
|
||||
let foreign_instance = (&*import.vmctx).instance();
|
||||
let foreign_memory = &*import.from;
|
||||
let foreign_index = foreign_instance.memory_index(foreign_memory);
|
||||
foreign_instance.defined_memory_fill(foreign_index, dst, val, len, source_loc)
|
||||
foreign_instance.defined_memory_fill(foreign_index, dst, val, len)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,7 +763,6 @@ impl Instance {
|
||||
dst: u32,
|
||||
src: u32,
|
||||
len: u32,
|
||||
source_loc: ir::SourceLoc,
|
||||
) -> Result<(), Trap> {
|
||||
// https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-memory-init
|
||||
|
||||
@@ -790,7 +779,7 @@ impl Instance {
|
||||
.checked_add(len)
|
||||
.map_or(true, |m| m as usize > memory.current_length)
|
||||
{
|
||||
return Err(Trap::wasm(source_loc, ir::TrapCode::HeapOutOfBounds));
|
||||
return Err(Trap::wasm(ir::TrapCode::HeapOutOfBounds));
|
||||
}
|
||||
|
||||
let src_slice = &data[src as usize..(src + len) as usize];
|
||||
@@ -859,7 +848,6 @@ impl InstanceHandle {
|
||||
/// safety.
|
||||
pub unsafe fn new(
|
||||
module: Arc<Module>,
|
||||
trap_registration: TrapRegistration,
|
||||
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||
imports: Imports,
|
||||
@@ -906,7 +894,6 @@ impl InstanceHandle {
|
||||
dbg_jit_registration,
|
||||
host_state,
|
||||
signal_handler: Cell::new(None),
|
||||
trap_registration,
|
||||
vmctx: VMContext {},
|
||||
};
|
||||
let layout = instance.alloc_layout();
|
||||
@@ -1256,7 +1243,6 @@ fn initialize_tables(instance: &Instance) -> Result<(), InstantiationError> {
|
||||
.map_or(true, |end| end > table.size() as usize)
|
||||
{
|
||||
return Err(InstantiationError::Trap(Trap::wasm(
|
||||
ir::SourceLoc::default(),
|
||||
ir::TrapCode::HeapOutOfBounds,
|
||||
)));
|
||||
}
|
||||
@@ -1332,7 +1318,6 @@ fn initialize_memories(
|
||||
.map_or(true, |end| end > memory.current_length)
|
||||
{
|
||||
return Err(InstantiationError::Trap(Trap::wasm(
|
||||
ir::SourceLoc::default(),
|
||||
ir::TrapCode::HeapOutOfBounds,
|
||||
)));
|
||||
}
|
||||
@@ -1407,9 +1392,9 @@ pub enum InstantiationError {
|
||||
|
||||
/// A trap ocurred during instantiation, after linking.
|
||||
#[error("Trap occurred during instantiation")]
|
||||
Trap(#[source] Trap),
|
||||
Trap(Trap),
|
||||
|
||||
/// A compilation error occured.
|
||||
#[error("Trap occurred while invoking start function")]
|
||||
StartTrap(#[source] Trap),
|
||||
StartTrap(Trap),
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ mod memory;
|
||||
mod mmap;
|
||||
mod sig_registry;
|
||||
mod table;
|
||||
mod trap_registry;
|
||||
mod traphandlers;
|
||||
mod vmcontext;
|
||||
|
||||
@@ -44,7 +43,6 @@ pub use crate::memory::{RuntimeLinearMemory, RuntimeMemoryCreator};
|
||||
pub use crate::mmap::Mmap;
|
||||
pub use crate::sig_registry::SignatureRegistry;
|
||||
pub use crate::table::Table;
|
||||
pub use crate::trap_registry::{TrapDescription, TrapRegistration, TrapRegistry};
|
||||
pub use crate::traphandlers::resume_panic;
|
||||
pub use crate::traphandlers::{catch_traps, raise_lib_trap, raise_user_trap, Trap};
|
||||
pub use crate::vmcontext::{
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
use crate::table::Table;
|
||||
use crate::traphandlers::raise_lib_trap;
|
||||
use crate::vmcontext::VMContext;
|
||||
use wasmtime_environ::ir;
|
||||
use wasmtime_environ::wasm::{DataIndex, DefinedMemoryIndex, ElemIndex, MemoryIndex, TableIndex};
|
||||
|
||||
/// Implementation of f32.ceil
|
||||
@@ -175,16 +174,14 @@ pub unsafe extern "C" fn wasmtime_table_copy(
|
||||
dst: u32,
|
||||
src: u32,
|
||||
len: u32,
|
||||
source_loc: u32,
|
||||
) {
|
||||
let result = {
|
||||
let dst_table_index = TableIndex::from_u32(dst_table_index);
|
||||
let src_table_index = TableIndex::from_u32(src_table_index);
|
||||
let source_loc = ir::SourceLoc::new(source_loc);
|
||||
let instance = (&mut *vmctx).instance();
|
||||
let dst_table = instance.get_table(dst_table_index);
|
||||
let src_table = instance.get_table(src_table_index);
|
||||
Table::copy(dst_table, src_table, dst, src, len, source_loc)
|
||||
Table::copy(dst_table, src_table, dst, src, len)
|
||||
};
|
||||
if let Err(trap) = result {
|
||||
raise_lib_trap(trap);
|
||||
@@ -199,14 +196,12 @@ pub unsafe extern "C" fn wasmtime_table_init(
|
||||
dst: u32,
|
||||
src: u32,
|
||||
len: u32,
|
||||
source_loc: u32,
|
||||
) {
|
||||
let result = {
|
||||
let table_index = TableIndex::from_u32(table_index);
|
||||
let source_loc = ir::SourceLoc::new(source_loc);
|
||||
let elem_index = ElemIndex::from_u32(elem_index);
|
||||
let instance = (&mut *vmctx).instance();
|
||||
instance.table_init(table_index, elem_index, dst, src, len, source_loc)
|
||||
instance.table_init(table_index, elem_index, dst, src, len)
|
||||
};
|
||||
if let Err(trap) = result {
|
||||
raise_lib_trap(trap);
|
||||
@@ -227,13 +222,11 @@ pub unsafe extern "C" fn wasmtime_defined_memory_copy(
|
||||
dst: u32,
|
||||
src: u32,
|
||||
len: u32,
|
||||
source_loc: u32,
|
||||
) {
|
||||
let result = {
|
||||
let memory_index = DefinedMemoryIndex::from_u32(memory_index);
|
||||
let source_loc = ir::SourceLoc::new(source_loc);
|
||||
let instance = (&mut *vmctx).instance();
|
||||
instance.defined_memory_copy(memory_index, dst, src, len, source_loc)
|
||||
instance.defined_memory_copy(memory_index, dst, src, len)
|
||||
};
|
||||
if let Err(trap) = result {
|
||||
raise_lib_trap(trap);
|
||||
@@ -247,13 +240,11 @@ pub unsafe extern "C" fn wasmtime_imported_memory_copy(
|
||||
dst: u32,
|
||||
src: u32,
|
||||
len: u32,
|
||||
source_loc: u32,
|
||||
) {
|
||||
let result = {
|
||||
let memory_index = MemoryIndex::from_u32(memory_index);
|
||||
let source_loc = ir::SourceLoc::new(source_loc);
|
||||
let instance = (&mut *vmctx).instance();
|
||||
instance.imported_memory_copy(memory_index, dst, src, len, source_loc)
|
||||
instance.imported_memory_copy(memory_index, dst, src, len)
|
||||
};
|
||||
if let Err(trap) = result {
|
||||
raise_lib_trap(trap);
|
||||
@@ -267,13 +258,11 @@ pub unsafe extern "C" fn wasmtime_memory_fill(
|
||||
dst: u32,
|
||||
val: u32,
|
||||
len: u32,
|
||||
source_loc: u32,
|
||||
) {
|
||||
let result = {
|
||||
let memory_index = DefinedMemoryIndex::from_u32(memory_index);
|
||||
let source_loc = ir::SourceLoc::new(source_loc);
|
||||
let instance = (&mut *vmctx).instance();
|
||||
instance.defined_memory_fill(memory_index, dst, val, len, source_loc)
|
||||
instance.defined_memory_fill(memory_index, dst, val, len)
|
||||
};
|
||||
if let Err(trap) = result {
|
||||
raise_lib_trap(trap);
|
||||
@@ -287,13 +276,11 @@ pub unsafe extern "C" fn wasmtime_imported_memory_fill(
|
||||
dst: u32,
|
||||
val: u32,
|
||||
len: u32,
|
||||
source_loc: u32,
|
||||
) {
|
||||
let result = {
|
||||
let memory_index = MemoryIndex::from_u32(memory_index);
|
||||
let source_loc = ir::SourceLoc::new(source_loc);
|
||||
let instance = (&mut *vmctx).instance();
|
||||
instance.imported_memory_fill(memory_index, dst, val, len, source_loc)
|
||||
instance.imported_memory_fill(memory_index, dst, val, len)
|
||||
};
|
||||
if let Err(trap) = result {
|
||||
raise_lib_trap(trap);
|
||||
@@ -308,14 +295,12 @@ pub unsafe extern "C" fn wasmtime_memory_init(
|
||||
dst: u32,
|
||||
src: u32,
|
||||
len: u32,
|
||||
source_loc: u32,
|
||||
) {
|
||||
let result = {
|
||||
let memory_index = MemoryIndex::from_u32(memory_index);
|
||||
let data_index = DataIndex::from_u32(data_index);
|
||||
let source_loc = ir::SourceLoc::new(source_loc);
|
||||
let instance = (&mut *vmctx).instance();
|
||||
instance.memory_init(memory_index, data_index, dst, src, len, source_loc)
|
||||
instance.memory_init(memory_index, data_index, dst, src, len)
|
||||
};
|
||||
if let Err(trap) = result {
|
||||
raise_lib_trap(trap);
|
||||
|
||||
@@ -100,7 +100,6 @@ impl Table {
|
||||
dst_index: u32,
|
||||
src_index: u32,
|
||||
len: u32,
|
||||
source_loc: ir::SourceLoc,
|
||||
) -> Result<(), Trap> {
|
||||
// https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-copy
|
||||
|
||||
@@ -111,7 +110,7 @@ impl Table {
|
||||
.checked_add(len)
|
||||
.map_or(true, |m| m > dst_table.size())
|
||||
{
|
||||
return Err(Trap::wasm(source_loc, ir::TrapCode::TableOutOfBounds));
|
||||
return Err(Trap::wasm(ir::TrapCode::TableOutOfBounds));
|
||||
}
|
||||
|
||||
let srcs = src_index..src_index + len;
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fmt;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use wasmtime_environ::ir;
|
||||
|
||||
/// The registry maintains descriptions of traps in currently allocated functions.
|
||||
#[derive(Default)]
|
||||
pub struct TrapRegistry {
|
||||
// This data structure is intended to be safe to use across many threads
|
||||
// since this is stored inside of a `Compiler` which, eventually, will be
|
||||
// used across many threads. To that end this is internally use an `Arc`
|
||||
// plus an `RwLock`.
|
||||
//
|
||||
// The problem that this data structure is solving is that when a
|
||||
// segfault/illegal instruction happens we need to answer "given this
|
||||
// hardware program counter what is the wasm reason this trap is being
|
||||
// raised"?
|
||||
//
|
||||
// The way this is answered here is done to minimize the amount of
|
||||
// synchronization (in theory) and have something like so:
|
||||
//
|
||||
// * Each module bulk-registers a list of in-memory pc addresses that have
|
||||
// traps. We assume that the range of traps for each module are always
|
||||
// disjoint.
|
||||
// * Each key in this `BTreeMap` is the highest trapping address and the
|
||||
// value contains the lowest address as well as all the individual
|
||||
// addresses in their own `HashMap`.
|
||||
// * Registration then looks by calculating the start/end and inserting
|
||||
// into this map (with some assertions about disjointed-ness)
|
||||
// * Lookup is done in two layers. First we find the corresponding entry
|
||||
// in the map and verify that a program counter falls in the start/end
|
||||
// range. Next we look up the address in the `traps` hash map below.
|
||||
//
|
||||
// The `register_traps` function works by returning an RAII guard that owns
|
||||
// a handle to this `Arc` as well, and when that type is dropped it will
|
||||
// automatically remove all trap information from this `ranges` list.
|
||||
ranges: Arc<RwLock<BTreeMap<usize, TrapGroup>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TrapGroup {
|
||||
/// The lowest key in the `trap` field.
|
||||
///
|
||||
/// This represents the start of the range of this group of traps, and the
|
||||
/// end of the range for this group of traps is stored as the key in the
|
||||
/// `ranges` struct above in `TrapRegistry`.
|
||||
start: usize,
|
||||
|
||||
/// All known traps in this group, mapped from program counter to the
|
||||
/// description of the trap itself.
|
||||
traps: HashMap<usize, TrapDescription>,
|
||||
}
|
||||
|
||||
/// RAII structure returned from `TrapRegistry::register_trap` to unregister
|
||||
/// trap information on drop.
|
||||
#[derive(Clone)]
|
||||
pub struct TrapRegistration {
|
||||
ranges: Arc<RwLock<BTreeMap<usize, TrapGroup>>>,
|
||||
end: Option<usize>,
|
||||
}
|
||||
|
||||
/// Description of a trap.
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub struct TrapDescription {
|
||||
/// Location of the trap in source binary module.
|
||||
pub source_loc: ir::SourceLoc,
|
||||
/// Code of the trap.
|
||||
pub trap_code: ir::TrapCode,
|
||||
}
|
||||
|
||||
impl fmt::Display for TrapDescription {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"wasm trap: {}, source location: {}",
|
||||
trap_code_to_expected_string(self.trap_code),
|
||||
self.source_loc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn trap_code_to_expected_string(trap_code: ir::TrapCode) -> String {
|
||||
use ir::TrapCode::*;
|
||||
match trap_code {
|
||||
StackOverflow => "call stack exhausted".to_string(),
|
||||
HeapOutOfBounds => "out of bounds memory access".to_string(),
|
||||
TableOutOfBounds => "undefined element: out of bounds table access".to_string(),
|
||||
OutOfBounds => "out of bounds".to_string(), // Note: not covered by the test suite
|
||||
IndirectCallToNull => "uninitialized element".to_string(),
|
||||
BadSignature => "indirect call type mismatch".to_string(),
|
||||
IntegerOverflow => "integer overflow".to_string(),
|
||||
IntegerDivisionByZero => "integer divide by zero".to_string(),
|
||||
BadConversionToInteger => "invalid conversion to integer".to_string(),
|
||||
UnreachableCodeReached => "unreachable".to_string(),
|
||||
Interrupt => "interrupt".to_string(), // Note: not covered by the test suite
|
||||
User(x) => format!("user trap {}", x), // Note: not covered by the test suite
|
||||
}
|
||||
}
|
||||
|
||||
impl TrapRegistry {
|
||||
/// Registers a list of traps.
|
||||
///
|
||||
/// Returns a RAII guard that deregisters all traps when dropped.
|
||||
pub fn register_traps(
|
||||
&self,
|
||||
list: impl IntoIterator<Item = (usize, ir::SourceLoc, ir::TrapCode)>,
|
||||
) -> TrapRegistration {
|
||||
let mut start = usize::max_value();
|
||||
let mut end = 0;
|
||||
let mut traps = HashMap::new();
|
||||
for (addr, source_loc, trap_code) in list.into_iter() {
|
||||
traps.insert(
|
||||
addr,
|
||||
TrapDescription {
|
||||
source_loc,
|
||||
trap_code,
|
||||
},
|
||||
);
|
||||
if addr < start {
|
||||
start = addr;
|
||||
}
|
||||
if addr > end {
|
||||
end = addr;
|
||||
}
|
||||
}
|
||||
if traps.len() == 0 {
|
||||
return TrapRegistration {
|
||||
ranges: self.ranges.clone(),
|
||||
end: None,
|
||||
};
|
||||
}
|
||||
let mut ranges = self.ranges.write().unwrap();
|
||||
|
||||
// Sanity check that no other group of traps overlaps with our
|
||||
// registration...
|
||||
if let Some((_, prev)) = ranges.range(end..).next() {
|
||||
assert!(prev.start > end);
|
||||
}
|
||||
if let Some((prev_end, _)) = ranges.range(..=start).next_back() {
|
||||
assert!(*prev_end < start);
|
||||
}
|
||||
|
||||
// ... and then register ourselves
|
||||
assert!(ranges.insert(end, TrapGroup { start, traps }).is_none());
|
||||
TrapRegistration {
|
||||
ranges: self.ranges.clone(),
|
||||
end: Some(end),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TrapRegistration {
|
||||
/// Gets a trap description at given address.
|
||||
pub fn get_trap(&self, address: usize) -> Option<TrapDescription> {
|
||||
let ranges = self.ranges.read().ok()?;
|
||||
let (end, group) = ranges.range(address..).next()?;
|
||||
if group.start <= address && address <= *end {
|
||||
group.traps.get(&address).copied()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TrapRegistration {
|
||||
fn drop(&mut self) {
|
||||
if let Some(end) = self.end {
|
||||
if let Ok(mut ranges) = self.ranges.write() {
|
||||
ranges.remove(&end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,11 @@
|
||||
//! signalhandling mechanisms.
|
||||
|
||||
use crate::instance::{InstanceHandle, SignalHandler};
|
||||
use crate::trap_registry::TrapDescription;
|
||||
use crate::vmcontext::VMContext;
|
||||
use backtrace::Backtrace;
|
||||
use std::any::Any;
|
||||
use std::cell::Cell;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::ptr;
|
||||
use std::sync::Once;
|
||||
@@ -319,11 +317,19 @@ fn reset_guard_page() {}
|
||||
pub enum Trap {
|
||||
/// A user-raised trap through `raise_user_trap`.
|
||||
User(Box<dyn Error + Send + Sync>),
|
||||
/// A wasm-originating trap from wasm code itself.
|
||||
|
||||
/// A trap raised from jit code
|
||||
Jit {
|
||||
/// The program counter in JIT code where this trap happened.
|
||||
pc: usize,
|
||||
/// Native stack backtrace at the time the trap occurred
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
/// A trap raised from a wasm libcall
|
||||
Wasm {
|
||||
/// What sort of trap happened, as well as where in the original wasm module
|
||||
/// it happened.
|
||||
desc: TrapDescription,
|
||||
/// Code of the trap.
|
||||
trap_code: ir::TrapCode,
|
||||
/// Native stack backtrace at the time the trap occurred
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
@@ -335,36 +341,23 @@ pub enum Trap {
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Display for Trap {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Trap::User(user) => user.fmt(f),
|
||||
Trap::Wasm { desc, .. } => desc.fmt(f),
|
||||
Trap::OOM { .. } => write!(f, "Out of memory"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Trap {}
|
||||
|
||||
impl Trap {
|
||||
/// Construct a new Wasm trap with the given source location and trap code.
|
||||
///
|
||||
/// Internally saves a backtrace when constructed.
|
||||
pub fn wasm(source_loc: ir::SourceLoc, trap_code: ir::TrapCode) -> Self {
|
||||
let desc = TrapDescription {
|
||||
source_loc,
|
||||
pub fn wasm(trap_code: ir::TrapCode) -> Self {
|
||||
let backtrace = Backtrace::new_unresolved();
|
||||
Trap::Wasm {
|
||||
trap_code,
|
||||
};
|
||||
let backtrace = Backtrace::new();
|
||||
Trap::Wasm { desc, backtrace }
|
||||
backtrace,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a new OOM trap with the given source location and trap code.
|
||||
///
|
||||
/// Internally saves a backtrace when constructed.
|
||||
pub fn oom() -> Self {
|
||||
let backtrace = Backtrace::new();
|
||||
let backtrace = Backtrace::new_unresolved();
|
||||
Trap::OOM { backtrace }
|
||||
}
|
||||
}
|
||||
@@ -413,7 +406,7 @@ enum UnwindReason {
|
||||
Panic(Box<dyn Any + Send>),
|
||||
UserTrap(Box<dyn Error + Send + Sync>),
|
||||
LibTrap(Trap),
|
||||
Trap { backtrace: Backtrace, pc: usize },
|
||||
JitTrap { backtrace: Backtrace, pc: usize },
|
||||
}
|
||||
|
||||
impl CallThreadState {
|
||||
@@ -442,21 +435,9 @@ impl CallThreadState {
|
||||
Err(Trap::User(data))
|
||||
}
|
||||
UnwindReason::LibTrap(trap) => Err(trap),
|
||||
UnwindReason::Trap { backtrace, pc } => {
|
||||
UnwindReason::JitTrap { backtrace, pc } => {
|
||||
debug_assert_eq!(ret, 0);
|
||||
let instance = unsafe { InstanceHandle::from_vmctx(self.vmctx) };
|
||||
|
||||
Err(Trap::Wasm {
|
||||
desc: instance
|
||||
.instance()
|
||||
.trap_registration
|
||||
.get_trap(pc)
|
||||
.unwrap_or_else(|| TrapDescription {
|
||||
source_loc: ir::SourceLoc::default(),
|
||||
trap_code: ir::TrapCode::StackOverflow,
|
||||
}),
|
||||
backtrace,
|
||||
})
|
||||
Err(Trap::Jit { pc, backtrace })
|
||||
}
|
||||
UnwindReason::Panic(panic) => {
|
||||
debug_assert_eq!(ret, 0);
|
||||
@@ -546,7 +527,7 @@ impl CallThreadState {
|
||||
}
|
||||
let backtrace = Backtrace::new_unresolved();
|
||||
self.reset_guard_page.set(reset_guard_page);
|
||||
self.unwind.replace(UnwindReason::Trap {
|
||||
self.unwind.replace(UnwindReason::JitTrap {
|
||||
backtrace,
|
||||
pc: pc as usize,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user