diff --git a/wasmtime-environ/build.rs b/wasmtime-environ/build.rs new file mode 100644 index 00000000000..21082e31bc4 --- /dev/null +++ b/wasmtime-environ/build.rs @@ -0,0 +1,9 @@ +use std::process::Command; + +fn main() { + let git_rev = match Command::new("git").args(&["rev-parse", "HEAD"]).output() { + Ok(output) => String::from_utf8(output.stdout).unwrap(), + Err(_) => String::from("git-not-found"), + }; + println!("cargo:rustc-env=GIT_REV={}", git_rev); +} diff --git a/wasmtime-environ/src/cache.rs b/wasmtime-environ/src/cache.rs index 3c934794e2a..7e8ebb92b87 100644 --- a/wasmtime-environ/src/cache.rs +++ b/wasmtime-environ/src/cache.rs @@ -5,17 +5,15 @@ use cranelift_codegen::ir; use cranelift_codegen::isa; use directories::ProjectDirs; use lazy_static::lazy_static; -use log::warn; +use log::{debug, warn}; use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; use serde::ser::{self, Serialize, SerializeSeq, SerializeStruct, Serializer}; -#[cfg(windows)] use std::ffi::OsString; use std::fmt; use std::fs; use std::io; -#[cfg(windows)] -use std::path::Path; use std::path::PathBuf; +use std::string::{String, ToString}; /// Module for configuring the cache system. pub mod conf { @@ -48,29 +46,47 @@ lazy_static! { Some(proj_dirs) => { let cache_dir = proj_dirs.cache_dir(); // Temporary workaround for: https://github.com/rust-lang/rust/issues/32689 - #[cfg(windows)] - let mut long_path = OsString::from("\\\\?\\"); - #[cfg(windows)] - let cache_dir = { - if cache_dir.starts_with("\\\\?\\") { - cache_dir + if cfg!(windows) { + let mut long_path = OsString::new(); + if !cache_dir.starts_with("\\\\?\\") { + long_path.push("\\\\?\\"); } - else { - long_path.push(cache_dir.as_os_str()); - Path::new(&long_path) - } - }; - match fs::create_dir_all(cache_dir) { - Ok(()) => (), - Err(err) => warn!("Unable to create cache directory, failed with: {}", err), - }; - Some(cache_dir.to_path_buf()) + long_path.push(cache_dir.as_os_str()); + Some(PathBuf::from(long_path)) + } + else { + Some(cache_dir.to_path_buf()) + } } None => { - warn!("Unable to find cache directory"); + warn!("Failed to find proper cache directory location."); None } }; + static ref SELF_MTIME: String = { + match std::env::current_exe() { + Ok(path) => match fs::metadata(&path) { + Ok(metadata) => match metadata.modified() { + Ok(mtime) => match mtime.duration_since(std::time::UNIX_EPOCH) { + Ok(duration) => format!("{}", duration.as_millis()), + Err(err) => format!("m{}", err.duration().as_millis()), + }, + Err(_) => { + warn!("Failed to get modification time of current executable"); + "no-mtime".to_string() + } + }, + Err(_) => { + warn!("Failed to get metadata of current executable"); + "no-mtime".to_string() + } + }, + Err(_) => { + warn!("Failed to get path of current executable"); + "no-mtime".to_string() + } + } + }; } pub struct ModuleCacheEntry { @@ -87,14 +103,33 @@ pub struct ModuleCacheData { type ModuleCacheDataTupleType = (Compilation, Relocations, ModuleAddressMap); impl ModuleCacheEntry { - pub fn new(module: &Module, _isa: &dyn isa::TargetIsa, _generate_debug_info: bool) -> Self { - // TODO: cache directory hierarchy with isa name, compiler name & git revision, and files with flag if debug symbols are available + pub fn new( + module: &Module, + isa: &dyn isa::TargetIsa, + compiler_name: &str, + generate_debug_info: bool, + ) -> Self { let mod_cache_path = if conf::cache_enabled() { CACHE_DIR.clone().and_then(|p| { module.hash.map(|hash| { - p.join(format!( - "mod-{}", - base64::encode_config(&hash, base64::URL_SAFE_NO_PAD) // standard encoding uses '/' which can't be used for filename + let compiler_dir = if cfg!(debug_assertions) { + format!( + "{comp_name}-{comp_ver}-{comp_mtime}", + comp_name = compiler_name, + comp_ver = env!("GIT_REV"), + comp_mtime = *SELF_MTIME, + ) + } else { + format!( + "{comp_name}-{comp_ver}", + comp_name = compiler_name, + comp_ver = env!("GIT_REV"), + ) + }; + p.join(isa.name()).join(compiler_dir).join(format!( + "mod-{mod_hash}{mod_dbg}", + mod_hash = base64::encode_config(&hash, base64::URL_SAFE_NO_PAD), // standard encoding uses '/' which can't be used for filename + mod_dbg = if generate_debug_info { ".d" } else { "" }, )) }) }) @@ -131,25 +166,46 @@ impl ModuleCacheEntry { return; } }; + // Optimize syscalls: first, try writing to disk. It should succeed in most cases. + // Otherwise, try creating the cache directory and retry writing to the file. match fs::write(p, &cache_buf) { Ok(()) => (), Err(err) => { - warn!( - "Failed to write cached code to disk, path: {}, message: {}", + debug!( + "Attempting to create the cache directory, because \ + failed to write cached code to disk, path: {}, message: {}", p.display(), - err + err, ); - match fs::remove_file(p) { - Ok(()) => (), - Err(err) => { - if err.kind() != io::ErrorKind::NotFound { + let cache_dir = p.parent().unwrap(); + match fs::create_dir_all(cache_dir) { + Ok(()) => match fs::write(p, &cache_buf) { + Ok(()) => (), + Err(err) => { warn!( - "Failed to cleanup invalid cache, path: {}, message: {}", + "Failed to write cached code to disk, path: {}, message: {}", p.display(), err ); + match fs::remove_file(p) { + Ok(()) => (), + Err(err) => { + if err.kind() != io::ErrorKind::NotFound { + warn!( + "Failed to cleanup invalid cache, path: {}, message: {}", + p.display(), + err + ); + } + } + } } - } + }, + Err(err) => warn!( + "Failed to create cache directory, path: {}, message: {}", + cache_dir.display(), + err + ), } } } diff --git a/wasmtime-environ/src/cranelift.rs b/wasmtime-environ/src/cranelift.rs index 508a50bad7e..83e1734f85d 100644 --- a/wasmtime-environ/src/cranelift.rs +++ b/wasmtime-environ/src/cranelift.rs @@ -124,7 +124,7 @@ impl crate::compilation::Compiler for Cranelift { isa: &dyn isa::TargetIsa, generate_debug_info: bool, ) -> Result<(Compilation, Relocations, ModuleAddressMap), CompileError> { - let cache_entry = ModuleCacheEntry::new(module, isa, generate_debug_info); + let cache_entry = ModuleCacheEntry::new(module, isa, "cranelift", generate_debug_info); let data = match cache_entry.get_data() { Some(data) => data,