Cache directory hierarchy (#217)
* Simple module compilation cache * Fix base64 encoding bug * Use warn! everywhere in cache system * Remove unused import * Temporary workaround for long path on Windows * Remove unused import for non-windows builds * Cache directory hierarchy * Fix conditional compilation for debug mode * Minor enhancements
This commit is contained in:
9
wasmtime-environ/build.rs
Normal file
9
wasmtime-environ/build.rs
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user