diff --git a/src/component.rs b/src/component.rs index c87f43b6..39bf9297 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}, }; @@ -131,6 +131,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/execute.rs b/src/execute.rs index 01d204d7..f2e45b84 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -671,6 +671,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. let resp = outcome diff --git a/src/linking.rs b/src/linking.rs index 8ef56508..edc4be7d 100644 --- a/src/linking.rs +++ b/src/linking.rs @@ -92,6 +92,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, @@ -100,6 +120,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. @@ -160,6 +181,7 @@ impl ComponentCtx { session, guest_profiler: guest_profiler.map(Box::new), limiter: Limiter::new(100, 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 4b46743b..90a05a3f 100644 --- a/wasm_abi/wit/deps/fastly/compute.wit +++ b/wasm_abi/wit/deps/fastly/compute.wit @@ -2940,6 +2940,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; +}