Skip to content

Commit 875a4b1

Browse files
committed
Add support for concurrent call recording
1 parent 18da308 commit 875a4b1

File tree

7 files changed

+155
-32
lines changed

7 files changed

+155
-32
lines changed

crates/wasmtime/src/runtime/component/concurrent.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
use crate::component::func::{self, Func};
5454
use crate::component::{HasData, HasSelf, Instance, Resource, ResourceTable, ResourceTableError};
5555
use crate::fiber::{self, StoreFiber, StoreFiberYield};
56+
use crate::rr::{RRWasmFuncType, component_hooks};
5657
use crate::store::{Store, StoreId, StoreInner, StoreOpaque, StoreToken};
5758
use crate::vm::component::{CallContext, ComponentInstance, InstanceFlags, ResourceTables};
5859
use crate::vm::{AlwaysMut, SendSyncPtr, VMFuncRef, VMMemoryDefinition, VMStore};
@@ -1715,7 +1716,8 @@ impl Instance {
17151716
///
17161717
/// SAFETY: The raw pointer arguments must be valid references to guest
17171718
/// functions (with the appropriate signatures) when the closures queued by
1718-
/// this function are called.
1719+
/// this function are called. For RR, the rr_handle must be a valid `Func`
1720+
/// corresponding to `callee` if provided
17191721
unsafe fn queue_call<T: 'static>(
17201722
self,
17211723
mut store: StoreContextMut<T>,
@@ -1727,6 +1729,7 @@ impl Instance {
17271729
async_: bool,
17281730
callback: Option<SendSyncPtr<VMFuncRef>>,
17291731
post_return: Option<SendSyncPtr<VMFuncRef>>,
1732+
rr_handle: Option<Func>,
17301733
) -> Result<()> {
17311734
/// Return a closure which will call the specified function in the scope
17321735
/// of the specified task.
@@ -1741,14 +1744,16 @@ impl Instance {
17411744
/// nothing.
17421745
///
17431746
/// SAFETY: `callee` must be a valid `*mut VMFuncRef` at the time when
1744-
/// the returned closure is called.
1747+
/// the returned closure is called. For RR, the handle must be a valid `Func`
1748+
/// corresponding to `callee` if provided
17451749
unsafe fn make_call<T: 'static>(
17461750
store: StoreContextMut<T>,
17471751
guest_thread: QualifiedThreadId,
17481752
callee: SendSyncPtr<VMFuncRef>,
17491753
param_count: usize,
17501754
result_count: usize,
17511755
flags: Option<InstanceFlags>,
1756+
rr_handle: Option<Func>,
17521757
) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
17531758
+ Send
17541759
+ Sync
@@ -1766,6 +1771,14 @@ impl Instance {
17661771
let may_enter_after_call = task.call_post_return_automatically();
17671772
let lower = task.lower_params.take().unwrap();
17681773

1774+
if let Some(func) = rr_handle {
1775+
component_hooks::record_wasm_func_begin(
1776+
func.instance().id().instance(),
1777+
func.index(),
1778+
store.store_opaque_mut(),
1779+
)?;
1780+
}
1781+
17691782
lower(store, &mut storage[..param_count])?;
17701783

17711784
let mut store = token.as_context_mut(store);
@@ -1776,15 +1789,31 @@ impl Instance {
17761789
if let Some(mut flags) = flags {
17771790
flags.set_may_enter(false);
17781791
}
1779-
crate::Func::call_unchecked_raw(
1792+
1793+
let rr_type = if let Some(func) = rr_handle {
1794+
let type_idx = func.abi_info(store.0).2;
1795+
let types = func
1796+
.instance()
1797+
.id()
1798+
.get(store.0)
1799+
.component()
1800+
.types()
1801+
.clone();
1802+
RRWasmFuncType::Component { type_idx, types }
1803+
} else {
1804+
RRWasmFuncType::None
1805+
};
1806+
crate::Func::call_unchecked_raw_with_rr(
17801807
&mut store,
17811808
callee.as_non_null(),
17821809
NonNull::new(
17831810
&mut storage[..param_count.max(result_count)]
17841811
as *mut [MaybeUninit<ValRaw>] as _,
17851812
)
17861813
.unwrap(),
1814+
rr_type,
17871815
)?;
1816+
17881817
if let Some(mut flags) = flags {
17891818
flags.set_may_enter(may_enter_after_call);
17901819
}
@@ -1805,6 +1834,7 @@ impl Instance {
18051834
param_count,
18061835
result_count,
18071836
flags,
1837+
rr_handle,
18081838
)
18091839
};
18101840

@@ -2330,6 +2360,7 @@ impl Instance {
23302360
(flags & START_FLAG_ASYNC_CALLEE) != 0,
23312361
NonNull::new(callback).map(SendSyncPtr::new),
23322362
NonNull::new(post_return).map(SendSyncPtr::new),
2363+
None,
23332364
)?;
23342365
}
23352366

@@ -5047,6 +5078,7 @@ fn queue_call0<T: 'static>(
50475078
is_concurrent,
50485079
callback,
50495080
post_return.map(SendSyncPtr::new),
5081+
Some(handle),
50505082
)
50515083
}
50525084
}

