Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache directory hierarchy #217

Merged
merged 11 commits into from Jul 26, 2019
9 changes: 9 additions & 0 deletions 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);
}
126 changes: 91 additions & 35 deletions wasmtime-environ/src/cache.rs
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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 { "" },
))
})
})
Expand Down Expand Up @@ -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
),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion wasmtime-environ/src/cranelift.rs
Expand Up @@ -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,
Expand Down