rewrite Store::{entering,exiting}_native_code_hook into Store::call_hook (#3313)

which provides additional detail on what state transition is being made
This commit is contained in:
Pat Hickey
2021-09-09 07:20:45 -07:00
committed by GitHub
parent 8ebaaf928d
commit bd19f43f84
6 changed files with 639 additions and 347 deletions

View File

@@ -1,7 +1,7 @@
use crate::store::{StoreData, StoreOpaque, Stored};
use crate::{
AsContext, AsContextMut, Engine, Extern, FuncType, Instance, InterruptHandle, StoreContext,
StoreContextMut, Trap, Val, ValType,
AsContext, AsContextMut, CallHook, Engine, Extern, FuncType, Instance, InterruptHandle,
StoreContext, StoreContextMut, Trap, Val, ValType,
};
use anyhow::{bail, Context as _, Result};
use std::cmp::max;
@@ -845,7 +845,7 @@ impl Func {
values_vec: *mut u128,
func: &dyn Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<(), Trap>,
) -> Result<(), Trap> {
caller.store.0.entering_native_hook()?;
caller.store.0.call_hook(CallHook::CallingHost)?;
// Translate the raw JIT arguments in `values_vec` into a `Val` which
// we'll be passing as a slice. The storage for our slice-of-`Val` we'll
@@ -895,7 +895,7 @@ impl Func {
// hostcall to reuse our own storage.
val_vec.truncate(0);
caller.store.0.save_hostcall_val_storage(val_vec);
caller.store.0.exiting_native_hook()?;
caller.store.0.call_hook(CallHook::ReturningFromHost)?;
Ok(())
}
@@ -1044,7 +1044,7 @@ pub(crate) fn invoke_wasm_and_catch_traps<T>(
unsafe {
let exit = enter_wasm(store)?;
if let Err(trap) = store.0.exiting_native_hook() {
if let Err(trap) = store.0.call_hook(CallHook::CallingWasm) {
exit_wasm(store, exit);
return Err(trap);
}
@@ -1055,7 +1055,7 @@ pub(crate) fn invoke_wasm_and_catch_traps<T>(
closure,
);
exit_wasm(store, exit);
store.0.entering_native_hook()?;
store.0.call_hook(CallHook::ReturningFromWasm)?;
result.map_err(Trap::from_runtime_box)
}
}
@@ -1741,7 +1741,7 @@ macro_rules! impl_into_func {
let ret = {
panic::catch_unwind(AssertUnwindSafe(|| {
if let Err(trap) = caller.store.0.entering_native_hook() {
if let Err(trap) = caller.store.0.call_hook(CallHook::CallingHost) {
return R::fallible_from_trap(trap);
}
$(let $args = $args::from_abi($args, caller.store.0);)*
@@ -1749,7 +1749,7 @@ macro_rules! impl_into_func {
caller.sub_caller(),
$( $args, )*
);
if let Err(trap) = caller.store.0.exiting_native_hook() {
if let Err(trap) = caller.store.0.call_hook(CallHook::ReturningFromHost) {
return R::fallible_from_trap(trap);
}
r.into_fallible()

View File

@@ -401,7 +401,7 @@ pub use crate::memory::*;
pub use crate::module::{FrameInfo, FrameSymbol, Module};
pub use crate::r#ref::ExternRef;
pub use crate::store::{
AsContext, AsContextMut, InterruptHandle, Store, StoreContext, StoreContextMut,
AsContext, AsContextMut, CallHook, InterruptHandle, Store, StoreContext, StoreContextMut,
};
pub use crate::trap::*;
pub use crate::types::*;

View File

@@ -157,6 +157,37 @@ pub struct Store<T> {
inner: ManuallyDrop<Box<StoreInner<T>>>,
}
#[derive(Copy, Clone, Debug)]
/// Passed to the argument of [`Store::call_hook`] to indicate a state transition in
/// the WebAssembly VM.
pub enum CallHook {
/// Indicates the VM is calling a WebAssembly function, from the host.
CallingWasm,
/// Indicates the VM is returning from a WebAssembly function, to the host.
ReturningFromWasm,
/// Indicates the VM is calling a host function, from WebAssembly.
CallingHost,
/// Indicates the VM is returning from a host function, to WebAssembly.
ReturningFromHost,
}
impl CallHook {
/// Indicates the VM is entering host code (exiting WebAssembly code)
pub fn entering_host(&self) -> bool {
match self {
CallHook::ReturningFromWasm | CallHook::CallingHost => true,
_ => false,
}
}
/// Indicates the VM is exiting host code (entering WebAssembly code)
pub fn exiting_host(&self) -> bool {
match self {
CallHook::ReturningFromHost | CallHook::CallingWasm => true,
_ => false,
}
}
}
/// Internal contents of a `Store<T>` that live on the heap.
///
/// The members of this struct are those that need to be generic over `T`, the
@@ -167,8 +198,7 @@ pub struct StoreInner<T> {
inner: StoreOpaque,
limiter: Option<Box<dyn FnMut(&mut T) -> &mut (dyn crate::ResourceLimiter) + Send + Sync>>,
entering_native_hook: Option<Box<dyn FnMut(&mut T) -> Result<(), crate::Trap> + Send + Sync>>,
exiting_native_hook: Option<Box<dyn FnMut(&mut T) -> Result<(), crate::Trap> + Send + Sync>>,
call_hook: Option<Box<dyn FnMut(&mut T, CallHook) -> Result<(), crate::Trap> + Send + Sync>>,
// for comments about `ManuallyDrop`, see `Store::into_data`
data: ManuallyDrop<T>,
}
@@ -340,8 +370,7 @@ impl<T> Store<T> {
hostcall_val_storage: Vec::new(),
},
limiter: None,
entering_native_hook: None,
exiting_native_hook: None,
call_hook: None,
data: ManuallyDrop::new(data),
});
@@ -433,56 +462,25 @@ impl<T> Store<T> {
inner.limiter = Some(Box::new(limiter));
}
/// Configure a function that runs each time the host resumes execution from
/// WebAssembly.
/// Configure a function that runs on calls and returns between WebAssembly
/// and host code.
///
/// This hook is called in two circumstances:
///
/// * When WebAssembly calls a function defined by the host, this hook is
/// called before other host code runs.
/// * When WebAssembly returns back to the host after being called, this
/// hook is called.
///
/// This method can be used with [`Store::exiting_native_code_hook`] to track
/// execution time of WebAssembly, for example, by starting/stopping timers
/// in the enter/exit hooks.
/// The function is passed a [`CallHook`] argument, which indicates which
/// state transition the VM is making.
///
/// This function may return a [`Trap`]. If a trap is returned when an
/// import was called, it is immediately raised as-if the host import had
/// returned the trap. If a trap is returned after wasm returns to the host
/// then the wasm function's result is ignored and this trap is returned
/// instead.
pub fn entering_native_code_hook(
///
/// After this function returns a trap, it may be called for subsequent returns
/// to host or wasm code as the trap propogates to the root call.
pub fn call_hook(
&mut self,
hook: impl FnMut(&mut T) -> Result<(), Trap> + Send + Sync + 'static,
hook: impl FnMut(&mut T, CallHook) -> Result<(), Trap> + Send + Sync + 'static,
) {
self.inner.entering_native_hook = Some(Box::new(hook));
}
/// Configure a function that runs just before WebAssembly code starts
/// executing.
///
/// The closure provided is called in two circumstances:
///
/// * When the host calls a WebAssembly function, the hook is called just
/// before WebAssembly starts executing.
/// * When a host function returns back to WebAssembly this hook is called
/// just before the return.
///
/// This method can be used with [`Store::entering_native_code_hook`] to track
/// execution time of WebAssembly, for example, by starting/stopping timers
/// in the enter/exit hooks.
///
/// This function may return a [`Trap`]. If a trap is returned when an
/// imported host function is returning, then the imported host function's
/// result is ignored and the trap is raised. If a trap is returned when
/// the host is about to start executing WebAssembly, then no WebAssembly
/// code is run and the trap is returned instead.
pub fn exiting_native_code_hook(
&mut self,
hook: impl FnMut(&mut T) -> Result<(), Trap> + Send + Sync + 'static,
) {
self.inner.exiting_native_hook = Some(Box::new(hook));
self.inner.call_hook = Some(Box::new(hook));
}
/// Returns the [`Engine`] that this store is associated with.
@@ -783,17 +781,9 @@ impl<T> StoreInner<T> {
Some(accessor(&mut self.data))
}
pub fn entering_native_hook(&mut self) -> Result<(), Trap> {
if let Some(hook) = &mut self.entering_native_hook {
hook(&mut self.data)
} else {
Ok(())
}
}
pub fn exiting_native_hook(&mut self) -> Result<(), Trap> {
if let Some(hook) = &mut self.exiting_native_hook {
hook(&mut self.data)
pub fn call_hook(&mut self, s: CallHook) -> Result<(), Trap> {
if let Some(hook) = &mut self.call_hook {
hook(&mut self.data, s)
} else {
Ok(())
}