Define runtime error values explicitly in Singlepass

This commit is contained in:
Mark McCaskey
2020-04-24 13:21:45 -07:00
parent bfb6814f23
commit b9ec8f9845
12 changed files with 129 additions and 102 deletions

View File

@ -6,13 +6,12 @@ use crate::{
backend::RunnableModule,
backend::{CacheGen, Compiler, CompilerConfig, Features, Token},
cache::{Artifact, Error as CacheError},
error::{CompileError, CompileResult},
error::{CompileError, CompileResult, RuntimeError},
module::{ModuleInfo, ModuleInner},
structures::Map,
types::{FuncIndex, FuncSig, SigIndex},
};
use smallvec::SmallVec;
use std::any::Any;
use std::collections::HashMap;
use std::fmt;
use std::fmt::Debug;
@ -23,7 +22,7 @@ use wasmparser::{Operator, Type as WpType};
/// A type that defines a function pointer, which is called when breakpoints occur.
pub type BreakpointHandler =
Box<dyn Fn(BreakpointInfo) -> Result<(), Box<dyn Any + Send>> + Send + Sync + 'static>;
Box<dyn Fn(BreakpointInfo) -> Result<(), RuntimeError> + Send + Sync + 'static>;
/// Maps instruction pointers to their breakpoint handlers.
pub type BreakpointMap = Arc<HashMap<usize, BreakpointHandler>>;

View File

@ -1,6 +1,6 @@
//! The error module contains the data structures and helper functions used to implement errors that
//! are produced and returned from the wasmer runtime core.
//use crate::backend::ExceptionCode;
use crate::backend::ExceptionCode;
use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type};
use core::borrow::Borrow;
use std::any::Any;
@ -222,31 +222,63 @@ impl std::error::Error for RuntimeError {}
/// extremely rare and impossible to handle.
#[derive(Debug)]
pub enum RuntimeError {
/// When an invoke returns an error (this is where exception codes come from?)
/// When an invoke returns an error
InvokeError(InvokeError),
/// A metering triggered error value.
///
/// An error of this type indicates that it was returned by the metering system.
Metering(Box<dyn Any + Send>),
/// A user triggered error value.
///
/// An error returned from a host function.
User(Box<dyn Any + Send>)
User(Box<dyn Any + Send>),
}
/// TODO:
#[derive(Debug)]
pub enum InvokeError {
/// not yet handled error cases, ideally we should be able to handle them all
Misc(Box<dyn Any + Send>),
/// Indicates an exceptional circumstance such as a bug that should be reported or
/// a hardware failure.
FailedWithNoError,
/// TODO:
UnknownTrap {
/// TODO:
address: usize,
/// TODO:
signal: &'static str,
},
/// TODO:
TrapCode {
/// TODO:
code: ExceptionCode,
/// TODO:
srcloc: u32,
},
/// TODO:
UnknownTrapCode {
/// TODO:
trap_code: String,
/// TODO:
srcloc: u32,
},
/// extra TODO: investigate if this can be a `Box<InvokeError>` instead (looks like probably no)
/// TODO:
EarlyTrap(Box<RuntimeError>),
/// Indicates an error that ocurred related to breakpoints. (currently Singlepass only)
Breakpoint(Box<RuntimeError>),
}
//impl std::error::Error for InvokeError {}
impl std::error::Error for RuntimeError {}
impl std::fmt::Display for RuntimeError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
// TODO: ideally improve the error type of invoke
// TODO: update invoke error formatting
RuntimeError::InvokeError(_) => write!(f, "Error when calling invoke"),
RuntimeError::Metering(_) => write!(f, "unknown metering error type"),
RuntimeError::User(user_error) => {
write!(f, "User supplied error: ")?;
if let Some(s) = user_error.downcast_ref::<String>() {
@ -256,9 +288,9 @@ impl std::fmt::Display for RuntimeError {
} else if let Some(n) = user_error.downcast_ref::<i32>() {
write!(f, "{}", n)
} else {
write!(f, "unknown error type")
write!(f, "unknown user error type")
}
},
}
}
}
}
@ -270,8 +302,6 @@ impl From<InternalError> for RuntimeError {
}
}
*/
/// This error type is produced by resolving a wasm function
/// given its name.

