xref: /wasmtime-44.0.1/crates/bench-api/src/lib.rs (revision dbbae440)
1cc81570aSNick Fitzgerald //! A C API for benchmarking Wasmtime's WebAssembly compilation, instantiation,
2cc81570aSNick Fitzgerald //! and execution.
341d668b4SAndrew Brown //!
418fabd77SNick Fitzgerald //! The API expects calls that match the following state machine:
5cc81570aSNick Fitzgerald //!
618fabd77SNick Fitzgerald //! ```text
718fabd77SNick Fitzgerald //!               |
818fabd77SNick Fitzgerald //!               |
918fabd77SNick Fitzgerald //!               V
1018fabd77SNick Fitzgerald //! .---> wasm_bench_create
1118fabd77SNick Fitzgerald //! |        |        |
1218fabd77SNick Fitzgerald //! |        |        |
1318fabd77SNick Fitzgerald //! |        |        V
1418fabd77SNick Fitzgerald //! |        |   wasm_bench_compile
1518fabd77SNick Fitzgerald //! |        |     |            |
1618fabd77SNick Fitzgerald //! |        |     |            |     .----.
1718fabd77SNick Fitzgerald //! |        |     |            |     |    |
1818fabd77SNick Fitzgerald //! |        |     |            V     V    |
1918fabd77SNick Fitzgerald //! |        |     |     wasm_bench_instantiate <------.
2018fabd77SNick Fitzgerald //! |        |     |            |        |             |
2118fabd77SNick Fitzgerald //! |        |     |            |        |             |
2218fabd77SNick Fitzgerald //! |        |     |            |        |             |
2318fabd77SNick Fitzgerald //! |        |     |     .------'        '-----> wasm_bench_execute
2418fabd77SNick Fitzgerald //! |        |     |     |                             |
2518fabd77SNick Fitzgerald //! |        |     |     |                             |
2618fabd77SNick Fitzgerald //! |        V     V     V                             |
2718fabd77SNick Fitzgerald //! '------ wasm_bench_free <--------------------------'
2818fabd77SNick Fitzgerald //!               |
2918fabd77SNick Fitzgerald //!               |
3018fabd77SNick Fitzgerald //!               V
3118fabd77SNick Fitzgerald //! ```
32cc81570aSNick Fitzgerald //!
33cc81570aSNick Fitzgerald //! All API calls must happen on the same thread.
34cc81570aSNick Fitzgerald //!
35cc81570aSNick Fitzgerald //! Functions which return pointers use null as an error value. Function which
36cc81570aSNick Fitzgerald //! return `int` use `0` as OK and non-zero as an error value.
37cc81570aSNick Fitzgerald //!
38cc81570aSNick Fitzgerald //! # Example
3941d668b4SAndrew Brown //!
4041d668b4SAndrew Brown //! ```
41d1c1cb6aSNick Fitzgerald //! use std::ptr;
4241d668b4SAndrew Brown //! use wasmtime_bench_api::*;
43cc81570aSNick Fitzgerald //!
44d1c1cb6aSNick Fitzgerald //! let working_dir = std::env::current_dir().unwrap().display().to_string();
45ba6635dbSNick Fitzgerald //! let stdout_path = "./stdout.log";
46ba6635dbSNick Fitzgerald //! let stderr_path = "./stderr.log";
47ba6635dbSNick Fitzgerald //!
4818fabd77SNick Fitzgerald //! // Functions to start/end timers for compilation.
4918fabd77SNick Fitzgerald //! //
5018fabd77SNick Fitzgerald //! // The `compilation_timer` pointer configured in the `WasmBenchConfig` is
5118fabd77SNick Fitzgerald //! // passed through.
5218fabd77SNick Fitzgerald //! extern "C" fn compilation_start(timer: *mut u8) {
5318fabd77SNick Fitzgerald //!     // Start your compilation timer here.
5418fabd77SNick Fitzgerald //! }
5518fabd77SNick Fitzgerald //! extern "C" fn compilation_end(timer: *mut u8) {
5618fabd77SNick Fitzgerald //!     // End your compilation timer here.
5718fabd77SNick Fitzgerald //! }
5818fabd77SNick Fitzgerald //!
5918fabd77SNick Fitzgerald //! // Similar for instantiation.
6018fabd77SNick Fitzgerald //! extern "C" fn instantiation_start(timer: *mut u8) {
6118fabd77SNick Fitzgerald //!     // Start your instantiation timer here.
6218fabd77SNick Fitzgerald //! }
6318fabd77SNick Fitzgerald //! extern "C" fn instantiation_end(timer: *mut u8) {
6418fabd77SNick Fitzgerald //!     // End your instantiation timer here.
6518fabd77SNick Fitzgerald //! }
6618fabd77SNick Fitzgerald //!
6718fabd77SNick Fitzgerald //! // Similar for execution.
6818fabd77SNick Fitzgerald //! extern "C" fn execution_start(timer: *mut u8) {
6918fabd77SNick Fitzgerald //!     // Start your execution timer here.
7018fabd77SNick Fitzgerald //! }
7118fabd77SNick Fitzgerald //! extern "C" fn execution_end(timer: *mut u8) {
7218fabd77SNick Fitzgerald //!     // End your execution timer here.
7318fabd77SNick Fitzgerald //! }
7418fabd77SNick Fitzgerald //!
75ba6635dbSNick Fitzgerald //! let config = WasmBenchConfig {
76ba6635dbSNick Fitzgerald //!     working_dir_ptr: working_dir.as_ptr(),
77ba6635dbSNick Fitzgerald //!     working_dir_len: working_dir.len(),
78ba6635dbSNick Fitzgerald //!     stdout_path_ptr: stdout_path.as_ptr(),
79ba6635dbSNick Fitzgerald //!     stdout_path_len: stdout_path.len(),
80ba6635dbSNick Fitzgerald //!     stderr_path_ptr: stderr_path.as_ptr(),
81ba6635dbSNick Fitzgerald //!     stderr_path_len: stderr_path.len(),
82ba6635dbSNick Fitzgerald //!     stdin_path_ptr: ptr::null(),
83ba6635dbSNick Fitzgerald //!     stdin_path_len: 0,
8418fabd77SNick Fitzgerald //!     compilation_timer: ptr::null_mut(),
8518fabd77SNick Fitzgerald //!     compilation_start,
8618fabd77SNick Fitzgerald //!     compilation_end,
8718fabd77SNick Fitzgerald //!     instantiation_timer: ptr::null_mut(),
8818fabd77SNick Fitzgerald //!     instantiation_start,
8918fabd77SNick Fitzgerald //!     instantiation_end,
9018fabd77SNick Fitzgerald //!     execution_timer: ptr::null_mut(),
9118fabd77SNick Fitzgerald //!     execution_start,
9218fabd77SNick Fitzgerald //!     execution_end,
935c3642fcSAndrew Brown //!     execution_flags_ptr: ptr::null(),
945c3642fcSAndrew Brown //!     execution_flags_len: 0,
95ba6635dbSNick Fitzgerald //! };
96ba6635dbSNick Fitzgerald //!
97d1c1cb6aSNick Fitzgerald //! let mut bench_api = ptr::null_mut();
98d1c1cb6aSNick Fitzgerald //! unsafe {
99ba6635dbSNick Fitzgerald //!     let code = wasm_bench_create(config, &mut bench_api);
100d1c1cb6aSNick Fitzgerald //!     assert_eq!(code, OK);
101d1c1cb6aSNick Fitzgerald //!     assert!(!bench_api.is_null());
102d1c1cb6aSNick Fitzgerald //! };
103cc81570aSNick Fitzgerald //!
104cc81570aSNick Fitzgerald //! let wasm = wat::parse_bytes(br#"
105cc81570aSNick Fitzgerald //!     (module
10641d668b4SAndrew Brown //!         (func $bench_start (import "bench" "start"))
10741d668b4SAndrew Brown //!         (func $bench_end (import "bench" "end"))
10841d668b4SAndrew Brown //!         (func $start (export "_start")
109cc81570aSNick Fitzgerald //!             call $bench_start
110cc81570aSNick Fitzgerald //!             i32.const 1
111cc81570aSNick Fitzgerald //!             i32.const 2
112cc81570aSNick Fitzgerald //!             i32.add
113cc81570aSNick Fitzgerald //!             drop
114cc81570aSNick Fitzgerald //!             call $bench_end
115cc81570aSNick Fitzgerald //!         )
116cc81570aSNick Fitzgerald //!     )
117cc81570aSNick Fitzgerald //! "#).unwrap();
11841d668b4SAndrew Brown //!
11918fabd77SNick Fitzgerald //! // This will call the `compilation_{start,end}` timing functions on success.
120d1c1cb6aSNick Fitzgerald //! let code = unsafe { wasm_bench_compile(bench_api, wasm.as_ptr(), wasm.len()) };
121cc81570aSNick Fitzgerald //! assert_eq!(code, OK);
12241d668b4SAndrew Brown //!
12318fabd77SNick Fitzgerald //! // This will call the `instantiation_{start,end}` timing functions on success.
12418fabd77SNick Fitzgerald //! let code = unsafe { wasm_bench_instantiate(bench_api) };
125cc81570aSNick Fitzgerald //! assert_eq!(code, OK);
12641d668b4SAndrew Brown //!
12718fabd77SNick Fitzgerald //! // This will call the `execution_{start,end}` timing functions on success.
128d1c1cb6aSNick Fitzgerald //! let code = unsafe { wasm_bench_execute(bench_api) };
129cc81570aSNick Fitzgerald //! assert_eq!(code, OK);
13041d668b4SAndrew Brown //!
131cc81570aSNick Fitzgerald //! unsafe {
132d1c1cb6aSNick Fitzgerald //!     wasm_bench_free(bench_api);
133cc81570aSNick Fitzgerald //! }
13441d668b4SAndrew Brown //! ```
135cc81570aSNick Fitzgerald 
13618fabd77SNick Fitzgerald mod unsafe_send_sync;
13718fabd77SNick Fitzgerald 
13818fabd77SNick Fitzgerald use crate::unsafe_send_sync::UnsafeSendSync;
139a491eacaSNick Fitzgerald use clap::Parser;
140cc81570aSNick Fitzgerald use std::os::raw::{c_int, c_void};
141cc81570aSNick Fitzgerald use std::slice;
14218fabd77SNick Fitzgerald use std::{env, path::PathBuf};
143*dbbae440SNick Fitzgerald use wasmtime::{Engine, Instance, Linker, Module, Result, Store, error::Context as _};
1448995750aSAlex Crichton use wasmtime_cli_flags::CommonOptions;
145f7a5aa34SAlex Crichton use wasmtime_wasi::cli::{InputFile, OutputFile};
1464ac219fdSAlex Crichton use wasmtime_wasi::{DirPerms, FilePerms, I32Exit, WasiCtx, p1::WasiP1Ctx};
14741d668b4SAndrew Brown 
148cc81570aSNick Fitzgerald pub type ExitCode = c_int;
149cc81570aSNick Fitzgerald pub const OK: ExitCode = 0;
150cc81570aSNick Fitzgerald pub const ERR: ExitCode = -1;
151cc81570aSNick Fitzgerald 
152bc6dc083SNick Fitzgerald // Randomize the location of heap objects to avoid accidental locality being an
153bc6dc083SNick Fitzgerald // uncontrolled variable that obscures performance evaluation in our
154bc6dc083SNick Fitzgerald // experiments.
155bc6dc083SNick Fitzgerald #[cfg(feature = "shuffling-allocator")]
156bc6dc083SNick Fitzgerald #[global_allocator]
157bc6dc083SNick Fitzgerald static ALLOC: shuffling_allocator::ShufflingAllocator<std::alloc::System> =
158bc6dc083SNick Fitzgerald     shuffling_allocator::wrap!(&std::alloc::System);
159bc6dc083SNick Fitzgerald 
160ba6635dbSNick Fitzgerald /// Configuration options for the benchmark.
161ba6635dbSNick Fitzgerald #[repr(C)]
162ba6635dbSNick Fitzgerald pub struct WasmBenchConfig {
163ba6635dbSNick Fitzgerald     /// The working directory where benchmarks should be executed.
164ba6635dbSNick Fitzgerald     pub working_dir_ptr: *const u8,
165ba6635dbSNick Fitzgerald     pub working_dir_len: usize,
166ba6635dbSNick Fitzgerald 
167ba6635dbSNick Fitzgerald     /// The file path that should be created and used as `stdout`.
168ba6635dbSNick Fitzgerald     pub stdout_path_ptr: *const u8,
169ba6635dbSNick Fitzgerald     pub stdout_path_len: usize,
170ba6635dbSNick Fitzgerald 
171ba6635dbSNick Fitzgerald     /// The file path that should be created and used as `stderr`.
172ba6635dbSNick Fitzgerald     pub stderr_path_ptr: *const u8,
173ba6635dbSNick Fitzgerald     pub stderr_path_len: usize,
174ba6635dbSNick Fitzgerald 
175ba6635dbSNick Fitzgerald     /// The (optional) file path that should be opened and used as `stdin`. If
176ba6635dbSNick Fitzgerald     /// not provided, then the WASI context will not have a `stdin` initialized.
177ba6635dbSNick Fitzgerald     pub stdin_path_ptr: *const u8,
178ba6635dbSNick Fitzgerald     pub stdin_path_len: usize,
17918fabd77SNick Fitzgerald 
18018fabd77SNick Fitzgerald     /// The functions to start and stop performance timers/counters during Wasm
18118fabd77SNick Fitzgerald     /// compilation.
18218fabd77SNick Fitzgerald     pub compilation_timer: *mut u8,
18318fabd77SNick Fitzgerald     pub compilation_start: extern "C" fn(*mut u8),
18418fabd77SNick Fitzgerald     pub compilation_end: extern "C" fn(*mut u8),
18518fabd77SNick Fitzgerald 
18618fabd77SNick Fitzgerald     /// The functions to start and stop performance timers/counters during Wasm
18718fabd77SNick Fitzgerald     /// instantiation.
18818fabd77SNick Fitzgerald     pub instantiation_timer: *mut u8,
18918fabd77SNick Fitzgerald     pub instantiation_start: extern "C" fn(*mut u8),
19018fabd77SNick Fitzgerald     pub instantiation_end: extern "C" fn(*mut u8),
19118fabd77SNick Fitzgerald 
19218fabd77SNick Fitzgerald     /// The functions to start and stop performance timers/counters during Wasm
19318fabd77SNick Fitzgerald     /// execution.
19418fabd77SNick Fitzgerald     pub execution_timer: *mut u8,
19518fabd77SNick Fitzgerald     pub execution_start: extern "C" fn(*mut u8),
19618fabd77SNick Fitzgerald     pub execution_end: extern "C" fn(*mut u8),
1975c3642fcSAndrew Brown 
1985c3642fcSAndrew Brown     /// The (optional) flags to use when running Wasmtime. These correspond to
1995c3642fcSAndrew Brown     /// the flags used when running Wasmtime from the command line.
2005c3642fcSAndrew Brown     pub execution_flags_ptr: *const u8,
2015c3642fcSAndrew Brown     pub execution_flags_len: usize,
202ba6635dbSNick Fitzgerald }
203ba6635dbSNick Fitzgerald 
204ba6635dbSNick Fitzgerald impl WasmBenchConfig {
working_dir(&self) -> Result<PathBuf>20518fabd77SNick Fitzgerald     fn working_dir(&self) -> Result<PathBuf> {
206ba6635dbSNick Fitzgerald         let working_dir =
207ba6635dbSNick Fitzgerald             unsafe { std::slice::from_raw_parts(self.working_dir_ptr, self.working_dir_len) };
208ba6635dbSNick Fitzgerald         let working_dir = std::str::from_utf8(working_dir)
209ba6635dbSNick Fitzgerald             .context("given working directory is not valid UTF-8")?;
21018fabd77SNick Fitzgerald         Ok(working_dir.into())
211ba6635dbSNick Fitzgerald     }
212ba6635dbSNick Fitzgerald 
stdout_path(&self) -> Result<PathBuf>21318fabd77SNick Fitzgerald     fn stdout_path(&self) -> Result<PathBuf> {
214ba6635dbSNick Fitzgerald         let stdout_path =
215ba6635dbSNick Fitzgerald             unsafe { std::slice::from_raw_parts(self.stdout_path_ptr, self.stdout_path_len) };
216ba6635dbSNick Fitzgerald         let stdout_path =
217ba6635dbSNick Fitzgerald             std::str::from_utf8(stdout_path).context("given stdout path is not valid UTF-8")?;
21818fabd77SNick Fitzgerald         Ok(stdout_path.into())
219ba6635dbSNick Fitzgerald     }
220ba6635dbSNick Fitzgerald 
stderr_path(&self) -> Result<PathBuf>22118fabd77SNick Fitzgerald     fn stderr_path(&self) -> Result<PathBuf> {
222ba6635dbSNick Fitzgerald         let stderr_path =
223ba6635dbSNick Fitzgerald             unsafe { std::slice::from_raw_parts(self.stderr_path_ptr, self.stderr_path_len) };
224ba6635dbSNick Fitzgerald         let stderr_path =
225ba6635dbSNick Fitzgerald             std::str::from_utf8(stderr_path).context("given stderr path is not valid UTF-8")?;
22618fabd77SNick Fitzgerald         Ok(stderr_path.into())
227ba6635dbSNick Fitzgerald     }
228ba6635dbSNick Fitzgerald 
stdin_path(&self) -> Result<Option<PathBuf>>22918fabd77SNick Fitzgerald     fn stdin_path(&self) -> Result<Option<PathBuf>> {
230ba6635dbSNick Fitzgerald         if self.stdin_path_ptr.is_null() {
231ba6635dbSNick Fitzgerald             return Ok(None);
232ba6635dbSNick Fitzgerald         }
233ba6635dbSNick Fitzgerald 
234ba6635dbSNick Fitzgerald         let stdin_path =
235ba6635dbSNick Fitzgerald             unsafe { std::slice::from_raw_parts(self.stdin_path_ptr, self.stdin_path_len) };
236ba6635dbSNick Fitzgerald         let stdin_path =
237ba6635dbSNick Fitzgerald             std::str::from_utf8(stdin_path).context("given stdin path is not valid UTF-8")?;
23818fabd77SNick Fitzgerald         Ok(Some(stdin_path.into()))
239ba6635dbSNick Fitzgerald     }
2405c3642fcSAndrew Brown 
execution_flags(&self) -> Result<CommonOptions>241a491eacaSNick Fitzgerald     fn execution_flags(&self) -> Result<CommonOptions> {
242a491eacaSNick Fitzgerald         let flags = if self.execution_flags_ptr.is_null() {
243a491eacaSNick Fitzgerald             ""
244a491eacaSNick Fitzgerald         } else {
2455c3642fcSAndrew Brown             let execution_flags = unsafe {
2465c3642fcSAndrew Brown                 std::slice::from_raw_parts(self.execution_flags_ptr, self.execution_flags_len)
2475c3642fcSAndrew Brown             };
248a491eacaSNick Fitzgerald             std::str::from_utf8(execution_flags)
249a491eacaSNick Fitzgerald                 .context("given execution flags string is not valid UTF-8")?
250a491eacaSNick Fitzgerald         };
251a491eacaSNick Fitzgerald         let options = CommonOptions::try_parse_from(
252a491eacaSNick Fitzgerald             ["wasmtime"]
253a491eacaSNick Fitzgerald                 .into_iter()
254a491eacaSNick Fitzgerald                 .chain(flags.split(' ').filter(|s| !s.is_empty())),
255a491eacaSNick Fitzgerald         )
256a491eacaSNick Fitzgerald         .context("failed to parse options")?;
257a491eacaSNick Fitzgerald         Ok(options)
2585c3642fcSAndrew Brown     }
259ba6635dbSNick Fitzgerald }
260ba6635dbSNick Fitzgerald 
261cc81570aSNick Fitzgerald /// Exposes a C-compatible way of creating the engine from the bytes of a single
262cc81570aSNick Fitzgerald /// Wasm module.
263cc81570aSNick Fitzgerald ///
264d1c1cb6aSNick Fitzgerald /// On success, the `out_bench_ptr` is initialized to a pointer to a structure
265d1c1cb6aSNick Fitzgerald /// that contains the engine's initialized state, and `0` is returned. On
266d1c1cb6aSNick Fitzgerald /// failure, a non-zero status code is returned and `out_bench_ptr` is left
267d1c1cb6aSNick Fitzgerald /// untouched.
268ae84e6edSAlex Crichton #[unsafe(no_mangle)]
wasm_bench_create( config: WasmBenchConfig, out_bench_ptr: *mut *mut c_void, ) -> ExitCode269d1c1cb6aSNick Fitzgerald pub extern "C" fn wasm_bench_create(
270ba6635dbSNick Fitzgerald     config: WasmBenchConfig,
271d1c1cb6aSNick Fitzgerald     out_bench_ptr: *mut *mut c_void,
272d1c1cb6aSNick Fitzgerald ) -> ExitCode {
273d1c1cb6aSNick Fitzgerald     let result = (|| -> Result<_> {
274ba6635dbSNick Fitzgerald         let working_dir = config.working_dir()?;
275ba6635dbSNick Fitzgerald         let stdout_path = config.stdout_path()?;
276ba6635dbSNick Fitzgerald         let stderr_path = config.stderr_path()?;
277ba6635dbSNick Fitzgerald         let stdin_path = config.stdin_path()?;
2785a288c2cSAndrew Brown         let options = config.execution_flags()?;
27918fabd77SNick Fitzgerald 
280ba6635dbSNick Fitzgerald         let state = Box::new(BenchState::new(
2815a288c2cSAndrew Brown             options,
28218fabd77SNick Fitzgerald             config.compilation_timer,
28318fabd77SNick Fitzgerald             config.compilation_start,
28418fabd77SNick Fitzgerald             config.compilation_end,
28518fabd77SNick Fitzgerald             config.instantiation_timer,
28618fabd77SNick Fitzgerald             config.instantiation_start,
28718fabd77SNick Fitzgerald             config.instantiation_end,
28818fabd77SNick Fitzgerald             config.execution_timer,
28918fabd77SNick Fitzgerald             config.execution_start,
29018fabd77SNick Fitzgerald             config.execution_end,
29118fabd77SNick Fitzgerald             move || {
292f7a5aa34SAlex Crichton                 let mut cx = WasiCtx::builder();
29318fabd77SNick Fitzgerald 
29418fabd77SNick Fitzgerald                 let stdout = std::fs::File::create(&stdout_path)
29518fabd77SNick Fitzgerald                     .with_context(|| format!("failed to create {}", stdout_path.display()))?;
296f7a5aa34SAlex Crichton                 cx.stdout(OutputFile::new(stdout));
29718fabd77SNick Fitzgerald 
29818fabd77SNick Fitzgerald                 let stderr = std::fs::File::create(&stderr_path)
29918fabd77SNick Fitzgerald                     .with_context(|| format!("failed to create {}", stderr_path.display()))?;
300f7a5aa34SAlex Crichton                 cx.stderr(OutputFile::new(stderr));
30118fabd77SNick Fitzgerald 
30218fabd77SNick Fitzgerald                 if let Some(stdin_path) = &stdin_path {
30318fabd77SNick Fitzgerald                     let stdin = std::fs::File::open(stdin_path)
30418fabd77SNick Fitzgerald                         .with_context(|| format!("failed to open {}", stdin_path.display()))?;
305f7a5aa34SAlex Crichton                     cx.stdin(InputFile::new(stdin));
30618fabd77SNick Fitzgerald                 }
30718fabd77SNick Fitzgerald 
30818fabd77SNick Fitzgerald                 // Allow access to the working directory so that the benchmark can read
30918fabd77SNick Fitzgerald                 // its input workload(s).
310f7a5aa34SAlex Crichton                 cx.preopened_dir(working_dir.clone(), ".", DirPerms::READ, FilePerms::READ)?;
31118fabd77SNick Fitzgerald 
31218fabd77SNick Fitzgerald                 // Pass this env var along so that the benchmark program can use smaller
31318fabd77SNick Fitzgerald                 // input workload(s) if it has them and that has been requested.
31418fabd77SNick Fitzgerald                 if let Ok(val) = env::var("WASM_BENCH_USE_SMALL_WORKLOAD") {
315f7a5aa34SAlex Crichton                     cx.env("WASM_BENCH_USE_SMALL_WORKLOAD", &val);
31618fabd77SNick Fitzgerald                 }
31718fabd77SNick Fitzgerald 
318f7a5aa34SAlex Crichton                 Ok(cx.build_p1())
31918fabd77SNick Fitzgerald             },
320ba6635dbSNick Fitzgerald         )?);
321d1c1cb6aSNick Fitzgerald         Ok(Box::into_raw(state) as _)
322d1c1cb6aSNick Fitzgerald     })();
323d1c1cb6aSNick Fitzgerald 
324d1c1cb6aSNick Fitzgerald     if let Ok(bench_ptr) = result {
325d1c1cb6aSNick Fitzgerald         unsafe {
326d1c1cb6aSNick Fitzgerald             assert!(!out_bench_ptr.is_null());
327d1c1cb6aSNick Fitzgerald             *out_bench_ptr = bench_ptr;
328d1c1cb6aSNick Fitzgerald         }
329d1c1cb6aSNick Fitzgerald     }
330d1c1cb6aSNick Fitzgerald 
331d1c1cb6aSNick Fitzgerald     to_exit_code(result.map(|_| ()))
33241d668b4SAndrew Brown }
33341d668b4SAndrew Brown 
33441d668b4SAndrew Brown /// Free the engine state allocated by this library.
335ae84e6edSAlex Crichton #[unsafe(no_mangle)]
wasm_bench_free(state: *mut c_void)336cc81570aSNick Fitzgerald pub extern "C" fn wasm_bench_free(state: *mut c_void) {
337cc81570aSNick Fitzgerald     assert!(!state.is_null());
33841d668b4SAndrew Brown     unsafe {
339b17a734aSNick Fitzgerald         drop(Box::from_raw(state as *mut BenchState));
34041d668b4SAndrew Brown     }
34141d668b4SAndrew Brown }
34241d668b4SAndrew Brown 
34341d668b4SAndrew Brown /// Compile the Wasm benchmark module.
344ae84e6edSAlex Crichton #[unsafe(no_mangle)]
wasm_bench_compile( state: *mut c_void, wasm_bytes: *const u8, wasm_bytes_length: usize, ) -> ExitCode345cc81570aSNick Fitzgerald pub extern "C" fn wasm_bench_compile(
346cc81570aSNick Fitzgerald     state: *mut c_void,
347cc81570aSNick Fitzgerald     wasm_bytes: *const u8,
348cc81570aSNick Fitzgerald     wasm_bytes_length: usize,
349cc81570aSNick Fitzgerald ) -> ExitCode {
350cc81570aSNick Fitzgerald     let state = unsafe { (state as *mut BenchState).as_mut().unwrap() };
351cc81570aSNick Fitzgerald     let wasm_bytes = unsafe { slice::from_raw_parts(wasm_bytes, wasm_bytes_length) };
352cc81570aSNick Fitzgerald     let result = state.compile(wasm_bytes).context("failed to compile");
353cc81570aSNick Fitzgerald     to_exit_code(result)
35441d668b4SAndrew Brown }
35541d668b4SAndrew Brown 
35641d668b4SAndrew Brown /// Instantiate the Wasm benchmark module.
357ae84e6edSAlex Crichton #[unsafe(no_mangle)]
wasm_bench_instantiate(state: *mut c_void) -> ExitCode35818fabd77SNick Fitzgerald pub extern "C" fn wasm_bench_instantiate(state: *mut c_void) -> ExitCode {
359cc81570aSNick Fitzgerald     let state = unsafe { (state as *mut BenchState).as_mut().unwrap() };
36018fabd77SNick Fitzgerald     let result = state.instantiate().context("failed to instantiate");
361cc81570aSNick Fitzgerald     to_exit_code(result)
36241d668b4SAndrew Brown }
36341d668b4SAndrew Brown 
36441d668b4SAndrew Brown /// Execute the Wasm benchmark module.
365ae84e6edSAlex Crichton #[unsafe(no_mangle)]
wasm_bench_execute(state: *mut c_void) -> ExitCode366cc81570aSNick Fitzgerald pub extern "C" fn wasm_bench_execute(state: *mut c_void) -> ExitCode {
367cc81570aSNick Fitzgerald     let state = unsafe { (state as *mut BenchState).as_mut().unwrap() };
368cc81570aSNick Fitzgerald     let result = state.execute().context("failed to execute");
369cc81570aSNick Fitzgerald     to_exit_code(result)
37041d668b4SAndrew Brown }
37141d668b4SAndrew Brown 
372cc81570aSNick Fitzgerald /// Helper function for converting a Rust result to a C error code.
373cc81570aSNick Fitzgerald ///
374cc81570aSNick Fitzgerald /// This will print an error indicating some information regarding the failure.
to_exit_code<T>(result: impl Into<Result<T>>) -> ExitCode375cc81570aSNick Fitzgerald fn to_exit_code<T>(result: impl Into<Result<T>>) -> ExitCode {
376cc81570aSNick Fitzgerald     match result.into() {
377cc81570aSNick Fitzgerald         Ok(_) => OK,
37841d668b4SAndrew Brown         Err(error) => {
379a0442ea0SHamir Mahal             eprintln!("{error:?}");
380cc81570aSNick Fitzgerald             ERR
38141d668b4SAndrew Brown         }
38241d668b4SAndrew Brown     }
38341d668b4SAndrew Brown }
38441d668b4SAndrew Brown 
385cc81570aSNick Fitzgerald /// This structure contains the actual Rust implementation of the state required
386cc81570aSNick Fitzgerald /// to manage the Wasmtime engine between calls.
387cc81570aSNick Fitzgerald struct BenchState {
3887a1b7cdfSAlex Crichton     linker: Linker<HostState>,
38918fabd77SNick Fitzgerald     compilation_timer: *mut u8,
39018fabd77SNick Fitzgerald     compilation_start: extern "C" fn(*mut u8),
39118fabd77SNick Fitzgerald     compilation_end: extern "C" fn(*mut u8),
39218fabd77SNick Fitzgerald     instantiation_timer: *mut u8,
39318fabd77SNick Fitzgerald     instantiation_start: extern "C" fn(*mut u8),
39418fabd77SNick Fitzgerald     instantiation_end: extern "C" fn(*mut u8),
395f7a5aa34SAlex Crichton     make_wasi_cx: Box<dyn FnMut() -> Result<WasiP1Ctx>>,
39641d668b4SAndrew Brown     module: Option<Module>,
3977a1b7cdfSAlex Crichton     store_and_instance: Option<(Store<HostState>, Instance)>,
3982cfa0248SAlex Crichton     epoch_interruption: bool,
3992cfa0248SAlex Crichton     fuel: Option<u64>,
4007a1b7cdfSAlex Crichton }
4017a1b7cdfSAlex Crichton 
4027a1b7cdfSAlex Crichton struct HostState {
403f7a5aa34SAlex Crichton     wasi: WasiP1Ctx,
4047a1b7cdfSAlex Crichton     #[cfg(feature = "wasi-nn")]
4050f4ae88aSAndrew Brown     wasi_nn: wasmtime_wasi_nn::witx::WasiNnCtx,
40641d668b4SAndrew Brown }
40741d668b4SAndrew Brown 
408cc81570aSNick Fitzgerald impl BenchState {
new( mut options: CommonOptions, compilation_timer: *mut u8, compilation_start: extern "C" fn(*mut u8), compilation_end: extern "C" fn(*mut u8), instantiation_timer: *mut u8, instantiation_start: extern "C" fn(*mut u8), instantiation_end: extern "C" fn(*mut u8), execution_timer: *mut u8, execution_start: extern "C" fn(*mut u8), execution_end: extern "C" fn(*mut u8), make_wasi_cx: impl FnMut() -> Result<WasiP1Ctx> + 'static, ) -> Result<Self>409ba6635dbSNick Fitzgerald     fn new(
4108995750aSAlex Crichton         mut options: CommonOptions,
41118fabd77SNick Fitzgerald         compilation_timer: *mut u8,
41218fabd77SNick Fitzgerald         compilation_start: extern "C" fn(*mut u8),
41318fabd77SNick Fitzgerald         compilation_end: extern "C" fn(*mut u8),
41418fabd77SNick Fitzgerald         instantiation_timer: *mut u8,
41518fabd77SNick Fitzgerald         instantiation_start: extern "C" fn(*mut u8),
41618fabd77SNick Fitzgerald         instantiation_end: extern "C" fn(*mut u8),
41718fabd77SNick Fitzgerald         execution_timer: *mut u8,
41818fabd77SNick Fitzgerald         execution_start: extern "C" fn(*mut u8),
41918fabd77SNick Fitzgerald         execution_end: extern "C" fn(*mut u8),
420f7a5aa34SAlex Crichton         make_wasi_cx: impl FnMut() -> Result<WasiP1Ctx> + 'static,
421ba6635dbSNick Fitzgerald     ) -> Result<Self> {
42257cd5a9eSAlex Crichton         let mut config = options.config(None)?;
423a491eacaSNick Fitzgerald         // NB: always disable the compilation cache.
4242cd52b76SBen Brandt         config.cache(None);
4255a288c2cSAndrew Brown         let engine = Engine::new(&config)?;
4267a1b7cdfSAlex Crichton         let mut linker = Linker::<HostState>::new(&engine);
42718fabd77SNick Fitzgerald 
42818fabd77SNick Fitzgerald         // Define the benchmarking start/end functions.
42918fabd77SNick Fitzgerald         let execution_timer = unsafe {
43018fabd77SNick Fitzgerald             // Safe because this bench API's contract requires that its methods
43118fabd77SNick Fitzgerald             // are only ever called from a single thread.
43218fabd77SNick Fitzgerald             UnsafeSendSync::new(execution_timer)
43318fabd77SNick Fitzgerald         };
4347a1b7cdfSAlex Crichton         linker.func_wrap("bench", "start", move || {
43518fabd77SNick Fitzgerald             execution_start(*execution_timer.get());
43618fabd77SNick Fitzgerald             Ok(())
4377a1b7cdfSAlex Crichton         })?;
4387a1b7cdfSAlex Crichton         linker.func_wrap("bench", "end", move || {
43918fabd77SNick Fitzgerald             execution_end(*execution_timer.get());
44018fabd77SNick Fitzgerald             Ok(())
4417a1b7cdfSAlex Crichton         })?;
442cc81570aSNick Fitzgerald 
4438995750aSAlex Crichton         let epoch_interruption = options.wasm.epoch_interruption.unwrap_or(false);
4448995750aSAlex Crichton         let fuel = options.wasm.fuel;
4452cfa0248SAlex Crichton 
4468995750aSAlex Crichton         if options.wasi.common != Some(false) {
4474ac219fdSAlex Crichton             wasmtime_wasi::p1::add_to_linker_sync(&mut linker, |cx| &mut cx.wasi)?;
4485a288c2cSAndrew Brown         }
4497a1b7cdfSAlex Crichton 
4507a1b7cdfSAlex Crichton         #[cfg(feature = "wasi-nn")]
4518995750aSAlex Crichton         if options.wasi.nn == Some(true) {
4526084b736SAlex Crichton             wasmtime_wasi_nn::witx::add_to_linker(&mut linker, |cx| &mut cx.wasi_nn)?;
4535a288c2cSAndrew Brown         }
4547a1b7cdfSAlex Crichton 
45518fabd77SNick Fitzgerald         Ok(Self {
4567a1b7cdfSAlex Crichton             linker,
45718fabd77SNick Fitzgerald             compilation_timer,
45818fabd77SNick Fitzgerald             compilation_start,
45918fabd77SNick Fitzgerald             compilation_end,
46018fabd77SNick Fitzgerald             instantiation_timer,
46118fabd77SNick Fitzgerald             instantiation_start,
46218fabd77SNick Fitzgerald             instantiation_end,
46318fabd77SNick Fitzgerald             make_wasi_cx: Box::new(make_wasi_cx) as _,
46418fabd77SNick Fitzgerald             module: None,
4657a1b7cdfSAlex Crichton             store_and_instance: None,
4662cfa0248SAlex Crichton             epoch_interruption,
4672cfa0248SAlex Crichton             fuel,
46818fabd77SNick Fitzgerald         })
469ba6635dbSNick Fitzgerald     }
470ba6635dbSNick Fitzgerald 
compile(&mut self, bytes: &[u8]) -> Result<()>47118fabd77SNick Fitzgerald     fn compile(&mut self, bytes: &[u8]) -> Result<()> {
472d621b453SNick Fitzgerald         self.module = None;
473ba6635dbSNick Fitzgerald 
47418fabd77SNick Fitzgerald         (self.compilation_start)(self.compilation_timer);
4757a1b7cdfSAlex Crichton         let module = Module::from_binary(self.linker.engine(), bytes)?;
47618fabd77SNick Fitzgerald         (self.compilation_end)(self.compilation_timer);
47718fabd77SNick Fitzgerald 
47818fabd77SNick Fitzgerald         self.module = Some(module);
47918fabd77SNick Fitzgerald         Ok(())
480d1c1cb6aSNick Fitzgerald     }
481d1c1cb6aSNick Fitzgerald 
instantiate(&mut self) -> Result<()>48218fabd77SNick Fitzgerald     fn instantiate(&mut self) -> Result<()> {
483d621b453SNick Fitzgerald         self.store_and_instance = None;
484d621b453SNick Fitzgerald 
48518fabd77SNick Fitzgerald         let module = self
48618fabd77SNick Fitzgerald             .module
48718fabd77SNick Fitzgerald             .as_ref()
48818fabd77SNick Fitzgerald             .expect("compile the module before instantiating it");
48918fabd77SNick Fitzgerald 
4907a1b7cdfSAlex Crichton         let host = HostState {
4917a1b7cdfSAlex Crichton             wasi: (self.make_wasi_cx)().context("failed to create a WASI context")?,
4927a1b7cdfSAlex Crichton             #[cfg(feature = "wasi-nn")]
4936084b736SAlex Crichton             wasi_nn: {
4946084b736SAlex Crichton                 let (backends, registry) = wasmtime_wasi_nn::preload(&[])?;
4950f4ae88aSAndrew Brown                 wasmtime_wasi_nn::witx::WasiNnCtx::new(backends, registry)
4966084b736SAlex Crichton             },
4977a1b7cdfSAlex Crichton         };
49818fabd77SNick Fitzgerald 
49918fabd77SNick Fitzgerald         // NB: Start measuring instantiation time *after* we've created the WASI
50018fabd77SNick Fitzgerald         // context, since that needs to do file I/O to setup
50118fabd77SNick Fitzgerald         // stdin/stdout/stderr.
50218fabd77SNick Fitzgerald         (self.instantiation_start)(self.instantiation_timer);
5037a1b7cdfSAlex Crichton         let mut store = Store::new(self.linker.engine(), host);
5042cfa0248SAlex Crichton         if self.epoch_interruption {
5052cfa0248SAlex Crichton             store.set_epoch_deadline(1);
5062cfa0248SAlex Crichton         }
5072cfa0248SAlex Crichton         if let Some(fuel) = self.fuel {
50885c0a2dfSTyler Rockwood             store.set_fuel(fuel).unwrap();
5092cfa0248SAlex Crichton         }
5102cfa0248SAlex Crichton 
5117a1b7cdfSAlex Crichton         let instance = self.linker.instantiate(&mut store, &module)?;
51218fabd77SNick Fitzgerald         (self.instantiation_end)(self.instantiation_timer);
51341d668b4SAndrew Brown 
5147a1b7cdfSAlex Crichton         self.store_and_instance = Some((store, instance));
51541d668b4SAndrew Brown         Ok(())
51641d668b4SAndrew Brown     }
51741d668b4SAndrew Brown 
execute(&mut self) -> Result<()>518cc81570aSNick Fitzgerald     fn execute(&mut self) -> Result<()> {
5197a1b7cdfSAlex Crichton         let (mut store, instance) = self
5207a1b7cdfSAlex Crichton             .store_and_instance
52118fabd77SNick Fitzgerald             .take()
522cc81570aSNick Fitzgerald             .expect("instantiate the module before executing it");
523cc81570aSNick Fitzgerald 
5241bd78f1aSTimothy Chen         let start_func = instance.get_typed_func::<(), ()>(&mut store, "_start")?;
5257a1b7cdfSAlex Crichton         match start_func.call(&mut store, ()) {
526cc81570aSNick Fitzgerald             Ok(_) => Ok(()),
52741d668b4SAndrew Brown             Err(trap) => {
52841d668b4SAndrew Brown                 // Since _start will likely return by using the system `exit` call, we must
52941d668b4SAndrew Brown                 // check the trap code to see if it actually represents a successful exit.
5302afaac51SAlex Crichton                 if let Some(exit) = trap.downcast_ref::<I32Exit>() {
5312afaac51SAlex Crichton                     if exit.0 == 0 {
5322afaac51SAlex Crichton                         return Ok(());
53341d668b4SAndrew Brown                     }
53441d668b4SAndrew Brown                 }
5352afaac51SAlex Crichton 
5362afaac51SAlex Crichton                 Err(trap)
5372afaac51SAlex Crichton             }
53841d668b4SAndrew Brown         }
53941d668b4SAndrew Brown     }
54041d668b4SAndrew Brown }
541