Fix new RuntimeError implementation for the Singlepass backend

This commit is contained in:
Mark McCaskey 2020-04-26 12:05:12 -07:00
parent 9723270f96
commit 89af5dc107
7 changed files with 73 additions and 49 deletions

View File

@ -7,7 +7,7 @@ use libc::c_void;
use std::{cell::Cell, ptr::NonNull, sync::Arc};
use wasmer_runtime_core::{
backend::RunnableModule,
error::{InvokeError, RuntimeError},
error::RuntimeError,
module::ModuleInfo,
typed_func::{Trampoline, Wasm},
types::{LocalFuncIndex, SigIndex},
@ -62,7 +62,7 @@ impl RunnableModule for Caller {
func: NonNull<vm::Func>,
args: *const u64,
rets: *mut u64,
error_out: *mut Option<InvokeError>,
error_out: *mut Option<RuntimeError>,
invoke_env: Option<NonNull<c_void>>,
) -> bool {
let handler_data = &*invoke_env.unwrap().cast().as_ptr();
@ -82,7 +82,7 @@ impl RunnableModule for Caller {
// probably makes the most sense to actually do a translation here to a
// a generic type defined in runtime-core
// TODO: figure out _this_ error return story
*error_out = Some(err);
*error_out = Some(err.into());
false
}
Ok(()) => true,

View File

@ -66,7 +66,7 @@ extern "C" {
params: *const u64,
results: *mut u64,
trap_out: *mut i32,
error_out: *mut Option<InvokeError>,
error_out: *mut Option<RuntimeError>,
invoke_env: Option<NonNull<c_void>>,
) -> bool;
}
@ -79,7 +79,7 @@ unsafe extern "C" fn invoke_trampoline(
func_ptr: NonNull<vm::Func>,
params: *const u64,
results: *mut u64,
error_out: *mut Option<InvokeError>,
error_out: *mut Option<RuntimeError>,
invoke_env: Option<NonNull<c_void>>,
) -> bool {
let mut trap_out: i32 = -1;
@ -105,11 +105,11 @@ unsafe extern "C" fn invoke_trampoline(
5 => ExceptionCode::MisalignedAtomicAccess,
_ => return ret,
};
Some(InvokeError::TrapCode {
Some(RuntimeError::InvokeError(InvokeError::TrapCode {
code: exception_code,
// TODO:
srcloc: 0,
})
}))
};
}
ret

View File

