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:
Artur Jamro
2019-07-26 07:24:58 -07:00
committed by Dan Gohman
parent 165dc4944d
commit b7d86af0ec
3 changed files with 101 additions and 36 deletions

View 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);
}

View File

@@ -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
),
}
}
}

View File

@@ -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,