Skip to content

Commit cb8e0fc

Browse files
committed
Revamped public API for record; replay still broken
1 parent 4a42d21 commit cb8e0fc

File tree

7 files changed

+78
-112
lines changed

7 files changed

+78
-112
lines changed

crates/wasmtime/src/config.rs

Lines changed: 16 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ pub struct Config {
179179
pub(crate) macos_use_mach_ports: bool,
180180
pub(crate) detect_host_feature: Option<fn(&str) -> Option<bool>>,
181181
#[cfg(feature = "rr")]
182-
pub(crate) rr: Option<RRConfig>,
182+
pub(crate) record_support: bool,
183183
}
184184

185185
/// User-provided configuration for the compiler.
@@ -395,7 +395,7 @@ impl Config {
395395
#[cfg(not(feature = "std"))]
396396
detect_host_feature: None,
397397
#[cfg(feature = "rr")]
398-
rr: None,
398+
record_support: false,
399399
};
400400
#[cfg(any(feature = "cranelift", feature = "winch"))]
401401
{
@@ -2834,66 +2834,30 @@ impl Config {
28342834
self
28352835
}
28362836

2837-
/// Evaluates to true if current configuration must respect
2838-
/// deterministic execution in its configuration.
2839-
///
2840-
/// Required for faithful record/replay execution.
2841-
#[cfg(feature = "rr")]
2842-
#[inline]
2843-
pub fn is_determinism_enforced(&mut self) -> bool {
2844-
self.rr.is_some()
2845-
}
2846-
28472837
/// Enable execution trace recording with the provided configuration.
28482838
///
28492839
/// This method implicitly enforces determinism (see [`Config::enforce_determinism`]
28502840
/// for details).
2851-
///
2852-
/// ## Errors
2853-
///
2854-
/// Errors if record/replay are simultaneously enabled.
28552841
#[cfg(feature = "rr")]
2856-
pub fn enable_record(&mut self, record: RecordConfig) -> Result<&mut Self> {
2857-
self.enforce_determinism();
2858-
if let Some(cfg) = &self.rr {
2859-
if let RRConfig::Replay(_) = cfg {
2860-
bail!("Cannot enable recording when replay is already enabled");
2861-
}
2862-
}
2863-
self.rr = Some(RRConfig::from(record));
2864-
Ok(self)
2865-
}
2866-
2867-
/// Enable replay execution based on the provided configuration.
2868-
///
2869-
/// This method implicitly enforces determinism (see [`Config::enforce_determinism`]
2870-
/// for details).
2871-
///
2872-
/// ## Errors
2873-
///
2874-
/// Errors if record/replay are simultaneously enabled.
2875-
#[cfg(feature = "rr")]
2876-
pub fn enable_replay(&mut self, replay: ReplayConfig) -> Result<&mut Self> {
2877-
self.enforce_determinism();
2878-
if let Some(cfg) = &self.rr {
2879-
if let RRConfig::Record(_) = cfg {
2880-
bail!("Cannot enable replay when recording is already enabled");
2881-
}
2842+
#[inline]
2843+
pub fn recording(&mut self, enable: bool) -> &mut Self {
2844+
if enable {
2845+
self.enforce_determinism();
2846+
} else {
2847+
self.remove_determinism_enforcement();
28822848
}
2883-
self.rr = Some(RRConfig::from(replay));
2884-
Ok(self)
2849+
self.record_support = enable;
2850+
self
28852851
}
28862852

2887-
/// Disable the currently active record/replay configuration, and remove
2888-
/// any determinism enforcement it introduced as side-effects.
2853+
/// Evaluates to true if current configuration must respect
2854+
/// deterministic execution in its configuration.
28892855
///
2890-
/// A common option is used for both record/replay here
2891-
/// since record and replay can never be set simultaneously/
2856+
/// Required for faithful record/replay execution.
28922857
#[cfg(feature = "rr")]
2893-
pub fn disable_record_replay(&mut self) -> &mut Self {
2894-
self.remove_determinism_enforcement();
2895-
self.rr = None;
2896-
self
2858+
#[inline]
2859+
pub fn is_determinism_enforced(&mut self) -> bool {
2860+
self.record_support
28972861
}
28982862
}
28992863

crates/wasmtime/src/engine.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
use crate::Config;
2-
#[cfg(feature = "rr")]
3-
use crate::RRConfig;
42
use crate::prelude::*;
53
#[cfg(feature = "runtime")]
64
pub use crate::runtime::code_memory::CustomCodeMemory;
@@ -256,12 +254,11 @@ impl Engine {
256254
self.config().async_support
257255
}
258256

259-
/// Returns an immutable reference to the record/replay configuration settings
260-
/// used by the engine
257+
/// Returns whether the engine is configured to support execution recording
261258
#[cfg(feature = "rr")]
262259
#[inline]
263-
pub fn rr(&self) -> Option<&RRConfig> {
264-
self.config().rr.as_ref()
260+
pub fn is_recording(&self) -> bool {
261+
self.config().record_support
265262
}
266263

267264
/// Detects whether the bytes provided are a precompiled object produced by

crates/wasmtime/src/runtime.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ pub use linker::*;
8888
pub use memory::*;
8989
pub use module::{Module, ModuleExport};
9090
pub use resources::*;
91+
#[cfg(feature = "rr")]
92+
pub use rr::{RecordWriter, ReplayReader};
9193
#[cfg(all(feature = "async", feature = "call-hook"))]
9294
pub use store::CallHookHandler;
9395
pub use store::{

crates/wasmtime/src/runtime/instance.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,7 @@ fn pre_instantiate_raw(
933933
}
934934

935935
#[cfg(feature = "rr")]
936-
if module.engine().rr().is_some()
936+
if module.engine().is_recording()
937937
&& module.exports().any(|export| {
938938
use crate::ExternType;
939939
if let ExternType::Memory(_) = export.ty() {
@@ -943,7 +943,9 @@ fn pre_instantiate_raw(
943943
}
944944
})
945945
{
946-
bail!("Cannot support record/replay for core wasm modules when a memory is exported");
946+
bail!(
947+
"Cannot support recording for core wasm modules when a memory is exported; consider using components"
948+
);
947949
}
948950

949951
Ok(imports)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ cfg_if::cfg_if! {
3636
/// Serialize and write `value` to a `RecordWriter`
3737
///
3838
/// Currently uses `postcard` serializer
39-
pub fn to_record_writer<T, W>(value: &T, writer: W) -> Result<()>
39+
pub(super) fn to_record_writer<T, W>(value: &T, writer: W) -> Result<()>
4040
where
4141
T: Serialize + ?Sized,
4242
W: RecordWriter,
@@ -55,7 +55,7 @@ where
5555
///
5656
/// Currently uses `postcard` deserializer, with optional scratch
5757
/// buffer to deserialize into
58-
pub fn from_replay_reader<'a, T, R>(reader: R, scratch: &'a mut [u8]) -> Result<T>
58+
pub(super) fn from_replay_reader<'a, T, R>(reader: R, scratch: &'a mut [u8]) -> Result<T>
5959
where
6060
T: Deserialize<'a>,
6161
R: ReplayReader + 'a,

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

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,14 @@ rr_event! {
118118
ComponentBuiltinReturn(__component_events::BuiltinReturnEvent),
119119

120120
// OPTIONAL events for replay validation
121-
//
122-
// ReallocReturn is optional because we can assume the realloc is deterministic
123-
// and the error message is subsumed by the containing LowerReturn/LowerStoreReturn
124121

125122
/// Return from a Wasm component function back to host
126123
ComponentWasmFuncReturn(__component_events::WasmFuncReturnEvent),
127124
/// Return from Component ABI realloc call
125+
///
126+
/// Since realloc is deterministic, ReallocReturn is optional.
127+
/// Any error is subsumed by the containing LowerReturn/LowerStoreReturn
128+
/// that triggered realloc
128129
ComponentReallocReturn(__component_events::ReallocReturnEvent),
129130
/// Call into host function from component
130131
ComponentHostFuncEntry(__component_events::HostFuncEntryEvent),
@@ -193,7 +194,7 @@ impl From<EventActionError> for ReplayError {
193194
/// This trait provides the interface for a FIFO recorder
194195
pub trait Recorder {
195196
/// Construct a recorder with the writer backend
196-
fn new_recorder(writer: Box<dyn RecordWriter>, settings: RecordSettings) -> Result<Self>
197+
fn new_recorder(writer: impl RecordWriter + 'static, settings: RecordSettings) -> Result<Self>
197198
where
198199
Self: Sized;
199200

@@ -242,19 +243,9 @@ pub trait Replayer: Iterator<Item = RREvent> {
242243
Self: Sized;
243244

244245
/// Get settings associated with the replay process
245-
#[allow(
246-
unused,
247-
reason = "currently used only for validation resulting in \
248-
many unnecessary feature gates. will expand in the future to more features and this attribute can be removed"
249-
)]
250246
fn settings(&self) -> &ReplaySettings;
251247

252248
/// Get the settings (embedded within the trace) during recording
253-
#[allow(
254-
unused,
255-
reason = "currently used only for validation resulting in \
256-
many unnecessary feature gates. will expand in the future to more features and this attribute can be removed"
257-
)]
258249
fn trace_settings(&self) -> &RecordSettings;
259250

260251
// Provided Methods
@@ -364,13 +355,16 @@ impl Drop for RecordBuffer {
364355
}
365356

366357
impl Recorder for RecordBuffer {
367-
fn new_recorder(mut writer: Box<dyn RecordWriter>, settings: RecordSettings) -> Result<Self> {
358+
fn new_recorder(
359+
mut writer: impl RecordWriter + 'static,
360+
settings: RecordSettings,
361+
) -> Result<Self> {
368362
// Replay requires the Module version and record settings
369363
io::to_record_writer(ModuleVersionStrategy::WasmtimeVersion.as_str(), &mut writer)?;
370364
io::to_record_writer(&settings, &mut writer)?;
371365
Ok(RecordBuffer {
372366
buf: Vec::new(),
373-
writer: writer,
367+
writer: Box::new(writer),
374368
settings: settings,
375369
})
376370
}
@@ -405,18 +399,8 @@ pub struct ReplayBuffer {
405399
/// Reader to read replay trace from
406400
reader: Box<dyn ReplayReader>,
407401
/// Settings in replay configuration
408-
#[allow(
409-
unused,
410-
reason = "currently used only for validation resulting in \
411-
many unnecessary feature gates. will expand in the future to more features and this attribute can be removed"
412-
)]
413402
settings: ReplaySettings,
414403
/// Settings for record configuration (encoded in the trace)
415-
#[allow(
416-
unused,
417-
reason = "currently used only for validation resulting in \
418-
many unnecessary feature gates. will expand in the future to more features and this attribute can be removed"
419-
)]
420404
trace_settings: RecordSettings,
421405
/// Intermediate static buffer for deserialization
422406
deser_buffer: Vec<u8>,
@@ -454,7 +438,8 @@ impl Drop for ReplayBuffer {
454438
if let RREvent::Eof = event {
455439
} else {
456440
log::warn!(
457-
"Replay buffer is dropped with {} remaining events, and is likely an invalid execution",
441+
"Replay buffer is dropped with {} remaining events,
442+
and is likely an invalid/incomplete execution",
458443
self.count()
459444
);
460445
}

crates/wasmtime/src/runtime/store.rs

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676
//! contents of `StoreOpaque`. This is an invariant that we, as the authors of
7777
//! `wasmtime`, must uphold for the public interface to be safe.
7878
79+
#[cfg(feature = "rr")]
80+
use crate::RecordSettings;
7981
use crate::RootSet;
8082
#[cfg(feature = "component-model-async")]
8183
use crate::component::ComponentStoreData;
@@ -88,7 +90,9 @@ use crate::prelude::*;
8890
#[cfg(feature = "rr-validate")]
8991
use crate::rr::Validate;
9092
#[cfg(feature = "rr")]
91-
use crate::rr::{RREvent, RecordBuffer, Recorder, ReplayBuffer, ReplayError, Replayer};
93+
use crate::rr::{
94+
RREvent, RecordBuffer, RecordWriter, Recorder, ReplayBuffer, ReplayError, Replayer,
95+
};
9296
#[cfg(feature = "gc")]
9397
use crate::runtime::vm::GcRootsList;
9498
#[cfg(feature = "stack-switching")]
@@ -617,29 +621,9 @@ impl<T> Store<T> {
617621
#[cfg(feature = "component-model-async")]
618622
concurrent_async_state: Default::default(),
619623
#[cfg(feature = "rr")]
620-
record_buffer: engine.rr().and_then(|v| {
621-
v.record().and_then(|record| {
622-
Some(
623-
RecordBuffer::new_recorder(
624-
(record.writer_initializer)(),
625-
record.settings.clone(),
626-
)
627-
.unwrap(),
628-
)
629-
})
630-
}),
624+
record_buffer: None,
631625
#[cfg(feature = "rr")]
632-
replay_buffer: engine.rr().and_then(|v| {
633-
v.replay().and_then(|replay| {
634-
Some(
635-
ReplayBuffer::new_replayer(
636-
(replay.reader_initializer)(),
637-
replay.settings.clone(),
638-
)
639-
.unwrap(),
640-
)
641-
})
642-
}),
626+
replay_buffer: None,
643627
};
644628
let mut inner = Box::new(StoreInner {
645629
inner,
@@ -1028,6 +1012,20 @@ impl<T> Store<T> {
10281012
) {
10291013
self.inner.epoch_deadline_callback(Box::new(callback));
10301014
}
1015+
1016+
/// Configure a [`Store`] to enable execution recording
1017+
///
1018+
/// This feature must be initialized before instantiating any module within
1019+
/// the Store. Recording of events is performed according to provided settings, and
1020+
/// written to the provided writer.
1021+
#[cfg(feature = "rr")]
1022+
pub fn init_recording(
1023+
&mut self,
1024+
recorder: impl RecordWriter + 'static,
1025+
settings: RecordSettings,
1026+
) -> Result<()> {
1027+
self.inner.init_recording(recorder, settings)
1028+
}
10311029
}
10321030

10331031
impl<'a, T> StoreContext<'a, T> {
@@ -1458,6 +1456,24 @@ impl StoreOpaque {
14581456
&mut self.vm_store_context
14591457
}
14601458

1459+
#[cfg(feature = "rr")]
1460+
pub fn init_recording(
1461+
&mut self,
1462+
recorder: impl RecordWriter + 'static,
1463+
settings: RecordSettings,
1464+
) -> Result<()> {
1465+
ensure!(
1466+
self.instance_count == 0,
1467+
"store recording must be initialized before instantiating any modules or components"
1468+
);
1469+
ensure!(
1470+
self.engine().is_recording(),
1471+
"store recording requires recording enabled on config"
1472+
);
1473+
self.record_buffer = Some(RecordBuffer::new_recorder(recorder, settings)?);
1474+
Ok(())
1475+
}
1476+
14611477
#[cfg(feature = "rr")]
14621478
#[inline(always)]
14631479
pub fn record_buffer_mut(&mut self) -> Option<&mut RecordBuffer> {

0 commit comments

Comments
 (0)