use alloc::string::ToString;
use super::*;
#[test]
fn records_event_mutations_and_replays_them() {
use std::sync::atomic::{AtomicU64, Ordering};
use miden_assembly::DefaultSourceManager;
use miden_core::events::EventId;
use miden_processor::{ProcessorState, advice::AdviceMutation, event::EventError};
struct CountingHandler {
calls: AtomicU64,
}
impl EventHandler for CountingHandler {
fn on_event(
&self,
_process: &ProcessorState<'_>,
) -> Result<Vec<AdviceMutation>, EventError> {
let call = self.calls.fetch_add(1, Ordering::SeqCst);
Ok(vec![AdviceMutation::extend_advice_stack(
[Felt::from(100u32 + call as u32)].into_iter().collect(),
)])
}
}
let source_manager: Arc<DefaultSourceManager> = Arc::new(DefaultSourceManager::default());
let event_name = "miden-debug::test::record-replay";
let event_id = EventId::from_name(event_name).as_u64();
let source = format!(
"begin push.{event_id} emit drop adv_push push.{event_id} emit drop adv_push add swap \
drop end"
);
let program = miden_assembly::Assembler::new(source_manager.clone())
.assemble_program("program", source)
.map(Arc::from)
.expect("failed to assemble test program");
let mut executor = Executor::new(Vec::new());
executor
.register_event_handler(
EventName::from_string(event_name.to_string()),
Arc::new(CountingHandler {
calls: AtomicU64::new(0),
}),
)
.expect("failed to register event handler");
executor.with_event_advice_mutations_recording();
let mut debug_executor = executor.into_debug(Arc::clone(&program), source_manager.clone());
while !debug_executor.stopped {
debug_executor.step().expect("recording step failed");
}
let recorded = debug_executor.host.take_recorded_event_mutations();
let recorded_result: u32 =
debug_executor.into_execution_trace().parse_result().expect("invalid result");
assert_eq!(recorded_result, 201);
assert_eq!(recorded.len(), 2, "expected one recorded entry per emit");
for (index, batch) in recorded.iter().enumerate() {
match batch.as_slice() {
[AdviceMutation::ExtendStack { stack }] => {
assert_eq!(
stack.iter().copied().collect::<Vec<_>>(),
[Felt::from(100u32 + index as u32)],
);
}
_ => panic!("unexpected mutations recorded for event {index}"),
}
}
let replay_executor = Executor::new(Vec::new());
let mut debug_executor = replay_executor.into_debug_with_replay(
program,
source_manager,
Vec::new(),
recorded.into(),
);
while !debug_executor.stopped {
debug_executor.step().expect("replay step failed");
}
let replayed_result: u32 = debug_executor
.into_execution_trace()
.parse_result()
.expect("invalid replay result");
assert_eq!(replayed_result, recorded_result);
}
#[test]
fn replay_invokes_builtin_event_handlers() {
use miden_assembly::DefaultSourceManager;
let source_manager: Arc<DefaultSourceManager> = Arc::new(DefaultSourceManager::default());
let program = miden_assembly::Assembler::new(source_manager.clone())
.assemble_program(
"program",
format!(
r#"
begin
emit.event("{FRAME_START_EVENT}")
emit.event("{FRAME_START_EVENT}")
end
"#
),
)
.map(Arc::<Package>::from)
.expect("failed to assemble test program");
let event_replay = VecDeque::from([Vec::new(), Vec::new()]);
let mut debug_executor = Executor::new(Vec::new()).into_debug_with_replay(
program,
source_manager,
Vec::new(),
event_replay,
);
while !debug_executor.stopped {
debug_executor.step().expect("replay step failed");
}
assert!(
debug_executor.callstack.frames().len() >= 2,
"expected replayed frame-start events to update the debugger call stack"
);
}
#[test]
fn replays_from_a_serialized_snapshot() {
use std::sync::atomic::{AtomicU64, Ordering};
use miden_assembly::DefaultSourceManager;
use miden_core::events::EventId;
use miden_processor::{ProcessorState, advice::AdviceMutation, event::EventError};
use crate::exec::ReplaySnapshot;
struct CountingHandler {
calls: AtomicU64,
}
impl EventHandler for CountingHandler {
fn on_event(
&self,
_process: &ProcessorState<'_>,
) -> Result<Vec<AdviceMutation>, EventError> {
let call = self.calls.fetch_add(1, Ordering::SeqCst);
Ok(vec![AdviceMutation::extend_advice_stack(
[Felt::from(100u32 + call as u32)].into_iter().collect(),
)])
}
}
let source_manager: Arc<DefaultSourceManager> = Arc::new(DefaultSourceManager::default());
let event_name = "miden-debug::test::snapshot-replay";
let event_id = EventId::from_name(event_name).as_u64();
let source = format!(
"begin push.{event_id} emit drop adv_push push.{event_id} emit drop adv_push add add end"
);
let program = miden_assembly::Assembler::new(source_manager.clone())
.assemble_program("program", source)
.map(Arc::<Package>::from)
.expect("failed to assemble test program");
let stack_inputs = StackInputs::new(&[Felt::from(7u32)]).unwrap();
let advice_inputs = AdviceInputs::default();
let options = ExecutionOptions::default();
let mut executor = Executor::from_config(ExecutionConfig {
inputs: stack_inputs,
advice_inputs: advice_inputs.clone(),
options,
});
executor
.register_event_handler(
EventName::from_string(event_name.to_string()),
Arc::new(CountingHandler {
calls: AtomicU64::new(0),
}),
)
.expect("failed to register event handler");
executor.with_event_advice_mutations_recording();
let mut debug_executor = executor.into_debug(program.clone(), source_manager.clone());
while !debug_executor.stopped {
debug_executor.step().expect("recording step failed");
}
let event_log = debug_executor.host.take_recorded_event_mutations();
let recorded_result: u32 =
debug_executor.into_execution_trace().parse_result().expect("invalid result");
let snapshot = ReplaySnapshot {
package: program.clone(),
stack_inputs,
advice_inputs,
options,
mast_forests: vec![LoadedMastForest::with_package_debug_info(
program.mast_forest().clone(),
program.debug_info(),
)],
event_log,
};
let restored = ReplaySnapshot::read_from_bytes(&snapshot.to_bytes())
.expect("snapshot failed to deserialize");
let replay_executor = Executor::from_config(ExecutionConfig {
inputs: restored.stack_inputs,
advice_inputs: restored.advice_inputs,
options: restored.options,
});
let mut debug_executor = replay_executor.into_debug_with_replay(
restored.package.clone(),
source_manager,
restored.mast_forests.clone(),
restored.event_log.into(),
);
while !debug_executor.stopped {
debug_executor.step().expect("replay step failed");
}
let replayed_result: u32 = debug_executor
.into_execution_trace()
.parse_result()
.expect("invalid replay result");
assert_eq!(replayed_result, recorded_result);
assert_eq!(replayed_result, 208);
}