2121//! # Example
2222//!
2323//! ```
24+ //! use std::ptr;
2425//! use wasmtime_bench_api::*;
2526//!
26- //! let engine = unsafe { wasm_bench_create() };
27- //! assert!(!engine.is_null());
27+ //! let working_dir = std::env::current_dir().unwrap().display().to_string();
28+ //! let mut bench_api = ptr::null_mut();
29+ //! unsafe {
30+ //! let code = wasm_bench_create(working_dir.as_ptr(), working_dir.len(), &mut bench_api);
31+ //! assert_eq!(code, OK);
32+ //! assert!(!bench_api.is_null());
33+ //! };
2834//!
2935//! let wasm = wat::parse_bytes(br#"
3036//! (module
4248//! "#).unwrap();
4349//!
4450//! // Start your compilation timer here.
45- //! let code = unsafe { wasm_bench_compile(engine , wasm.as_ptr(), wasm.len()) };
51+ //! let code = unsafe { wasm_bench_compile(bench_api , wasm.as_ptr(), wasm.len()) };
4652//! // End your compilation timer here.
4753//! assert_eq!(code, OK);
4854//!
5763//! }
5864//!
5965//! // Start your instantiation timer here.
60- //! let code = unsafe { wasm_bench_instantiate(engine , bench_start, bench_stop) };
66+ //! let code = unsafe { wasm_bench_instantiate(bench_api , bench_start, bench_stop) };
6167//! // End your instantiation timer here.
6268//! assert_eq!(code, OK);
6369//!
6470//! // No need to start timers for the execution since, by convention, the timer
6571//! // functions we passed during instantiation will be called by the benchmark
6672//! // at the appropriate time (before and after the benchmarked section).
67- //! let code = unsafe { wasm_bench_execute(engine ) };
73+ //! let code = unsafe { wasm_bench_execute(bench_api ) };
6874//! assert_eq!(code, OK);
6975//!
7076//! unsafe {
71- //! wasm_bench_free(engine );
77+ //! wasm_bench_free(bench_api );
7278//! }
7379//! ```
7480
7581use anyhow:: { anyhow, Context , Result } ;
7682use std:: env;
7783use std:: os:: raw:: { c_int, c_void} ;
84+ use std:: path:: Path ;
7885use std:: slice;
7986use wasi_common:: WasiCtxBuilder ;
8087use wasmtime:: { Config , Engine , Instance , Linker , Module , Store } ;
@@ -95,12 +102,32 @@ static ALLOC: shuffling_allocator::ShufflingAllocator<std::alloc::System> =
95102/// Exposes a C-compatible way of creating the engine from the bytes of a single
96103/// Wasm module.
97104///
98- /// This function returns a pointer to a structure that contains the engine's
99- /// initialized state.
105+ /// On success, the `out_bench_ptr` is initialized to a pointer to a structure
106+ /// that contains the engine's initialized state, and `0` is returned. On
107+ /// failure, a non-zero status code is returned and `out_bench_ptr` is left
108+ /// untouched.
100109#[ no_mangle]
101- pub extern "C" fn wasm_bench_create ( ) -> * mut c_void {
102- let state = Box :: new ( BenchState :: new ( ) ) ;
103- Box :: into_raw ( state) as _
110+ pub extern "C" fn wasm_bench_create (
111+ working_dir_ptr : * const u8 ,
112+ working_dir_len : usize ,
113+ out_bench_ptr : * mut * mut c_void ,
114+ ) -> ExitCode {
115+ let result = ( || -> Result < _ > {
116+ let working_dir = unsafe { std:: slice:: from_raw_parts ( working_dir_ptr, working_dir_len) } ;
117+ let working_dir = std:: str:: from_utf8 ( working_dir)
118+ . context ( "given working directory is not valid UTF-8" ) ?;
119+ let state = Box :: new ( BenchState :: new ( working_dir) ?) ;
120+ Ok ( Box :: into_raw ( state) as _ )
121+ } ) ( ) ;
122+
123+ if let Ok ( bench_ptr) = result {
124+ unsafe {
125+ assert ! ( !out_bench_ptr. is_null( ) ) ;
126+ * out_bench_ptr = bench_ptr;
127+ }
128+ }
129+
130+ to_exit_code ( result. map ( |_| ( ) ) )
104131}
105132
106133/// Free the engine state allocated by this library.
@@ -164,27 +191,49 @@ fn to_exit_code<T>(result: impl Into<Result<T>>) -> ExitCode {
164191/// to manage the Wasmtime engine between calls.
165192struct BenchState {
166193 engine : Engine ,
167- store : Store ,
194+ linker : Linker ,
168195 module : Option < Module > ,
169196 instance : Option < Instance > ,
170197 did_execute : bool ,
171198}
172199
173200impl BenchState {
174- fn new ( ) -> Self {
201+ fn new ( working_dir : impl AsRef < Path > ) -> Result < Self > {
175202 let mut config = Config :: new ( ) ;
176203 config. wasm_simd ( true ) ;
177204 // NB: do not configure a code cache.
178205
179206 let engine = Engine :: new ( & config) ;
180207 let store = Store :: new ( & engine) ;
181- Self {
208+
209+ let mut linker = Linker :: new ( & store) ;
210+
211+ // Create a WASI environment.
212+
213+ let mut cx = WasiCtxBuilder :: new ( ) ;
214+ cx. inherit_stdio ( ) ;
215+ // Allow access to the working directory so that the benchmark can read
216+ // its input workload(s).
217+ let working_dir = wasi_common:: preopen_dir ( working_dir)
218+ . context ( "failed to preopen the working directory" ) ?;
219+ cx. preopened_dir ( working_dir, "." ) ;
220+ // Pass this env var along so that the benchmark program can use smaller
221+ // input workload(s) if it has them and that has been requested.
222+ if let Ok ( val) = env:: var ( "WASM_BENCH_USE_SMALL_WORKLOAD" ) {
223+ cx. env ( "WASM_BENCH_USE_SMALL_WORKLOAD" , & val) ;
224+ }
225+
226+ let cx = cx. build ( ) ?;
227+ let wasi = Wasi :: new ( & store, cx) ;
228+ wasi. add_to_linker ( & mut linker) ?;
229+
230+ Ok ( Self {
182231 engine,
183- store ,
232+ linker ,
184233 module : None ,
185234 instance : None ,
186235 did_execute : false ,
187- }
236+ } )
188237 }
189238
190239 fn compile ( & mut self , bytes : & [ u8 ] ) -> Result < ( ) > {
@@ -210,34 +259,11 @@ impl BenchState {
210259 . as_mut ( )
211260 . expect ( "compile the module before instantiating it" ) ;
212261
213- let mut linker = Linker :: new ( & self . store ) ;
214-
215- // Create a WASI environment.
216-
217- let mut cx = WasiCtxBuilder :: new ( ) ;
218- cx. inherit_stdio ( ) ;
219- // Allow access to the current working directory so that the benchmark
220- // can read its input workload(s). The sightglass benchmark runner will
221- // make sure that this process is spawned with its current directory set
222- // to the current benchmark's directory.
223- let cwd = wasi_common:: preopen_dir ( "." )
224- . context ( "failed to open the current working directory" ) ?;
225- cx. preopened_dir ( cwd, "." ) ;
226- // Pass this env var along so that the benchmark program can use smaller
227- // input workload(s) if it has them and that has been requested.
228- if let Ok ( val) = env:: var ( "WASM_BENCH_USE_SMALL_WORKLOAD" ) {
229- cx. env ( "WASM_BENCH_USE_SMALL_WORKLOAD" , & val) ;
230- }
231-
232- let cx = cx. build ( ) ?;
233- let wasi = Wasi :: new ( linker. store ( ) , cx) ;
234- wasi. add_to_linker ( & mut linker) ?;
235-
236262 // Import the specialized benchmarking functions.
237- linker. func ( "bench" , "start" , move || bench_start ( ) ) ?;
238- linker. func ( "bench" , "end" , move || bench_end ( ) ) ?;
263+ self . linker . func ( "bench" , "start" , move || bench_start ( ) ) ?;
264+ self . linker . func ( "bench" , "end" , move || bench_end ( ) ) ?;
239265
240- self . instance = Some ( linker. instantiate ( & module) ?) ;
266+ self . instance = Some ( self . linker . instantiate ( & module) ?) ;
241267 Ok ( ( ) )
242268 }
243269
0 commit comments