diff --git a/CHANGELOG.md b/CHANGELOG.md index 514bc58b14d..67d4cec641d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,10 @@ All PRs to the Wasmer repository must add to this file. Blocks of changes will separated by version increments. ## **[Unreleased]** -## 0.5.4 +- [#537](https://github.com/wasmerio/wasmer/pull/537) Add hidden flag (`--cache-key`) to use prehashed key into the compiled wasm cache - [#536](https://github.com/wasmerio/wasmer/pull/536) Update cache to use compiler backend name in cache key + +## 0.5.4 - [#529](https://github.com/wasmerio/wasmer/pull/529) Updates the Wasm Interface library, which is used by wapm, with bug fixes and error message improvements ## 0.5.3 diff --git a/lib/runtime-core/src/cache.rs b/lib/runtime-core/src/cache.rs index 8f1c82db2b4..0a1c5465618 100644 --- a/lib/runtime-core/src/cache.rs +++ b/lib/runtime-core/src/cache.rs @@ -77,6 +77,30 @@ impl WasmHash { hex::encode(&self.into_array() as &[u8]) } + /// Create hash from hexadecimal representation + pub fn decode(hex_str: &str) -> Result { + let bytes = hex::decode(hex_str).map_err(|e| { + Error::DeserializeError(format!( + "Could not decode prehashed key as hexadecimal: {}", + e + )) + })?; + if bytes.len() != 64 { + return Err(Error::DeserializeError( + "Prehashed keys must deserialze into exactly 64 bytes".to_string(), + )); + } + use std::convert::TryInto; + Ok(WasmHash( + bytes[0..32].try_into().map_err(|e| { + Error::DeserializeError(format!("Could not get first 32 bytes: {}", e)) + })?, + bytes[32..64].try_into().map_err(|e| { + Error::DeserializeError(format!("Could not get last 32 bytes: {}", e)) + })?, + )) + } + pub(crate) fn into_array(self) -> [u8; 64] { let mut total = [0u8; 64]; total[0..32].copy_from_slice(&self.0); diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 297d0199b78..978033bcc82 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -24,6 +24,7 @@ use wasmer_runtime::{ use wasmer_runtime_core::{ self, backend::{Backend, Compiler, CompilerConfig, MemoryBoundCheckMode}, + debug, loader::{Instance as LoadedInstance, LocalLoader}, }; #[cfg(feature = "backend:singlepass")] @@ -115,9 +116,18 @@ struct Run { #[structopt(long = "resume")] resume: Option, + /// The command name is a string that will override the first argument passed + /// to the wasm program. This is used in wapm to provide nicer output in + /// help commands and error messages of the running wasm program #[structopt(long = "command-name", hidden = true)] command_name: Option, + /// A prehashed string, used to speed up start times by avoiding hashing the + /// wasm module. If the specified hash is not found, Wasmer will hash the module + /// as if no `cache-key` argument was passed. + #[structopt(long = "cache-key", hidden = true)] + cache_key: Option, + /// Application arguments #[structopt(name = "--", raw(multiple = "true"))] args: Vec, @@ -350,10 +360,6 @@ fn execute_wasm(options: &Run) -> Result<(), String> { } else { // If we have cache enabled - // We generate a hash for the given binary, so we can use it as key - // for the Filesystem cache - let hash = WasmHash::generate_for_backend(&wasm_binary, options.backend); - let wasmer_cache_dir = get_cache_dir(); // We create a new cache instance. @@ -362,31 +368,47 @@ fn execute_wasm(options: &Run) -> Result<(), String> { let mut cache = unsafe { FileSystemCache::new(wasmer_cache_dir).map_err(|e| format!("Cache error: {:?}", e))? }; - - // cache.load will return the Module if it's able to deserialize it properly, and an error if: - // * The file is not found - // * The file exists, but it's corrupted or can't be converted to a module - match cache.load(hash) { - Ok(module) => { - // We are able to load the module from cache - module + let load_cache_key = || -> Result<_, String> { + if let Some(ref prehashed_cache_key) = options.cache_key { + if let Ok(module) = WasmHash::decode(prehashed_cache_key) + .and_then(|prehashed_key| cache.load(prehashed_key)) + { + debug!("using prehashed key: {}", prehashed_cache_key); + return Ok(module); + } } - Err(_) => { - let module = webassembly::compile_with_config_with( - &wasm_binary[..], - CompilerConfig { - symbol_map: em_symbol_map, - ..Default::default() - }, - &*compiler, - ) - .map_err(|e| format!("Can't compile module: {:?}", e))?; - // We try to save the module into a cache file - cache.store(hash, module.clone()).unwrap_or_default(); - module + // We generate a hash for the given binary, so we can use it as key + // for the Filesystem cache + let hash = WasmHash::generate_for_backend(&wasm_binary, options.backend); + + // cache.load will return the Module if it's able to deserialize it properly, and an error if: + // * The file is not found + // * The file exists, but it's corrupted or can't be converted to a module + match cache.load(hash) { + Ok(module) => { + // We are able to load the module from cache + Ok(module) + } + Err(_) => { + let module = webassembly::compile_with_config_with( + &wasm_binary[..], + CompilerConfig { + symbol_map: em_symbol_map, + ..Default::default() + }, + &*compiler, + ) + .map_err(|e| format!("Can't compile module: {:?}", e))?; + // We try to save the module into a cache file + cache.store(hash, module.clone()).unwrap_or_default(); + + Ok(module) + } } - } + }; + + load_cache_key()? }; if let Some(loader) = options.loader {