bench-api: receive working directory as an argument

Rather than implicitly use the current working directory.
This commit is contained in:
Nick Fitzgerald
2021-01-27 15:20:43 -08:00
committed by Andrew Brown
parent be1ac15beb
commit d1c1cb6a25

View File

@@ -21,10 +21,16 @@
//! # Example //! # Example
//! //!
//! ``` //! ```
//! use std::ptr;
//! use wasmtime_bench_api::*; //! use wasmtime_bench_api::*;
//! //!
//! let engine = unsafe { wasm_bench_create() }; //! let working_dir = std::env::current_dir().unwrap().display().to_string();
//! assert!(!engine.is_null()); //! let mut bench_api = ptr::null_mut();
//! unsafe {
//! let code = wasm_bench_create(working_dir.as_ptr(), working_dir.len(), &mut bench_api);
//! assert_eq!(code, OK);
//! assert!(!bench_api.is_null());
//! };
//! //!
//! let wasm = wat::parse_bytes(br#" //! let wasm = wat::parse_bytes(br#"
//! (module //! (module
@@ -42,7 +48,7 @@
//! "#).unwrap(); //! "#).unwrap();
//! //!
//! // Start your compilation timer here. //! // Start your compilation timer here.
//! let code = unsafe { wasm_bench_compile(engine, wasm.as_ptr(), wasm.len()) }; //! let code = unsafe { wasm_bench_compile(bench_api, wasm.as_ptr(), wasm.len()) };
//! // End your compilation timer here. //! // End your compilation timer here.
//! assert_eq!(code, OK); //! assert_eq!(code, OK);
//! //!
@@ -57,24 +63,25 @@
//! } //! }
//! //!
//! // Start your instantiation timer here. //! // Start your instantiation timer here.
//! let code = unsafe { wasm_bench_instantiate(engine, bench_start, bench_stop) }; //! let code = unsafe { wasm_bench_instantiate(bench_api, bench_start, bench_stop) };
//! // End your instantiation timer here. //! // End your instantiation timer here.
//! assert_eq!(code, OK); //! assert_eq!(code, OK);
//! //!
//! // No need to start timers for the execution since, by convention, the timer //! // No need to start timers for the execution since, by convention, the timer
//! // functions we passed during instantiation will be called by the benchmark //! // functions we passed during instantiation will be called by the benchmark
//! // at the appropriate time (before and after the benchmarked section). //! // at the appropriate time (before and after the benchmarked section).
//! let code = unsafe { wasm_bench_execute(engine) }; //! let code = unsafe { wasm_bench_execute(bench_api) };
//! assert_eq!(code, OK); //! assert_eq!(code, OK);
//! //!
//! unsafe { //! unsafe {
//! wasm_bench_free(engine); //! wasm_bench_free(bench_api);
//! } //! }
//! ``` //! ```
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use std::env; use std::env;
use std::os::raw::{c_int, c_void}; use std::os::raw::{c_int, c_void};
use std::path::Path;
use std::slice; use std::slice;
use wasi_common::WasiCtxBuilder; use wasi_common::WasiCtxBuilder;
use wasmtime::{Config, Engine, Instance, Linker, Module, Store}; use wasmtime::{Config, Engine, Instance, Linker, Module, Store};
@@ -95,12 +102,32 @@ static ALLOC: shuffling_allocator::ShufflingAllocator<std::alloc::System> =
/// Exposes a C-compatible way of creating the engine from the bytes of a single /// Exposes a C-compatible way of creating the engine from the bytes of a single
/// Wasm module. /// Wasm module.
/// ///
/// This function returns a pointer to a structure that contains the engine's /// On success, the `out_bench_ptr` is initialized to a pointer to a structure
/// initialized state. /// that contains the engine's initialized state, and `0` is returned. On
/// failure, a non-zero status code is returned and `out_bench_ptr` is left
/// untouched.
#[no_mangle] #[no_mangle]
pub extern "C" fn wasm_bench_create() -> *mut c_void { pub extern "C" fn wasm_bench_create(
let state = Box::new(BenchState::new()); working_dir_ptr: *const u8,
Box::into_raw(state) as _ working_dir_len: usize,
out_bench_ptr: *mut *mut c_void,
) -> ExitCode {
let result = (|| -> Result<_> {
let working_dir = unsafe { std::slice::from_raw_parts(working_dir_ptr, working_dir_len) };
let working_dir = std::str::from_utf8(working_dir)
.context("given working directory is not valid UTF-8")?;
let state = Box::new(BenchState::new(working_dir)?);
Ok(Box::into_raw(state) as _)
})();
if let Ok(bench_ptr) = result {
unsafe {
assert!(!out_bench_ptr.is_null());
*out_bench_ptr = bench_ptr;
}
}
to_exit_code(result.map(|_| ()))
} }
/// Free the engine state allocated by this library. /// Free the engine state allocated by this library.
@@ -164,27 +191,49 @@ fn to_exit_code<T>(result: impl Into<Result<T>>) -> ExitCode {
/// to manage the Wasmtime engine between calls. /// to manage the Wasmtime engine between calls.
struct BenchState { struct BenchState {
engine: Engine, engine: Engine,
store: Store, linker: Linker,
module: Option<Module>, module: Option<Module>,
instance: Option<Instance>, instance: Option<Instance>,
did_execute: bool, did_execute: bool,
} }
impl BenchState { impl BenchState {
fn new() -> Self { fn new(working_dir: impl AsRef<Path>) -> Result<Self> {
let mut config = Config::new(); let mut config = Config::new();
config.wasm_simd(true); config.wasm_simd(true);
// NB: do not configure a code cache. // NB: do not configure a code cache.
let engine = Engine::new(&config); let engine = Engine::new(&config);
let store = Store::new(&engine); let store = Store::new(&engine);
Self {
let mut linker = Linker::new(&store);
// Create a WASI environment.
let mut cx = WasiCtxBuilder::new();
cx.inherit_stdio();
// Allow access to the working directory so that the benchmark can read
// its input workload(s).
let working_dir = wasi_common::preopen_dir(working_dir)
.context("failed to preopen the working directory")?;
cx.preopened_dir(working_dir, ".");
// Pass this env var along so that the benchmark program can use smaller
// input workload(s) if it has them and that has been requested.
if let Ok(val) = env::var("WASM_BENCH_USE_SMALL_WORKLOAD") {
cx.env("WASM_BENCH_USE_SMALL_WORKLOAD", &val);
}
let cx = cx.build()?;
let wasi = Wasi::new(&store, cx);
wasi.add_to_linker(&mut linker)?;
Ok(Self {
engine, engine,
store, linker,
module: None, module: None,
instance: None, instance: None,
did_execute: false, did_execute: false,
} })
} }
fn compile(&mut self, bytes: &[u8]) -> Result<()> { fn compile(&mut self, bytes: &[u8]) -> Result<()> {
@@ -210,34 +259,11 @@ impl BenchState {
.as_mut() .as_mut()
.expect("compile the module before instantiating it"); .expect("compile the module before instantiating it");
let mut linker = Linker::new(&self.store);
// Create a WASI environment.
let mut cx = WasiCtxBuilder::new();
cx.inherit_stdio();
// Allow access to the current working directory so that the benchmark
// can read its input workload(s). The sightglass benchmark runner will
// make sure that this process is spawned with its current directory set
// to the current benchmark's directory.
let cwd = wasi_common::preopen_dir(".")
.context("failed to open the current working directory")?;
cx.preopened_dir(cwd, ".");
// Pass this env var along so that the benchmark program can use smaller
// input workload(s) if it has them and that has been requested.
if let Ok(val) = env::var("WASM_BENCH_USE_SMALL_WORKLOAD") {
cx.env("WASM_BENCH_USE_SMALL_WORKLOAD", &val);
}
let cx = cx.build()?;
let wasi = Wasi::new(linker.store(), cx);
wasi.add_to_linker(&mut linker)?;
// Import the specialized benchmarking functions. // Import the specialized benchmarking functions.
linker.func("bench", "start", move || bench_start())?; self.linker.func("bench", "start", move || bench_start())?;
linker.func("bench", "end", move || bench_end())?; self.linker.func("bench", "end", move || bench_end())?;
self.instance = Some(linker.instantiate(&module)?); self.instance = Some(self.linker.instantiate(&module)?);
Ok(()) Ok(())
} }