View File

@ -29,14 +29,14 @@ pub mod raw {
use crate::codegen::{BreakpointInfo, BreakpointMap};
use crate::state::x64::{build_instance_image, read_stack, X64Register, GPR};
use crate::state::{CodeVersion, ExecutionStateImage};
use crate::state::{CodeVersion, ExecutionStateImage, InstanceImage};
use crate::error::{RuntimeError, InvokeError};
use crate::vm;
use libc::{mmap, mprotect, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
use nix::sys::signal::{
sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGINT,
SIGSEGV, SIGTRAP,
};
use std::any::Any;
use std::cell::{Cell, RefCell, UnsafeCell};
use std::ffi::c_void;
use std::process;
@ -61,7 +61,7 @@ type SetJmpBuffer = [i32; SETJMP_BUFFER_LEN];
struct UnwindInfo {
jmpbuf: SetJmpBuffer, // in
breakpoints: Option<BreakpointMap>,
payload: Option<Box<dyn Any + Send>>, // out
payload: Option<RuntimeError>, // out
}
/// A store for boundary register preservation.
@ -182,7 +182,7 @@ pub unsafe fn clear_wasm_interrupt() {
pub unsafe fn catch_unsafe_unwind<R, F: FnOnce() -> R>(
f: F,
breakpoints: Option<BreakpointMap>,
) -> Result<R, Box<dyn Any + Send>> {
) -> Result<R, RuntimeError> {
let unwind = UNWIND.with(|x| x.get());
let old = (*unwind).take();
*unwind = Some(UnwindInfo {
@ -205,7 +205,7 @@ pub unsafe fn catch_unsafe_unwind<R, F: FnOnce() -> R>(
}
/// Begins an unsafe unwind.
pub unsafe fn begin_unsafe_unwind(e: Box<dyn Any + Send>) -> ! {
pub unsafe fn begin_unsafe_unwind(e: RuntimeError) -> ! {
let unwind = UNWIND.with(|x| x.get());
let inner = (*unwind)
.as_mut()
@ -279,7 +279,7 @@ extern "C" fn signal_trap_handler(
static ARCH: Architecture = Architecture::Aarch64;
let mut should_unwind = false;
let mut unwind_result: Box<dyn Any + Send> = Box::new(());
let mut unwind_result: Option<Result<InstanceImage, RuntimeError>> = None;
unsafe {
let fault = get_fault_info(siginfo as _, ucontext);
@ -302,7 +302,7 @@ extern "C" fn signal_trap_handler(
) {
match ib.ty {
InlineBreakpointType::Middleware => {
let out: Option<Result<(), Box<dyn Any + Send>>> =
let out: Option<Result<(), RuntimeError>> =
with_breakpoint_map(|bkpt_map| {
bkpt_map.and_then(|x| x.get(&ip)).map(|x| {
x(BreakpointInfo {
@ -313,7 +313,7 @@ extern "C" fn signal_trap_handler(
if let Some(Ok(())) = out {
} else if let Some(Err(e)) = out {
should_unwind = true;
unwind_result = e;
unwind_result = Some(Err(e));
}
}
}
@ -328,7 +328,7 @@ extern "C" fn signal_trap_handler(
})
});
if should_unwind {
begin_unsafe_unwind(unwind_result);
begin_unsafe_unwind(unwind_result.unwrap().unwrap_err());
}
if early_return {
return;
@ -342,9 +342,9 @@ extern "C" fn signal_trap_handler(
match Signal::from_c_int(signum) {
Ok(SIGTRAP) => {
// breakpoint
let out: Option<Result<(), Box<dyn Any + Send>>> =
with_breakpoint_map(|bkpt_map| {
bkpt_map.and_then(|x| x.get(&(fault.ip.get()))).map(|x| {
let out: Option<Result<(), RuntimeError>> =
with_breakpoint_map(|bkpt_map| -> Option<Result<(), RuntimeError>> {
bkpt_map.and_then(|x| x.get(&(fault.ip.get()))).map(|x| -> Result<(), RuntimeError> {
x(BreakpointInfo {
fault: Some(&fault),
})
@ -355,7 +355,7 @@ extern "C" fn signal_trap_handler(
return false;
}
Some(Err(e)) => {
unwind_result = e;
unwind_result = Some(Err(e));
return true;
}
None => {}
@ -387,7 +387,7 @@ extern "C" fn signal_trap_handler(
if is_suspend_signal {
// If this is a suspend signal, we parse the runtime state and return the resulting image.
let image = build_instance_image(ctx, es_image);
unwind_result = Box::new(image);
unwind_result = Some(Ok(image));
} else {
// Otherwise, this is a real exception and we just throw it to the caller.
if !es_image.frames.is_empty() {
@ -415,7 +415,11 @@ extern "C" fn signal_trap_handler(
None
});
if let Some(code) = exc_code {
unwind_result = Box::new(code);
unwind_result = Some(Err(RuntimeError::InvokeError(InvokeError::TrapCode {
code,
// TODO:
srcloc: 0,
})));
}
}
@ -423,7 +427,7 @@ extern "C" fn signal_trap_handler(
});
if should_unwind {
begin_unsafe_unwind(unwind_result);
begin_unsafe_unwind(unwind_result.unwrap().unwrap_err());
}
}
}

View File

@ -5,7 +5,7 @@
use crate::{
backend::RunnableModule,
backing::{ImportBacking, LocalBacking},
error::{CallResult, ResolveError, ResolveResult, Result, RuntimeError, InvokeError},
error::{CallResult, InvokeError, ResolveError, ResolveResult, Result, RuntimeError},
export::{Context, Export, ExportIter, Exportable, FuncPointer},
global::Global,
import::{ImportObject, LikeNamespace},
@ -587,15 +587,17 @@ pub(crate) fn call_func_with_index_inner(
let run_wasm = |result_space: *mut u64| -> CallResult<()> {
let mut error_out = None;
let success = unsafe { invoke(
trampoline,
ctx_ptr,
func_ptr,
raw_args.as_ptr(),
result_space,
&mut error_out,
invoke_env,
)};
let success = unsafe {
invoke(
trampoline,
ctx_ptr,
func_ptr,
raw_args.as_ptr(),
result_space,
&mut error_out,
invoke_env,
)
};
if success {
Ok(())

View File

@ -705,7 +705,7 @@ pub mod x64 {
use crate::structures::TypedIndex;
use crate::types::LocalGlobalIndex;
use crate::vm::Ctx;
use std::any::Any;
use crate::error::RuntimeError;
#[allow(clippy::cast_ptr_alignment)]
unsafe fn compute_vmctx_deref(vmctx: *const Ctx, seq: &[usize]) -> u64 {
@ -738,7 +738,7 @@ pub mod x64 {
image: InstanceImage,
vmctx: &mut Ctx,
breakpoints: Option<BreakpointMap>,
) -> Result<u64, Box<dyn Any + Send>> {
) -> Result<u64, RuntimeError> {
let mut stack: Vec<u64> = vec![0; 1048576 * 8 / 8]; // 8MB stack
let mut stack_offset: usize = stack.len();

View File

@ -1,7 +1,7 @@
//! The typed func module implements a way of representing a wasm function
//! with the correct types from rust. Function calls using a typed func have a low overhead.
use crate::{
error::{RuntimeError, InvokeError},
error::{InvokeError, RuntimeError},
export::{Context, Export, FuncPointer},
import::IsExport,
types::{FuncSig, NativeWasmType, Type, WasmExternType},
@ -340,7 +340,9 @@ impl<'a> DynamicFunc<'a> {
Err(e) => {
// At this point, there is an error that needs to be trapped.
drop(args); // Release the Vec which will leak otherwise.
(&*vmctx.module).runnable_module.do_early_trap(RuntimeError::User(e))
(&*vmctx.module)
.runnable_module
.do_early_trap(RuntimeError::User(e))
}
}
}