@ -213,27 +213,6 @@ impl std::error::Error for RuntimeError {}
*/
/// An `InternalError` is an error that happened inside of Wasmer and is a
/// catch-all for errors that would otherwise be returned as
/// `RuntimeError(Box::new(<string>))`.
///
/// This type provides greater visibility into the kinds of things that may fail
/// and improves the ability of users to handle them, though these errors may be
/// extremely rare and impossible to handle.
#[derive(Debug)]
pub enum RuntimeError {
/// 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>),
}
/// TODO:
#[derive(Debug)]
pub enum InvokeError {
@ -278,14 +257,39 @@ impl From<InvokeError> for RuntimeError {
}
}
//impl std::error::Error for InvokeError {}
/// An `InternalError` is an error that happened inside of Wasmer and is a
/// catch-all for errors that would otherwise be returned as
/// `RuntimeError(Box::new(<string>))`.
///
/// This type provides greater visibility into the kinds of things that may fail
/// and improves the ability of users to handle them, though these errors may be
/// extremely rare and impossible to handle.
#[derive(Debug)]
pub enum RuntimeError {
/// 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 frozen state of Wasm used to pause and resume execution. Not strictly an
/// "error", but this happens while executing and therefore is a `RuntimeError`
/// from the persective of the caller that expected the code to fully execute.
InstanceImage(Box<dyn Any + Send>),
/// A user triggered error value.
///
/// An error returned from a host function.
User(Box<dyn Any + Send>),
}
impl PartialEq for RuntimeError {
fn eq(&self, _other: &RuntimeError) -> bool {
false
}
}
//impl std::error::Error for InvokeError {}
impl std::error::Error for RuntimeError {}
impl std::fmt::Display for RuntimeError {
@ -294,6 +298,10 @@ impl std::fmt::Display for RuntimeError {
// TODO: update invoke error formatting
RuntimeError::InvokeError(_) => write!(f, "Error when calling invoke"),
RuntimeError::Metering(_) => write!(f, "unknown metering error type"),
RuntimeError::InstanceImage(_) => write!(
f,
"Execution interrupted by a suspend signal: instance image returned"
),
RuntimeError::User(user_error) => {
write!(f, "User supplied error: ")?;
if let Some(s) = user_error.downcast_ref::<String>() {

View File

@ -30,7 +30,7 @@ pub mod raw {
use crate::codegen::{BreakpointInfo, BreakpointMap};
use crate::error::{InvokeError, RuntimeError};
use crate::state::x64::{build_instance_image, read_stack, X64Register, GPR};
use crate::state::{CodeVersion, ExecutionStateImage, InstanceImage};
use crate::state::{CodeVersion, ExecutionStateImage};
use crate::vm;
use libc::{mmap, mprotect, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
use nix::sys::signal::{
@ -61,7 +61,7 @@ type SetJmpBuffer = [i32; SETJMP_BUFFER_LEN];
struct UnwindInfo {
jmpbuf: SetJmpBuffer, // in
breakpoints: Option<BreakpointMap>,
payload: Option<RuntimeError>, // out
payload: Option<Box<RuntimeError>>, // out
}
/// A store for boundary register preservation.
@ -195,7 +195,7 @@ pub unsafe fn catch_unsafe_unwind<R, F: FnOnce() -> R>(
// error
let ret = (*unwind).as_mut().unwrap().payload.take().unwrap();
*unwind = old;
Err(ret)
Err(*ret)
} else {
let ret = f();
// implicit control flow to the error case...
@ -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: RuntimeError) -> ! {
pub unsafe fn begin_unsafe_unwind(e: Box<RuntimeError>) -> ! {
let unwind = UNWIND.with(|x| x.get());
let inner = (*unwind)
.as_mut()
@ -279,7 +279,11 @@ extern "C" fn signal_trap_handler(
static ARCH: Architecture = Architecture::Aarch64;
let mut should_unwind = false;
let mut unwind_result: Option<Result<InstanceImage, RuntimeError>> = None;
let mut unwind_result: Option<Box<RuntimeError>> = None;
let get_unwind_result = |uw_result: Option<Box<RuntimeError>>| -> Box<RuntimeError> {
uw_result
.unwrap_or_else(|| Box::new(RuntimeError::InvokeError(InvokeError::FailedWithNoError)))
};
unsafe {
let fault = get_fault_info(siginfo as _, ucontext);
@ -313,7 +317,7 @@ extern "C" fn signal_trap_handler(
if let Some(Ok(())) = out {
} else if let Some(Err(e)) = out {
should_unwind = true;
unwind_result = Some(Err(e));
unwind_result = Some(Box::new(e));
}
}
}
@ -328,7 +332,7 @@ extern "C" fn signal_trap_handler(
})
});
if should_unwind {
begin_unsafe_unwind(unwind_result.unwrap().unwrap_err());
begin_unsafe_unwind(get_unwind_result(unwind_result));
}
if early_return {
return;
@ -357,7 +361,7 @@ extern "C" fn signal_trap_handler(
return false;
}
Some(Err(e)) => {
unwind_result = Some(Err(e));
unwind_result = Some(Box::new(e));
return true;
}
None => {}
@ -389,7 +393,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 = Some(Ok(image));
unwind_result = Some(Box::new(RuntimeError::InstanceImage(Box::new(image))));
} else {
// Otherwise, this is a real exception and we just throw it to the caller.
if !es_image.frames.is_empty() {
@ -417,11 +421,12 @@ extern "C" fn signal_trap_handler(
None
});
if let Some(code) = exc_code {
unwind_result = Some(Err(RuntimeError::InvokeError(InvokeError::TrapCode {
code,
// TODO:
srcloc: 0,
})));
unwind_result =
Some(Box::new(RuntimeError::InvokeError(InvokeError::TrapCode {
code,
// TODO:
srcloc: 0,
})));
}
}
@ -429,7 +434,7 @@ extern "C" fn signal_trap_handler(
});
if should_unwind {
begin_unsafe_unwind(unwind_result.unwrap().unwrap_err());
begin_unsafe_unwind(get_unwind_result(unwind_result));
}
}
}

View File

@ -37,7 +37,7 @@ pub type Invoke = unsafe extern "C" fn(
func: NonNull<vm::Func>,
args: *const u64,
rets: *mut u64,
error_out: *mut Option<InvokeError>,
error_out: *mut Option<RuntimeError>,
extra: Option<NonNull<c_void>>,
) -> bool;

View File

@ -507,7 +507,7 @@ impl RunnableModule for X64ExecutionContext {
func: NonNull<vm::Func>,
args: *const u64,
rets: *mut u64,
error_out: *mut Option<InvokeError>,
error_out: *mut Option<RuntimeError>,
num_params_plus_one: Option<NonNull<c_void>>,
) -> bool {
let rm: &Box<dyn RunnableModule> = &(&*(*ctx).module).runnable_module;
@ -655,7 +655,7 @@ impl RunnableModule for X64ExecutionContext {
true
}
Err(err) => {
*error_out = Some(InvokeError::Breakpoint(Box::new(err)));
*error_out = Some(InvokeError::Breakpoint(Box::new(err)).into());
false
}
};
@ -681,7 +681,7 @@ impl RunnableModule for X64ExecutionContext {
}
unsafe fn do_early_trap(&self, data: RuntimeError) -> ! {
fault::begin_unsafe_unwind(data);
fault::begin_unsafe_unwind(Box::new(data));
}
fn get_code(&self) -> Option<&[u8]> {

View File

@ -268,7 +268,18 @@ wasmer_backends! {
let result = foo.call();
dbg!(&result);
if let Err(e) = &result {
dbg!(&e);
eprintln!("{}", &e);
}
match &result {
Err(RuntimeError::User(e)) => { dbg!("USER", &e); } ,
Err(e) => {dbg!("GENERIC",&e); } ,
_ => { dbg!("OKaY!"); },
}
if let Err(RuntimeError::User(e)) = result {
dbg!("WAT");
let exit_code = e.downcast::<ExitCode>().unwrap();
assert_eq!(exit_code.code, 42);
} else {