Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve traps #2305

Merged
merged 15 commits into from
May 12, 2021
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();
syrusakbary marked this conversation as resolved.
Show resolved Hide resolved
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