Skip to content

Commit bc16bc7

Browse files
committed
Temporary patch: Replay API in-store; separate into independent driver in future
1 parent 3ba9849 commit bc16bc7

File tree

8 files changed

+113
-92
lines changed

8 files changed

+113
-92
lines changed

crates/wasmtime/src/config.rs

Lines changed: 21 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ use crate::stack::{StackCreator, StackCreatorProxy};
2424
#[cfg(feature = "async")]
2525
use wasmtime_fiber::RuntimeFiberStackCreator;
2626

27-
#[cfg(feature = "rr")]
28-
use crate::rr::ReplayReader;
2927
#[cfg(feature = "runtime")]
3028
pub use crate::runtime::code_memory::CustomCodeMemory;
3129
#[cfg(feature = "cache")]
@@ -178,6 +176,8 @@ pub struct Config {
178176
pub(crate) detect_host_feature: Option<fn(&str) -> Option<bool>>,
179177
#[cfg(feature = "rr")]
180178
pub(crate) record_support: bool,
179+
#[cfg(feature = "rr")]
180+
pub(crate) replay_support: bool,
181181
}
182182

183183
/// User-provided configuration for the compiler.
@@ -234,63 +234,6 @@ impl Default for CompilerConfig {
234234
}
235235
}
236236

