From 5f0f4fbc0735831df50f2c1f69a6ba9d6ce38b87 Mon Sep 17 00:00:00 2001 From: Yan Chen Date: Tue, 7 Oct 2025 21:36:02 -0700 Subject: [PATCH 1/3] add recorder api --- src/component.rs | 26 +++++++++++++++++++++++++- src/linking.rs | 24 +++++++++++++++++++++++- wasm_abi/wit/deps/fastly/compute.wit | 1 + wasm_abi/wit/deps/proxy/recorder.wit | 16 ++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 wasm_abi/wit/deps/proxy/recorder.wit diff --git a/src/component.rs b/src/component.rs index d96879ad..c683c6d6 100644 --- a/src/component.rs +++ b/src/component.rs @@ -1,5 +1,5 @@ use { - crate::linking::ComponentCtx, + crate::linking::{ComponentCtx, FuncCall}, wasmtime::component::{self, HasSelf}, }; @@ -119,6 +119,30 @@ pub(crate) mod bindings { }); } +impl bindings::proxy::recorder::record::Host for ComponentCtx { + fn record_args(&mut self, method: Option, args: Vec, is_export: bool) { + let call = if is_export { + FuncCall::ExportArgs { + method: method.unwrap(), + args, + } + } else { + FuncCall::ImportArgs { method, args } + }; + println!("{:?}", call); + self.logger.push(call); + } + fn record_ret(&mut self, method: Option, ret: Option, is_export: bool) { + let call = if is_export { + FuncCall::ExportRet { method, ret } + } else { + FuncCall::ImportRet { method, ret } + }; + println!("{:?}", call); + self.logger.push(call); + } +} + pub fn link_host_functions(linker: &mut component::Linker) -> anyhow::Result<()> { let options = bindings::LinkOptions::default(); diff --git a/src/linking.rs b/src/linking.rs index d46ed8b3..02d26602 100644 --- a/src/linking.rs +++ b/src/linking.rs @@ -30,7 +30,7 @@ impl Limiter { memory_allocated: 0, internal: StoreLimitsBuilder::new() .instances(max_instances) - .memories(1) + .memories(5) .memory_size(128 * 1024 * 1024) .table_elements(98765) .tables(max_tables) @@ -91,6 +91,26 @@ impl wasmtime::ResourceLimiter for Limiter { } } +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub(crate) enum FuncCall { + ExportArgs { + method: String, + args: Vec, + }, + ExportRet { + method: Option, + ret: Option, + }, + ImportArgs { + method: Option, + args: Vec, + }, + ImportRet { + method: Option, + ret: Option, + }, +} + #[allow(unused)] pub struct ComponentCtx { pub wasi_ctx: wasmtime_wasi::WasiCtx, @@ -99,6 +119,7 @@ pub struct ComponentCtx { pub(crate) session: Session, guest_profiler: Option>, limiter: Limiter, + pub(crate) logger: Vec, } /// An extension trait for users of `ComponentCtx` to access the session. @@ -159,6 +180,7 @@ impl ComponentCtx { session, guest_profiler: guest_profiler.map(Box::new), limiter: Limiter::new(100, 100), + logger: Vec::new(), }; let mut store = Store::new(ctx.engine(), wasm_ctx); store.set_epoch_deadline(1); diff --git a/wasm_abi/wit/deps/fastly/compute.wit b/wasm_abi/wit/deps/fastly/compute.wit index 9299e048..77d4d12d 100644 --- a/wasm_abi/wit/deps/fastly/compute.wit +++ b/wasm_abi/wit/deps/fastly/compute.wit @@ -2864,6 +2864,7 @@ world service-imports { import secret-store; import security; import shielding; + import proxy:recorder/%record@0.1.0; } /// A Fastly Compute service. diff --git a/wasm_abi/wit/deps/proxy/recorder.wit b/wasm_abi/wit/deps/proxy/recorder.wit new file mode 100644 index 00000000..7fd55bb0 --- /dev/null +++ b/wasm_abi/wit/deps/proxy/recorder.wit @@ -0,0 +1,16 @@ +package proxy:recorder@0.1.0; + +interface %record { + record-args: func(method: option, args: list, is-export: bool); + record-ret: func(method: option, ret: option, is-export: bool); +} + +interface replay { + replay-export: func() -> option>>; + assert-export-ret: func(method: option, ret: option); + replay-import: func(method: option, args: option>) -> option; +} + +world host { + import %record; +} From aa2b8d6903b6fcabc637ebeba065575fc33b1371 Mon Sep 17 00:00:00 2001 From: Yan Chen Date: Wed, 8 Oct 2025 09:26:45 -0700 Subject: [PATCH 2/3] write trace to disk --- src/execute.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/execute.rs b/src/execute.rs index b6c7f774..cf821a5a 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -676,6 +676,12 @@ impl ExecuteCtx { } }; + // If we collected a recording trace, write to a file + if !store.data().logger.is_empty() { + let trace = serde_json::to_string(&store.data().logger).unwrap(); + std::fs::write("trace.out", &trace).unwrap(); + } + // Ensure the downstream response channel is closed, whether or not a response was // sent during execution. store.data_mut().session.close_downstream_response_sender(); From 06c045acdeb2a96084f09c55f393d08fbc711276 Mon Sep 17 00:00:00 2001 From: Yan Chen Date: Wed, 15 Oct 2025 16:33:46 -0700 Subject: [PATCH 3/3] fix --- src/execute.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/execute.rs b/src/execute.rs index cf821a5a..ec334360 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -664,7 +664,7 @@ impl ExecuteCtx { Err(e) => { if let Some(exit) = e.downcast_ref::() { if exit.0 == 0 { - return Ok(()); + Ok(()) } else { event!(Level::ERROR, "WebAssembly exited with error: {:?}", e); Err(ExecutionError::WasmTrap(e))