crates/wasmtime/src/runtime/component/func.rs

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::component::storage::storage_as_slice;
44
use crate::component::types::ComponentFunc;
55
use crate::component::values::Val;
66
use crate::prelude::*;
7-
use crate::rr::component_hooks;
7+
use crate::rr::{RRWasmFuncType, component_hooks};
88
use crate::runtime::vm::component::{ComponentInstance, InstanceFlags, ResourceTables};
99
use crate::runtime::vm::{Export, VMFuncRef};
1010
use crate::store::StoreOpaque;
@@ -581,16 +581,12 @@ impl Func {
581581
LowerParams: Copy,
582582
LowerReturn: Copy,
583583
{
584-
#[cfg(feature = "rr-component")]
585-
{
586-
use crate::rr::component_events::WasmFuncBeginEvent;
584+
component_hooks::record_wasm_func_begin(
585+
self.instance.id().instance(),
586+
self.index,
587+
store.0,
588+
)?;
587589

588-
let instance = self.instance.id().instance();
589-
let func_idx = self.index;
590-
store
591-
.0
592-
.record_event(|| WasmFuncBeginEvent { instance, func_idx })?;
593-
}
594590
let export = self.lifted_core_func(store.0);
595591

596592
#[repr(C)]
@@ -635,12 +631,13 @@ impl Func {
635631
))
636632
.unwrap();
637633

638-
component_hooks::record_and_replay_validate_wasm_func(
639-
|store| crate::Func::call_unchecked_raw(store, export, params_and_returns),
640-
params_and_returns.as_ref(),
641-
self.abi_info(store.0).2,
642-
self.instance.id().get(store.0).component().types().clone(),
634+
let type_idx = self.abi_info(store.0).2;
635+
let types = self.instance.id().get(store.0).component().types().clone();
636+
crate::Func::call_unchecked_raw_with_rr(
643637
&mut store,
638+
export,
639+
params_and_returns,
640+
RRWasmFuncType::Component { type_idx, types },
644641
)?;
645642
}
646643

crates/wasmtime/src/runtime/func.rs

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::prelude::*;
2-
use crate::rr;
2+
use crate::rr::{self, RRWasmFuncType};
33
use crate::runtime::Uninhabited;
44
use crate::runtime::vm::{
55
self, InterpreterRef, SendSyncPtr, StoreBox, VMArrayCallHostFuncContext,
@@ -1043,17 +1043,69 @@ impl Func {
10431043
let func_ref = self.vm_func_ref(store.0);
10441044
let params_and_returns = NonNull::new(params_and_returns).unwrap_or(NonNull::from(&mut []));
10451045

1046-
rr::core_hooks::record_and_replay_validate_wasm_func(
1047-
|mut store| {
1048-
// SAFETY: the safety of this function call is the same as the contract
1049-
// of this function.
1050-
unsafe { Self::call_unchecked_raw(&mut store, func_ref, params_and_returns) }
1046+
unsafe {
1047+
let ty = &self.ty(&store);
1048+
let origin = self.origin.expand();
1049+
Self::call_unchecked_raw_with_rr(
1050+
&mut store,
1051+
func_ref,
1052+
params_and_returns,
1053+
RRWasmFuncType::Core { ty, origin },
1054+
)
1055+
}
1056+
}
1057+
1058+
/// Same as [`Func::call_unchecked_raw`] but enables recording and replaying
1059+
/// hooks for func entry/exit based on whether the intended call was from a component or
1060+
/// core wasm module.
1061+
///
1062+
/// This method is essentially a wrapper over [`Func::call_unchecked_raw`] and operates exactly like it
1063+
/// when recording/replay is not enabled, so use this for all calls that will potentially need
1064+
/// to be recorded/replayed.
1065+
///
1066+
/// `RRWasmFuncType::None` can be used to disable recording/replaying and passthrough to `call_unchecked_raw`.
1067+
/// Eventually we can replace all occurences of `call_unchecked_raw` with this method with `RRWasmFuncType::None``
1068+
pub(crate) unsafe fn call_unchecked_raw_with_rr<T>(
1069+
mut store: &mut StoreContextMut<'_, T>,
1070+
func_ref: NonNull<VMFuncRef>,
1071+
params_and_returns: NonNull<[ValRaw]>,
1072+
rr: RRWasmFuncType,
1073+
) -> Result<()> {
1074+
// SAFETY: the safety of this function call is the same as the contract
1075+
// of this function.
1076+
match rr {
1077+
RRWasmFuncType::Core { ty, origin } => {
1078+
rr::core_hooks::record_and_replay_validate_wasm_func(
1079+
|mut store| {
1080+
// SAFETY: the safety of this function call is the same as the contract
1081+
// of this function.
1082+
unsafe {
1083+
Self::call_unchecked_raw(&mut store, func_ref, params_and_returns)
1084+
}
1085+
},
1086+
unsafe { params_and_returns.as_ref() },
1087+
ty,
1088+
origin,
1089+
&mut store,
1090+
)
1091+
}
1092+
#[cfg(feature = "component-model")]
1093+
RRWasmFuncType::Component { type_idx, types } => {
1094+
rr::component_hooks::record_and_replay_validate_wasm_func(
1095+
|mut store| unsafe {
1096+
Self::call_unchecked_raw(&mut store, func_ref, params_and_returns)
1097+
},
1098+
unsafe { params_and_returns.as_ref() },
1099+
type_idx,
1100+
types,
1101+
&mut store,
1102+
)
1103+
}
1104+
// Passthrough
1105+
RRWasmFuncType::None => unsafe {
1106+
Self::call_unchecked_raw(&mut store, func_ref, params_and_returns)
10511107
},
1052-
unsafe { params_and_returns.as_ref() },
1053-
&self.ty(&store),
1054-
self.origin.expand(),
1055-
&mut store,
1056-
)
1108+
}
10571109
}
10581110

10591111
pub(crate) unsafe fn call_unchecked_raw<T>(

crates/wasmtime/src/runtime/func/typed.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ where
211211
// For component mode, Realloc uses this method `TypedFunc::call_raw`, but Realloc is its
212212
// own separate event for record/replay purposes. For now, we use the should_record flag to
213213
// distinguish but this could be removed in the future by folding it into the main function call event.
214+
//
215+
// Note: This can't use [`crate::Func::call_unchecked_raw_with_rr`] directly because of the closure capture
214216
rr::core_hooks::record_and_replay_validate_wasm_func(
215217
|store| {
216218
invoke_wasm_and_catch_traps(store, |caller, vm| {

crates/wasmtime/src/runtime/rr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl FlatBytes for MaybeUninit<ValRaw> {
4343

4444
/// Convenience method hooks for injecting event recording/replaying in the rest of the engine
4545
mod hooks;
46-
pub(crate) use hooks::core_hooks;
46+
pub(crate) use hooks::{RRWasmFuncType, core_hooks};
4747
#[cfg(feature = "component-model")]
4848
pub(crate) use hooks::{
4949
component_hooks, component_hooks::ConstMemorySliceCell, component_hooks::MemorySliceCell,

crates/wasmtime/src/runtime/rr/hooks.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,26 @@
33
pub mod component_hooks;
44
/// Core RR hooks
55
pub mod core_hooks;
6+
7+
use crate::{FuncType, WasmFuncOrigin};
8+
#[cfg(feature = "component-model")]
9+
use alloc::sync::Arc;
10+
#[cfg(feature = "component-model")]
11+
use wasmtime_environ::component::{ComponentTypes, TypeFuncIndex};
12+
13+
/// Wasm function type information for RR hooks
14+
pub enum RRWasmFuncType<'a> {
15+
/// No RR hooks to be performed
16+
None,
17+
/// Core RR hooks to be performed
18+
Core {
19+
ty: &'a FuncType,
20+
origin: Option<WasmFuncOrigin>,
21+
},
22+
/// Component RR hooks to be performed
23+
#[cfg(feature = "component-model")]
24+
Component {
25+
type_idx: TypeFuncIndex,
26+
types: Arc<ComponentTypes>,
27+
},
28+
}

crates/wasmtime/src/runtime/rr/hooks/component_hooks.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::ValRaw;
2+
use crate::component::ComponentInstanceId;
23
#[cfg(feature = "rr-component")]
34
use crate::rr::{RecordBuffer, Recorder, component_events::MemorySliceWriteEvent};
45

@@ -10,15 +11,15 @@ use crate::rr::common_events::{HostFuncEntryEvent, WasmFuncReturnEvent};
1011
#[cfg(feature = "rr-component")]
1112
use crate::rr::component_events::{
1213
LowerFlatEntryEvent, LowerFlatReturnEvent, LowerMemoryEntryEvent, LowerMemoryReturnEvent,
13-
WasmFuncEntryEvent,
14+
WasmFuncBeginEvent, WasmFuncEntryEvent,
1415
};
1516
#[cfg(feature = "rr-component")]
1617
use crate::rr::{RRFuncArgVals, ResultEvent, common_events::HostFuncReturnEvent};
1718
use crate::store::StoreOpaque;
1819
use crate::{StoreContextMut, prelude::*};
1920
use alloc::sync::Arc;
2021
use core::mem::MaybeUninit;
21-
use wasmtime_environ::component::{ComponentTypes, InterfaceType, TypeFuncIndex};
22+
use wasmtime_environ::component::{ComponentTypes, ExportIndex, InterfaceType, TypeFuncIndex};
2223
#[cfg(all(feature = "rr-component"))]
2324
use wasmtime_environ::component::{MAX_FLAT_PARAMS, MAX_FLAT_RESULTS};
2425

@@ -30,6 +31,22 @@ pub enum ReplayLoweringPhase {
3031
HostFuncReturn,
3132
}
3233

34+
/// Record hook for initiating wasm component function call
35+
///
36+
/// This differs from WasmFuncEntryEvent since this is pre-lowering, and
37+
/// WasmFuncEntryEvent is post-lowering
38+
#[inline]
39+
pub fn record_wasm_func_begin(
40+
instance: ComponentInstanceId,
41+
func_idx: ExportIndex,
42+
store: &mut StoreOpaque,
43+
) -> Result<()> {
44+
#[cfg(feature = "rr-component")]
45+
store.record_event(|| WasmFuncBeginEvent { instance, func_idx })?;
46+
let _ = (instance, func_idx, store);
47+
Ok(())
48+
}
49+
3350
/// Record hook wrapping a wasm component export function invocation and replay
3451
/// validation of return value
3552
#[inline]

0 commit comments

Comments
 (0)