237-
/// Settings for execution replay.
238-
#[cfg(feature = "rr")]
239-
#[derive(Debug, Clone)]
240-
pub struct ReplaySettings {
241-
/// Flag to include additional signatures for replay validation.
242-
pub validate: bool,
243-
/// Static buffer size for deserialization of variable-length types (like [String]).
244-
pub deser_buffer_size: usize,
245-
}
246-
247-
#[cfg(feature = "rr")]
248-
impl Default for ReplaySettings {
249-
fn default() -> Self {
250-
Self {
251-
validate: false,
252-
deser_buffer_size: 64,
253-
}
254-
}
255-
}
256-
257-
/// Configuration for replay execution.
258-
#[cfg(feature = "rr")]
259-
#[derive(Clone)]
260-
pub struct ReplayConfig {
261-
/// Closure that generates a reader for replaying execution traces.
262-
pub reader_initializer: Arc<dyn Fn() -> Box<dyn ReplayReader> + Send + Sync>,
263-
/// Flag for dynamic validation checks when replaying events.
264-
pub settings: ReplaySettings,
265-
}
266-
267-
/// Configurations for record/replay (RR) executions.
268-
#[cfg(feature = "rr")]
269-
#[derive(Clone)]
270-
pub enum RRConfig {
271-
/// Replay configuration.
272-
Replay(ReplayConfig),
273-
}
274-
275-
#[cfg(feature = "rr")]
276-
impl From<ReplayConfig> for RRConfig {
277-
fn from(value: ReplayConfig) -> Self {
278-
Self::Replay(value)
279-
}
280-
}
281-
282-
#[cfg(feature = "rr")]
283-
impl RRConfig {
284-
/// Obtain the replay configuration.
285-
///
286-
/// Return [`None`] if it is not configured.
287-
pub fn replay(&self) -> Option<&ReplayConfig> {
288-
match self {
289-
Self::Replay(r) => Some(r),
290-
}
291-
}
292-
}
293-
294237
impl Config {
295238
/// Creates a new configuration object with the default configuration
296239
/// specified.
@@ -345,6 +288,8 @@ impl Config {
345288
detect_host_feature: None,
346289
#[cfg(feature = "rr")]
347290
record_support: false,
291+
#[cfg(feature = "rr")]
292+
replay_support: false,
348293
};
349294
#[cfg(any(feature = "cranelift", feature = "winch"))]
350295
{
@@ -2799,14 +2744,30 @@ impl Config {
27992744
self
28002745
}
28012746

2747+
/// Enable execution trace replaying with the provided configuration.
2748+
///
2749+
/// This method implicitly enforces determinism (see [`Config::enforce_determinism`]
2750+
/// for details).
2751+
#[cfg(feature = "rr")]
2752+
#[inline]
2753+
pub fn replaying(&mut self, enable: bool) -> &mut Self {
2754+
if enable {
2755+
self.enforce_determinism();
2756+
} else {
2757+
self.remove_determinism_enforcement();
2758+
}
2759+
self.replay_support = enable;
2760+
self
2761+
}
2762+
28022763
/// Evaluates to true if current configuration must respect
28032764
/// deterministic execution in its configuration.
28042765
///
28052766
/// Required for faithful record/replay execution.
28062767
#[cfg(feature = "rr")]
28072768
#[inline]
28082769
pub fn is_determinism_enforced(&mut self) -> bool {
2809-
self.record_support
2770+
self.record_support || self.replay_support
28102771
}
28112772
}
28122773

crates/wasmtime/src/runtime.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ pub use memory::*;
8888
pub use module::{Module, ModuleExport};
8989
pub use resources::*;
9090
#[cfg(feature = "rr")]
91-
pub use rr::{RecordSettings, RecordWriter, ReplayReader};
91+
pub use rr::{RecordSettings, RecordWriter, ReplayReader, ReplaySettings};
9292
#[cfg(all(feature = "async", feature = "call-hook"))]
9393
pub use store::CallHookHandler;
9494
pub use store::{

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

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::config::{ModuleVersionStrategy, ReplaySettings};
1+
use crate::config::ModuleVersionStrategy;
22
use crate::prelude::*;
33
use core::fmt;
44
use events::EventActionError;
@@ -36,6 +36,26 @@ impl Default for RecordSettings {
3636
}
3737
}
3838

39+
/// Settings for execution replay.
40+
#[cfg(feature = "rr")]
41+
#[derive(Debug, Clone, Serialize, Deserialize)]
42+
pub struct ReplaySettings {
43+
/// Flag to include additional signatures for replay validation.
44+
pub validate: bool,
45+
/// Static buffer size for deserialization of variable-length types (like [String]).
46+
pub deser_buffer_size: usize,
47+
}
48+
49+
#[cfg(feature = "rr")]
50+
impl Default for ReplaySettings {
51+
fn default() -> Self {
52+
Self {
53+
validate: false,
54+
deser_buffer_size: 64,
55+
}
56+
}
57+
}
58+
3959
/// Encapsulation of event types comprising an [`RREvent`] sum type
4060
mod events;
4161
/// I/O support for reading and writing traces
@@ -249,7 +269,7 @@ pub trait Recorder {
249269
/// essentially operates as an iterator over the recorded events
250270
pub trait Replayer: Iterator<Item = RREvent> {
251271
/// Constructs a reader on buffer
252-
fn new_replayer(reader: Box<dyn ReplayReader>, settings: ReplaySettings) -> Result<Self>
272+
fn new_replayer(reader: impl ReplayReader + 'static, settings: ReplaySettings) -> Result<Self>
253273
where
254274
Self: Sized;
255275

@@ -459,7 +479,10 @@ impl Drop for ReplayBuffer {
459479
}
460480

461481
impl Replayer for ReplayBuffer {
462-
fn new_replayer(mut reader: Box<dyn ReplayReader>, settings: ReplaySettings) -> Result<Self> {
482+
fn new_replayer(
483+
mut reader: impl ReplayReader + 'static,
484+
settings: ReplaySettings,
485+
) -> Result<Self> {
463486
let mut scratch = [0u8; 12];
464487
// Ensure module versions match
465488
let version = io::from_replay_reader::<&str, _>(&mut reader, &mut scratch)?;
@@ -479,6 +502,7 @@ impl Replayer for ReplayBuffer {
479502
}
480503

481504
let deser_buffer = vec![0; settings.deser_buffer_size];
505+
let reader = Box::new(reader);
482506

483507
Ok(ReplayBuffer {
484508
reader,

crates/wasmtime/src/runtime/rr/core/events.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ where
132132
if self == expect {
133133
Ok(())
134134
} else {
135+
log::error!("Validation against {:?} failed!", expect);
135136
Err(ReplayError::FailedValidation)
136137
}
137138
}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ where
4747
store
4848
.0
4949
.record_event_validation(|| WasmFuncReturnEvent::from_anyhow_result(&result))?;
50-
store
51-
.0
52-
.next_replay_event_validation::<WasmFuncReturnEvent, Result<RRFuncArgVals>>(&result)?;
50+
// TODO: After adding validation support, replay with `next_replay_event_validation`
51+
store.0.next_replay_event_and(|_r: WasmFuncReturnEvent| {
52+
log::warn!("Yet to implement validation for WasmFuncReturnEvent; skipping for now");
53+
Ok(())
54+
})?;
5355
result?;
5456
return Ok(());
5557
}

crates/wasmtime/src/runtime/store.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ use crate::rr::Validate;
9090
#[cfg(feature = "rr")]
9191
use crate::rr::{
9292
RREvent, RecordBuffer, RecordSettings, RecordWriter, Recorder, ReplayBuffer, ReplayError,
93-
Replayer,
93+
ReplayReader, ReplaySettings, Replayer,
9494
};
9595
#[cfg(feature = "gc")]
9696
use crate::runtime::vm::GcRootsList;
@@ -1025,6 +1025,20 @@ impl<T> Store<T> {
10251025
) -> Result<()> {
10261026
self.inner.init_recording(recorder, settings)
10271027
}
1028+
1029+
/// Configure a [`Store`] to enable execution replaying
1030+
///
1031+
/// This feature must be initialized before instantiating any module within
1032+
/// the Store. Replay of events is performed according to provided settings, and
1033+
/// read from the provided reader.
1034+
#[cfg(feature = "rr")]
1035+
pub fn init_replaying(
1036+
&mut self,
1037+
replayer: impl ReplayReader + 'static,
1038+
settings: ReplaySettings,
1039+
) -> Result<()> {
1040+
self.inner.init_replaying(replayer, settings)
1041+
}
10281042
}
10291043

10301044
impl<'a, T> StoreContext<'a, T> {
@@ -1473,6 +1487,24 @@ impl StoreOpaque {
14731487
Ok(())
14741488
}
14751489

1490+
#[cfg(feature = "rr")]
1491+
pub fn init_replaying(
1492+
&mut self,
1493+
replayer: impl ReplayReader + 'static,
1494+
settings: ReplaySettings,
1495+
) -> Result<()> {
1496+
ensure!(
1497+
self.instance_count == 0,
1498+
"replaying store must not initialize any modules"
1499+
);
1500+
ensure!(
1501+
!self.engine().is_recording(),
1502+
"store recording cannot be enabled when initializing replay"
1503+
);
1504+
self.replay_buffer = Some(ReplayBuffer::new_replayer(replayer, settings)?);
1505+
Ok(())
1506+
}
1507+
14761508
#[cfg(feature = "rr")]
14771509
#[inline(always)]
14781510
pub fn record_buffer_mut(&mut self) -> Option<&mut RecordBuffer> {

src/commands/replay.rs

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
use crate::commands::run::RunCommand;
44
use anyhow::Result;
55
use clap::Parser;
6-
use std::{fs, io::BufReader, path::PathBuf, sync::Arc};
7-
use wasmtime::{ReplayConfig, ReplaySettings};
6+
use std::path::PathBuf;
87

98
#[derive(Parser)]
109
/// Replay-specific options for CLI
@@ -16,19 +15,19 @@ pub struct ReplayOptions {
1615
///
1716
/// Note: The module used for replay must exactly match that used during recording
1817
#[arg(short, long, required = true, value_name = "RECORDED TRACE")]
19-
trace: PathBuf,
18+
pub trace: PathBuf,
2019

2120
/// Dynamic checks of record signatures to validate replay consistency.
2221
///
2322
/// Requires record traces to be generated with `validation_metadata` enabled.
2423
#[arg(short, long, default_value_t = false)]
25-
validate: bool,
24+
pub validate: bool,
2625

2726
/// Size of static buffer needed to deserialized variable-length types like String. This is not
2827
/// not relevant for basic functional recording/replaying, but may be required to replay traces where
2928
/// `validation-metadata` was enabled for recording
3029
#[arg(short, long, default_value_t = 64)]
31-
deser_buffer_size: usize,
30+
pub deser_buffer_size: usize,
3231
}
3332

3433
/// Execute a deterministic, embedding-agnostic replay of a Wasm modules given its associated recorded trace
@@ -48,19 +47,7 @@ impl ReplayCommand {
4847
if self.replay_opts.validate {
4948
anyhow::bail!("Cannot use `validate` when `rr-validate` feature is disabled");
5049
}
51-
let replay_cfg = ReplayConfig {
52-
reader_initializer: Arc::new(move || {
53-
Box::new(BufReader::new(
54-
fs::File::open(&self.replay_opts.trace).unwrap(),
55-
))
56-
}),
57-
settings: ReplaySettings {
58-
validate: self.replay_opts.validate,
59-
deser_buffer_size: self.replay_opts.deser_buffer_size,
60-
..Default::default()
61-
},
62-
};
6350
// Replay uses the `run` command harness
64-
self.run_cmd.execute(Some(replay_cfg))
51+
self.run_cmd.execute(Some(self.replay_opts))
6552
}
6653
}

src/commands/run.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,25 @@
55
allow(irrefutable_let_patterns, unreachable_patterns)
66
)]
77

8+
#[cfg(feature = "rr")]
9+
use crate::commands::ReplayOptions;
810
use crate::common::{Profile, RunCommon, RunTarget};
911
use anyhow::{Context as _, Error, Result, anyhow, bail};
1012
use clap::Parser;
1113
use std::ffi::OsString;
14+
#[cfg(feature = "rr")]
15+
use std::io::BufReader;
1216
use std::path::{Path, PathBuf};
1317
use std::sync::{Arc, Mutex};
1418
use std::thread;
1519
use wasi_common::sync::{Dir, TcpListener, WasiCtxBuilder, ambient_authority};
16-
#[cfg(feature = "rr")]
17-
use wasmtime::ReplayConfig;
1820
use wasmtime::{Engine, Func, Module, Store, StoreLimits, Val, ValType};
1921
use wasmtime_wasi::{WasiCtxView, WasiView};
2022

2123
#[cfg(feature = "rr")]
2224
use std::{fs, io};
2325
#[cfg(feature = "rr")]
24-
use wasmtime::RecordSettings;
26+
use wasmtime::{RecordSettings, ReplaySettings};
2527
#[cfg(feature = "wasi-config")]
2628
use wasmtime_wasi_config::{WasiConfig, WasiConfigVariables};
2729
#[cfg(feature = "wasi-http")]
@@ -93,7 +95,7 @@ impl RunCommand {
9395
/// Executes the command.
9496
pub fn execute(
9597
mut self,
96-
#[cfg(feature = "rr")] replay_cfg: Option<ReplayConfig>,
98+
#[cfg(feature = "rr")] replay_opts: Option<ReplayOptions>,
9799
) -> Result<()> {
98100
self.run.common.init_logging()?;
99101

@@ -115,8 +117,8 @@ impl RunCommand {
115117
}
116118

117119
#[cfg(feature = "rr")]
118-
if let Some(cfg) = replay_cfg {
119-
//config.enable_replay(cfg)?;
120+
if replay_opts.is_some() {
121+
config.replaying(true);
120122
}
121123

122124
let engine = Engine::new(&config)?;
@@ -195,6 +197,18 @@ impl RunCommand {
195197
store.init_recording(fs::File::create(&path)?, settings)?;
196198
}
197199
}
200+
201+
if let Some(opts) = replay_opts {
202+
let settings = ReplaySettings {
203+
validate: opts.validate,
204+
deser_buffer_size: opts.deser_buffer_size,
205+
..Default::default()
206+
};
207+
store.init_replaying(
208+
BufReader::new(fs::File::open(opts.trace).unwrap()),
209+
settings,
210+
)?;
211+
}
198212
}
199213

200214
// Always run the module asynchronously to ensure that the module can be

0 commit comments

Comments
 (0)