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

View File

@ -66,7 +66,7 @@ extern "C" {
params: *const u64, params: *const u64,
results: *mut u64, results: *mut u64,
trap_out: *mut i32, trap_out: *mut i32,
error_out: *mut Option<InvokeError>, error_out: *mut Option<RuntimeError>,
invoke_env: Option<NonNull<c_void>>, invoke_env: Option<NonNull<c_void>>,
) -> bool; ) -> bool;
} }
@ -79,7 +79,7 @@ unsafe extern "C" fn invoke_trampoline(
func_ptr: NonNull<vm::Func>, func_ptr: NonNull<vm::Func>,
params: *const u64, params: *const u64,
results: *mut u64, results: *mut u64,
error_out: *mut Option<InvokeError>, error_out: *mut Option<RuntimeError>,
invoke_env: Option<NonNull<c_void>>, invoke_env: Option<NonNull<c_void>>,
) -> bool { ) -> bool {
let mut trap_out: i32 = -1; let mut trap_out: i32 = -1;
@ -105,11 +105,11 @@ unsafe extern "C" fn invoke_trampoline(
5 => ExceptionCode::MisalignedAtomicAccess, 5 => ExceptionCode::MisalignedAtomicAccess,
_ => return ret, _ => return ret,
}; };
Some(InvokeError::TrapCode { Some(RuntimeError::InvokeError(InvokeError::TrapCode {
code: exception_code, code: exception_code,
// TODO: // TODO:
srcloc: 0, srcloc: 0,
}) }))
}; };
} }
ret 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: /// TODO:
#[derive(Debug)] #[derive(Debug)]
pub enum InvokeError { 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 { impl PartialEq for RuntimeError {
fn eq(&self, _other: &RuntimeError) -> bool { fn eq(&self, _other: &RuntimeError) -> bool {
false false
} }
} }
//impl std::error::Error for InvokeError {}
impl std::error::Error for RuntimeError {} impl std::error::Error for RuntimeError {}
impl std::fmt::Display for RuntimeError { impl std::fmt::Display for RuntimeError {
@ -294,6 +298,10 @@ impl std::fmt::Display for RuntimeError {
// TODO: update invoke error formatting // TODO: update invoke error formatting
RuntimeError::InvokeError(_) => write!(f, "Error when calling invoke"), RuntimeError::InvokeError(_) => write!(f, "Error when calling invoke"),
RuntimeError::Metering(_) => write!(f, "unknown metering error type"), 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) => { RuntimeError::User(user_error) => {
write!(f, "User supplied error: ")?; write!(f, "User supplied error: ")?;
if let Some(s) = user_error.downcast_ref::<String>() { 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::codegen::{BreakpointInfo, BreakpointMap};
use crate::error::{InvokeError, RuntimeError}; use crate::error::{InvokeError, RuntimeError};
use crate::state::x64::{build_instance_image, read_stack, X64Register, GPR}; 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 crate::vm;
use libc::{mmap, mprotect, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; use libc::{mmap, mprotect, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
use nix::sys::signal::{ use nix::sys::signal::{
@ -61,7 +61,7 @@ type SetJmpBuffer = [i32; SETJMP_BUFFER_LEN];
struct UnwindInfo { struct UnwindInfo {
jmpbuf: SetJmpBuffer, // in jmpbuf: SetJmpBuffer, // in
breakpoints: Option<BreakpointMap>, breakpoints: Option<BreakpointMap>,
payload: Option<RuntimeError>, // out payload: Option<Box<RuntimeError>>, // out
} }
/// A store for boundary register preservation. /// A store for boundary register preservation.
@ -195,7 +195,7 @@ pub unsafe fn catch_unsafe_unwind<R, F: FnOnce() -> R>(
// error // error
let ret = (*unwind).as_mut().unwrap().payload.take().unwrap(); let ret = (*unwind).as_mut().unwrap().payload.take().unwrap();
*unwind = old; *unwind = old;
Err(ret) Err(*ret)
} else { } else {
let ret = f(); let ret = f();
// implicit control flow to the error case... // 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. /// 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 unwind = UNWIND.with(|x| x.get());
let inner = (*unwind) let inner = (*unwind)
.as_mut() .as_mut()
@ -279,7 +279,11 @@ extern "C" fn signal_trap_handler(
static ARCH: Architecture = Architecture::Aarch64; static ARCH: Architecture = Architecture::Aarch64;
let mut should_unwind = false; 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 { unsafe {
let fault = get_fault_info(siginfo as _, ucontext); let fault = get_fault_info(siginfo as _, ucontext);
@ -313,7 +317,7 @@ extern "C" fn signal_trap_handler(
if let Some(Ok(())) = out { if let Some(Ok(())) = out {
} else if let Some(Err(e)) = out { } else if let Some(Err(e)) = out {
should_unwind = true; 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 { if should_unwind {
begin_unsafe_unwind(unwind_result.unwrap().unwrap_err()); begin_unsafe_unwind(get_unwind_result(unwind_result));
} }
if early_return { if early_return {
return; return;
@ -357,7 +361,7 @@ extern "C" fn signal_trap_handler(
return false; return false;
} }
Some(Err(e)) => { Some(Err(e)) => {
unwind_result = Some(Err(e)); unwind_result = Some(Box::new(e));
return true; return true;
} }
None => {} None => {}
@ -389,7 +393,7 @@ extern "C" fn signal_trap_handler(
if is_suspend_signal { if is_suspend_signal {
// If this is a suspend signal, we parse the runtime state and return the resulting image. // If this is a suspend signal, we parse the runtime state and return the resulting image.
let image = build_instance_image(ctx, es_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 { } else {
// Otherwise, this is a real exception and we just throw it to the caller. // Otherwise, this is a real exception and we just throw it to the caller.
if !es_image.frames.is_empty() { if !es_image.frames.is_empty() {
@ -417,7 +421,8 @@ extern "C" fn signal_trap_handler(
None None
}); });
if let Some(code) = exc_code { if let Some(code) = exc_code {
unwind_result = Some(Err(RuntimeError::InvokeError(InvokeError::TrapCode { unwind_result =
Some(Box::new(RuntimeError::InvokeError(InvokeError::TrapCode {
code, code,
// TODO: // TODO:
srcloc: 0, srcloc: 0,
@ -429,7 +434,7 @@ extern "C" fn signal_trap_handler(
}); });
if should_unwind { 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>, func: NonNull<vm::Func>,
args: *const u64, args: *const u64,
rets: *mut u64, rets: *mut u64,
error_out: *mut Option<InvokeError>, error_out: *mut Option<RuntimeError>,
extra: Option<NonNull<c_void>>, extra: Option<NonNull<c_void>>,
) -> bool; ) -> bool;

View File

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

View File

@ -268,7 +268,18 @@ wasmer_backends! {
let result = foo.call(); 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 { if let Err(RuntimeError::User(e)) = result {
dbg!("WAT");
let exit_code = e.downcast::<ExitCode>().unwrap(); let exit_code = e.downcast::<ExitCode>().unwrap();
assert_eq!(exit_code.code, 42); assert_eq!(exit_code.code, 42);
} else { } else {