Skip to content

Commit b7d86af

Browse files
mrowqasunfishcode
authored andcommitted
Cache directory hierarchy (bytecodealliance#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
1 parent 165dc49 commit b7d86af

File tree

3 files changed

+101
-36
lines changed

3 files changed

+101
-36
lines changed

wasmtime-environ/build.rs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use std::process::Command;
2+
3+
fn main() {
4+
let git_rev = match Command::new("git").args(&["rev-parse", "HEAD"]).output() {
5+
Ok(output) => String::from_utf8(output.stdout).unwrap(),
6+
Err(_) => String::from("git-not-found"),
7+
};
8+
println!("cargo:rustc-env=GIT_REV={}", git_rev);
9+
}

wasmtime-environ/src/cache.rs

+91-35
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,15 @@ use cranelift_codegen::ir;
55
use cranelift_codegen::isa;
66
use directories::ProjectDirs;
77
use lazy_static::lazy_static;
8-
use log::warn;
8+
use log::{debug, warn};
99
use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
1010
use serde::ser::{self, Serialize, SerializeSeq, SerializeStruct, Serializer};
11-
#[cfg(windows)]
1211
use std::ffi::OsString;
1312
use std::fmt;
1413
use std::fs;
1514
use std::io;
16-
#[cfg(windows)]
17-
use std::path::Path;
1815
use std::path::PathBuf;
16+
use std::string::{String, ToString};
1917

2018
/// Module for configuring the cache system.
2119
pub mod conf {
@@ -48,29 +46,47 @@ lazy_static! {
4846
Some(proj_dirs) => {
4947
let cache_dir = proj_dirs.cache_dir();
5048
// Temporary workaround for: https://github.com/rust-lang/rust/issues/32689
51-
#[cfg(windows)]
52-
let mut long_path = OsString::from("\\\\?\\");
53-
#[cfg(windows)]
54-
let cache_dir = {
55-
if cache_dir.starts_with("\\\\?\\") {
56-
cache_dir
49+
if cfg!(windows) {
50+
let mut long_path = OsString::new();
51+
if !cache_dir.starts_with("\\\\?\\") {
52+
long_path.push("\\\\?\\");
5753
}
58-
else {
59-
long_path.push(cache_dir.as_os_str());
60-
Path::new(&long_path)
61-
}
62-
};
63-
match fs::create_dir_all(cache_dir) {
64-
Ok(()) => (),
65-
Err(err) => warn!("Unable to create cache directory, failed with: {}", err),
66-
};
67-
Some(cache_dir.to_path_buf())
54+
long_path.push(cache_dir.as_os_str());
55+
Some(PathBuf::from(long_path))
56+
}
57+
else {
58+
Some(cache_dir.to_path_buf())
59+
}
6860
}
6961
None => {
70-
warn!("Unable to find cache directory");
62+
warn!("Failed to find proper cache directory location.");
7163
None
7264
}
7365
};
66+
static ref SELF_MTIME: String = {
67+
match std::env::current_exe() {
68+
Ok(path) => match fs::metadata(&path) {
69+
Ok(metadata) => match metadata.modified() {
70+
Ok(mtime) => match mtime.duration_since(std::time::UNIX_EPOCH) {
71+
Ok(duration) => format!("{}", duration.as_millis()),
72+
Err(err) => format!("m{}", err.duration().as_millis()),
73+
},
74+
Err(_) => {
75+
warn!("Failed to get modification time of current executable");
76+
"no-mtime".to_string()
77+
}
78+
},
79+
Err(_) => {
80+
warn!("Failed to get metadata of current executable");
81+
"no-mtime".to_string()
82+
}
83+
},
84+
Err(_) => {
85+
warn!("Failed to get path of current executable");
86+
"no-mtime".to_string()
87+
}
88+
}
89+
};
7490
}
7591

7692
pub struct ModuleCacheEntry {
@@ -87,14 +103,33 @@ pub struct ModuleCacheData {
87103
type ModuleCacheDataTupleType = (Compilation, Relocations, ModuleAddressMap);
88104

89105
impl ModuleCacheEntry {
90-
pub fn new(module: &Module, _isa: &dyn isa::TargetIsa, _generate_debug_info: bool) -> Self {
91-
// TODO: cache directory hierarchy with isa name, compiler name & git revision, and files with flag if debug symbols are available
106+
pub fn new(
107+
module: &Module,
108+
isa: &dyn isa::TargetIsa,
109+
compiler_name: &str,
110+
generate_debug_info: bool,
111+
) -> Self {
92112
let mod_cache_path = if conf::cache_enabled() {
93113
CACHE_DIR.clone().and_then(|p| {
94114
module.hash.map(|hash| {
95-
p.join(format!(
96-
"mod-{}",
97-
base64::encode_config(&hash, base64::URL_SAFE_NO_PAD) // standard encoding uses '/' which can't be used for filename
115+
let compiler_dir = if cfg!(debug_assertions) {
116+
format!(
117+
"{comp_name}-{comp_ver}-{comp_mtime}",
118+
comp_name = compiler_name,
119+
comp_ver = env!("GIT_REV"),
120+
comp_mtime = *SELF_MTIME,
121+
)
122+
} else {
123+
format!(
124+
"{comp_name}-{comp_ver}",
125+
comp_name = compiler_name,
126+
comp_ver = env!("GIT_REV"),
127+
)
128+
};
129+
p.join(isa.name()).join(compiler_dir).join(format!(
130+
"mod-{mod_hash}{mod_dbg}",
131+
mod_hash = base64::encode_config(&hash, base64::URL_SAFE_NO_PAD), // standard encoding uses '/' which can't be used for filename
132+
mod_dbg = if generate_debug_info { ".d" } else { "" },
98133
))
99134
})
100135
})
@@ -131,25 +166,46 @@ impl ModuleCacheEntry {
131166
return;
132167
}
133168
};
169+
// Optimize syscalls: first, try writing to disk. It should succeed in most cases.
170+
// Otherwise, try creating the cache directory and retry writing to the file.
134171
match fs::write(p, &cache_buf) {
135172
Ok(()) => (),
136173
Err(err) => {
137-
warn!(
138-
"Failed to write cached code to disk, path: {}, message: {}",
174+
debug!(
175+
"Attempting to create the cache directory, because \
176+
failed to write cached code to disk, path: {}, message: {}",
139177
p.display(),
140-
err
178+
err,
141179
);
142-
match fs::remove_file(p) {
143-
Ok(()) => (),
144-
Err(err) => {
145-
if err.kind() != io::ErrorKind::NotFound {
180+
let cache_dir = p.parent().unwrap();
181+
match fs::create_dir_all(cache_dir) {
182+
Ok(()) => match fs::write(p, &cache_buf) {
183+
Ok(()) => (),
184+
Err(err) => {
146185
warn!(
147-
"Failed to cleanup invalid cache, path: {}, message: {}",
186+
"Failed to write cached code to disk, path: {}, message: {}",
148187
p.display(),
149188
err
150189
);
190+
match fs::remove_file(p) {
191+
Ok(()) => (),
192+
Err(err) => {
193+
if err.kind() != io::ErrorKind::NotFound {
194+
warn!(
195+
"Failed to cleanup invalid cache, path: {}, message: {}",
196+
p.display(),
197+
err
198+
);
199+
}
200+
}
201+
}
151202
}
152-
}
203+
},
204+
Err(err) => warn!(
205+
"Failed to create cache directory, path: {}, message: {}",
206+
cache_dir.display(),
207+
err
208+
),
153209
}
154210
}
155211
}

wasmtime-environ/src/cranelift.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ impl crate::compilation::Compiler for Cranelift {
124124
isa: &dyn isa::TargetIsa,
125125
generate_debug_info: bool,
126126
) -> Result<(Compilation, Relocations, ModuleAddressMap), CompileError> {
127-
let cache_entry = ModuleCacheEntry::new(module, isa, generate_debug_info);
127+
let cache_entry = ModuleCacheEntry::new(module, isa, "cranelift", generate_debug_info);
128128

129129
let data = match cache_entry.get_data() {
130130
Some(data) => data,

0 commit comments

Comments
 (0)