Skip to content

Commit

Permalink
Merge pull request #2305 from wasmerio/improve-traps
Browse files Browse the repository at this point in the history
Improve traps
  • Loading branch information
syrusakbary authored May 12, 2021
2 parents 7d491b4 + 29fd430 commit 425846a
Show file tree
Hide file tree
Showing 20 changed files with 429 additions and 295 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ test-jit = [
# that raise signals because that interferes with tarpaulin.
coverage = []

[profile.dev]
split-debuginfo = "unpacked"

[[bench]]
name = "static_and_dynamic_functions"
harness = false
Expand Down
1 change: 1 addition & 0 deletions lib/api/src/externals/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ impl Function {
// Call the trampoline.
if let Err(error) = unsafe {
wasmer_call_trampoline(
&self.store,
self.exported.vm_function.vmctx,
trampoline,
self.exported.vm_function.address,
Expand Down
3 changes: 2 additions & 1 deletion lib/api/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,8 @@ impl Module {
// of this steps traps, we still need to keep the instance alive
// as some of the Instance elements may have placed in other
// instance tables.
self.artifact.finish_instantiation(&instance_handle)?;
self.artifact
.finish_instantiation(&self.store, &instance_handle)?;

Ok(instance_handle)
}
Expand Down
5 changes: 3 additions & 2 deletions lib/api/src/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ macro_rules! impl_native_traits {
};
unsafe {
wasmer_vm::wasmer_call_trampoline(
&self.store,
self.vmctx(),
trampoline,
self.address(),
Expand All @@ -145,8 +146,8 @@ macro_rules! impl_native_traits {
// TODO: we can probably remove this copy by doing some clever `transmute`s.
// we know it's not overlapping because `using_rets_array` is false
std::ptr::copy_nonoverlapping(src_pointer,
rets_list,
num_rets);
rets_list,
num_rets);
}
}
Ok(Rets::from_array(rets_list_array))
Expand Down
49 changes: 39 additions & 10 deletions lib/api/src/store.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use crate::tunables::BaseTunables;
use loupe::MemoryUsage;
use std::any::Any;
use std::fmt;
use std::sync::Arc;
use std::sync::{Arc, RwLock};
#[cfg(all(feature = "compiler", feature = "engine"))]
use wasmer_compiler::CompilerConfig;
use wasmer_engine::{Engine, Tunables};
use wasmer_engine::{is_wasm_pc, Engine, Tunables};
use wasmer_vm::{init_traps, TrapHandler, TrapHandlerFn};

/// The store represents all global state that can be manipulated by
/// WebAssembly programs. It consists of the runtime representation
Expand All @@ -20,6 +22,8 @@ use wasmer_engine::{Engine, Tunables};
pub struct Store {
engine: Arc<dyn Engine + Send + Sync>,
tunables: Arc<dyn Tunables + Send + Sync>,
#[loupe(skip)]
trap_handler: Arc<RwLock<Option<Box<TrapHandlerFn>>>>,
}

impl Store {
Expand All @@ -28,20 +32,28 @@ impl Store {
where
E: Engine + ?Sized,
{
Self {
engine: engine.cloned(),
tunables: Arc::new(BaseTunables::for_target(engine.target())),
}
Self::new_with_tunables(engine, BaseTunables::for_target(engine.target()))
}

/// Set the trap handler in this store.
pub fn set_trap_handler(&self, handler: Option<Box<TrapHandlerFn>>) {
let mut m = self.trap_handler.write().unwrap();
*m = handler;
}

/// Creates a new `Store` with a specific [`Engine`] and [`Tunables`].
pub fn new_with_tunables<E>(engine: &E, tunables: impl Tunables + Send + Sync + 'static) -> Self
where
E: Engine + ?Sized,
{
// Make sure the signal handlers are installed.
// This is required for handling traps.
init_traps(is_wasm_pc);

Self {
engine: engine.cloned(),
tunables: Arc::new(tunables),
trap_handler: Arc::new(RwLock::new(None)),
}
}

Expand Down Expand Up @@ -69,6 +81,26 @@ impl PartialEq for Store {
}
}

unsafe impl TrapHandler for Store {
#[inline]
fn as_any(&self) -> &dyn Any {
self
}

fn custom_trap_handler(&self, call: &dyn Fn(&TrapHandlerFn) -> bool) -> bool {
if let Some(handler) = *&self.trap_handler.read().unwrap().as_ref() {
call(handler)
} else {
false
}
}
}

// This is required to be able to set the trap_handler in the
// Store.
unsafe impl Send for Store {}
unsafe impl Sync for Store {}

// We only implement default if we have assigned a default compiler and engine
#[cfg(all(feature = "default-compiler", feature = "default-engine"))]
impl Default for Store {
Expand Down Expand Up @@ -109,10 +141,7 @@ impl Default for Store {
let config = get_config();
let engine = get_engine(config);
let tunables = BaseTunables::for_target(engine.target());
Store {
engine: Arc::new(engine),
tunables: Arc::new(tunables),
}
Self::new_with_tunables(&engine, tunables)
}
}

Expand Down
3 changes: 2 additions & 1 deletion lib/compiler-cranelift/src/sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,9 @@ fn translate_ir_trapcode(trap: ir::TrapCode) -> TrapCode {
ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero,
ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger,
ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached,
ir::TrapCode::Interrupt => TrapCode::Interrupt,
ir::TrapCode::Interrupt => unimplemented!("Interrupts not supported"),
ir::TrapCode::User(_user_code) => unimplemented!("User trap code not supported"),
// ir::TrapCode::Interrupt => TrapCode::Interrupt,
// ir::TrapCode::User(user_code) => TrapCode::User(user_code),
}
}
5 changes: 3 additions & 2 deletions lib/engine/src/artifact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use wasmer_types::{
};
use wasmer_vm::{
FuncDataRegistry, FunctionBodyPtr, InstanceAllocator, InstanceHandle, MemoryStyle, ModuleInfo,
TableStyle, VMSharedSignatureIndex, VMTrampoline,
TableStyle, TrapHandler, VMSharedSignatureIndex, VMTrampoline,
};

/// An `Artifact` is the product that the `Engine`
Expand Down Expand Up @@ -161,6 +161,7 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage {
/// See [`InstanceHandle::finish_instantiation`].
unsafe fn finish_instantiation(
&self,
trap_handler: &dyn TrapHandler,
handle: &InstanceHandle,
) -> Result<(), InstantiationError> {
let data_initializers = self
Expand All @@ -172,7 +173,7 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage {
})
.collect::<Vec<_>>();
handle
.finish_instantiation(&data_initializers)
.finish_instantiation(trap_handler, &data_initializers)
.map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))
}
}
Expand Down
9 changes: 8 additions & 1 deletion lib/engine/src/trap/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub struct RuntimeError {
#[derive(Debug)]
enum RuntimeErrorSource {
Generic(String),
OOM,
User(Box<dyn Error + Send + Sync>),
Trap(TrapCode),
}
Expand All @@ -25,6 +26,7 @@ impl fmt::Display for RuntimeErrorSource {
match self {
Self::Generic(s) => write!(f, "{}", s),
Self::User(s) => write!(f, "{}", s),
Self::OOM => write!(f, "Wasmer VM out of memory"),
Self::Trap(s) => write!(f, "{}", s.message()),
}
}
Expand Down Expand Up @@ -66,6 +68,7 @@ impl RuntimeError {
pub fn from_trap(trap: Trap) -> Self {
let info = FRAME_INFO.read().unwrap();
match trap {
// A user error
Trap::User(error) => {
match error.downcast::<Self>() {
// The error is already a RuntimeError, we return it directly
Expand All @@ -78,6 +81,10 @@ impl RuntimeError {
),
}
}
// A trap caused by the VM being Out of Memory
Trap::OOM { backtrace } => {
Self::new_with_trace(&info, None, RuntimeErrorSource::OOM, backtrace)
}
// A trap caused by an error on the generated machine code for a Wasm function
Trap::Wasm {
pc,
Expand All @@ -92,7 +99,7 @@ impl RuntimeError {
Self::new_with_trace(&info, Some(pc), RuntimeErrorSource::Trap(code), backtrace)
}
// A trap triggered manually from the Wasmer runtime
Trap::Runtime {
Trap::Lib {
trap_code,
backtrace,
} => Self::new_with_trace(&info, None, RuntimeErrorSource::Trap(trap_code), backtrace),
Expand Down
8 changes: 8 additions & 0 deletions lib/engine/src/trap/frame_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ pub struct GlobalFrameInfo {
ranges: BTreeMap<usize, ModuleInfoFrameInfo>,
}

/// Returns whether the `pc`, according to globally registered information,
/// is a wasm trap or not.
pub fn is_wasm_pc(pc: usize) -> bool {
let frame_info = FRAME_INFO.read().unwrap();
let module_info = frame_info.module_info(pc);
module_info.is_some()
}

/// An RAII structure used to unregister a module's frame information when the
/// module is destroyed.
#[derive(MemoryUsage)]
Expand Down
4 changes: 2 additions & 2 deletions lib/engine/src/trap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ mod error;
mod frame_info;
pub use error::RuntimeError;
pub use frame_info::{
register as register_frame_info, FrameInfo, FunctionExtent, GlobalFrameInfoRegistration,
FRAME_INFO,
is_wasm_pc, register as register_frame_info, FrameInfo, FunctionExtent,
GlobalFrameInfoRegistration, FRAME_INFO,
};
16 changes: 13 additions & 3 deletions lib/vm/build.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
//! Runtime build script compiles C code using setjmp for trap handling.

use std::env;

fn main() {
println!("cargo:rerun-if-changed=src/trap/helpers.c");
println!("cargo:rerun-if-changed=src/trap/handlers.c");

cc::Build::new()
.warnings(true)
.file("src/trap/helpers.c")
.compile("helpers");
.define(
&format!(
"CFG_TARGET_OS_{}",
env::var("CARGO_CFG_TARGET_OS").unwrap().to_uppercase()
),
None,
)
.file("src/trap/handlers.c")
.compile("handlers");
}
Loading

0 comments on commit 425846a

Please sign in to comment.