Cache config as a file

This commit is contained in:
Artur Jamro
2019-08-23 17:00:38 -07:00
committed by Dan Gohman
parent c42698dc85
commit 633dfa17ee
14 changed files with 530 additions and 201 deletions

View File

@@ -50,7 +50,7 @@ use std::str;
use std::str::FromStr; use std::str::FromStr;
use target_lexicon::Triple; use target_lexicon::Triple;
use wasmtime_debug::{emit_debugsections, read_debuginfo}; use wasmtime_debug::{emit_debugsections, read_debuginfo};
use wasmtime_environ::cache_conf; use wasmtime_environ::cache_config;
use wasmtime_environ::{ use wasmtime_environ::{
Compiler, Cranelift, ModuleEnvironment, ModuleVmctxInfo, Tunables, VMOffsets, Compiler, Cranelift, ModuleEnvironment, ModuleVmctxInfo, Tunables, VMOffsets,
}; };
@@ -63,7 +63,7 @@ The translation is dependent on the environment chosen.
The default is a dummy environment that produces placeholder values. The default is a dummy environment that produces placeholder values.
Usage: Usage:
wasm2obj [--target TARGET] [-dg] [--cache] [--cache-dir=<cache_dir>] [--cache-compression-level=<compr_level>] [--enable-simd] <file> -o <output> wasm2obj [--target TARGET] [-dg] [--cache | --cache-config=<cache_config_file>] [--create-cache-config] [--enable-simd] <file> -o <output>
wasm2obj --help | --version wasm2obj --help | --version
Options: Options:
@@ -71,11 +71,12 @@ Options:
-h, --help print this help message -h, --help print this help message
--target <TARGET> build for the target triple; default is the host machine --target <TARGET> build for the target triple; default is the host machine
-g generate debug information -g generate debug information
-c, --cache enable caching system, use default cache directory -c, --cache enable caching system, use default configuration
--cache-dir=<cache_dir> --cache-config=<cache_config_file>
enable caching system, use specified cache directory enable caching system, use specified cache configuration
--cache-compression-level=<compr_level> --create-cache-config
enable caching system, use custom compression level for new cache, values 1-21 used with --cache or --cache-config, creates default configuration and writes it to the disk,
will fail if specified file already exists (or default file if used with --cache)
--enable-simd enable proposed SIMD instructions --enable-simd enable proposed SIMD instructions
--version print the Cranelift version --version print the Cranelift version
-d, --debug enable debug output on stderr/stdout -d, --debug enable debug output on stderr/stdout
@@ -88,9 +89,9 @@ struct Args {
arg_target: Option<String>, arg_target: Option<String>,
flag_g: bool, flag_g: bool,
flag_debug: bool, flag_debug: bool,
flag_cache: bool, flag_cache: bool, // TODO change to disable cache after implementing cache eviction
flag_cache_dir: Option<String>, flag_cache_config_file: Option<String>,
flag_cache_compression_level: Option<i32>, flag_create_cache_config: bool,
flag_enable_simd: bool, flag_enable_simd: bool,
} }
@@ -117,14 +118,20 @@ fn main() {
wasmtime::init_file_per_thread_logger("wasm2obj.dbg."); wasmtime::init_file_per_thread_logger("wasm2obj.dbg.");
} }
cache_conf::init( let errors = cache_config::init(
args.flag_cache args.flag_cache || args.flag_cache_config_file.is_some(),
|| args.flag_cache_dir.is_some() args.flag_cache_config_file.as_ref(),
|| args.flag_cache_compression_level.is_some(), args.flag_create_cache_config,
args.flag_cache_dir.as_ref(),
args.flag_cache_compression_level,
); );
if !errors.is_empty() {
eprintln!("Cache initialization failed. Errors:");
for e in errors {
eprintln!("-> {}", e);
}
process::exit(1);
}
let path = Path::new(&args.arg_file); let path = Path::new(&args.arg_file);
match handle_module( match handle_module(
path.to_path_buf(), path.to_path_buf(),

View File

@@ -47,7 +47,7 @@ use std::rc::Rc;
use wabt; use wabt;
use wasi_common::preopen_dir; use wasi_common::preopen_dir;
use wasmtime_api::{Config, Engine, Instance, Module, Store}; use wasmtime_api::{Config, Engine, Instance, Module, Store};
use wasmtime_environ::cache_conf; use wasmtime_environ::cache_config;
use wasmtime_interface_types::ModuleData; use wasmtime_interface_types::ModuleData;
use wasmtime_jit::Features; use wasmtime_jit::Features;
use wasmtime_wasi::instantiate_wasi; use wasmtime_wasi::instantiate_wasi;
@@ -64,18 +64,19 @@ including calling the start function if one is present. Additional functions
given with --invoke are then called. given with --invoke are then called.
Usage: Usage:
wasmtime [-odg] [--enable-simd] [--wasi-c] [--cache] [--cache-dir=<cache_dir>] [--cache-compression-level=<compr_level>] [--preload=<wasm>...] [--env=<env>...] [--dir=<dir>...] [--mapdir=<mapping>...] <file> [<arg>...] wasmtime [-odg] [--enable-simd] [--wasi-c] [--cache | --cache-config=<cache_config_file>] [--create-cache-config] [--preload=<wasm>...] [--env=<env>...] [--dir=<dir>...] [--mapdir=<mapping>...] <file> [<arg>...]
wasmtime [-odg] [--enable-simd] [--wasi-c] [--cache] [--cache-dir=<cache_dir>] [--cache-compression-level=<compr_level>] [--env=<env>...] [--dir=<dir>...] [--mapdir=<mapping>...] --invoke=<fn> <file> [<arg>...] wasmtime [-odg] [--enable-simd] [--wasi-c] [--cache | --cache-config=<cache_config_file>] [--create-cache-config] [--env=<env>...] [--dir=<dir>...] [--mapdir=<mapping>...] --invoke=<fn> <file> [<arg>...]
wasmtime --help | --version wasmtime --help | --version
Options: Options:
--invoke=<fn> name of function to run --invoke=<fn> name of function to run
-o, --optimize runs optimization passes on the translated functions -o, --optimize runs optimization passes on the translated functions
-c, --cache enable caching system, use default cache directory -c, --cache enable caching system, use default configuration
--cache-dir=<cache_dir> --cache-config=<cache_config_file>
enable caching system, use specified cache directory enable caching system, use specified cache configuration
--cache-compression-level=<compr_level> --create-cache-config
enable caching system, use custom compression level for new cache, values 1-21 used with --cache or --cache-config, creates default configuration and writes it to the disk,
will fail if specified file already exists (or default file if used with --cache)
-g generate debug information -g generate debug information
-d, --debug enable debug output on stderr/stdout -d, --debug enable debug output on stderr/stdout
--enable-simd enable proposed SIMD instructions --enable-simd enable proposed SIMD instructions
@@ -94,9 +95,9 @@ struct Args {
arg_file: String, arg_file: String,
arg_arg: Vec<String>, arg_arg: Vec<String>,
flag_optimize: bool, flag_optimize: bool,
flag_cache: bool, flag_cache: bool, // TODO change to disable cache after implementing cache eviction
flag_cache_dir: Option<String>, flag_cache_config_file: Option<String>,
flag_cache_compression_level: Option<i32>, flag_create_cache_config: bool,
flag_debug: bool, flag_debug: bool,
flag_g: bool, flag_g: bool,
flag_enable_simd: bool, flag_enable_simd: bool,
@@ -201,7 +202,7 @@ fn main() {
for cause in err.iter_causes() { for cause in err.iter_causes() {
eprintln!(" caused by: {}", cause); eprintln!(" caused by: {}", cause);
} }
std::process::exit(1); exit(1);
} }
fn rmain() -> Result<(), Error> { fn rmain() -> Result<(), Error> {
@@ -220,14 +221,20 @@ fn rmain() -> Result<(), Error> {
wasmtime::init_file_per_thread_logger("wasmtime.dbg."); wasmtime::init_file_per_thread_logger("wasmtime.dbg.");
} }
cache_conf::init( let errors = cache_config::init(
args.flag_cache args.flag_cache || args.flag_cache_config_file.is_some(),
|| args.flag_cache_dir.is_some() args.flag_cache_config_file.as_ref(),
|| args.flag_cache_compression_level.is_some(), args.flag_create_cache_config,
args.flag_cache_dir.as_ref(),
args.flag_cache_compression_level,
); );
if !errors.is_empty() {
eprintln!("Cache initialization failed. Errors:");
for e in errors {
eprintln!("-> {}", e);
}
exit(1);
}
let mut flag_builder = settings::builder(); let mut flag_builder = settings::builder();
let mut features: Features = Default::default(); let mut features: Features = Default::default();

View File

@@ -33,7 +33,7 @@ use pretty_env_logger;
use serde::Deserialize; use serde::Deserialize;
use std::path::Path; use std::path::Path;
use std::process; use std::process;
use wasmtime_environ::cache_conf; use wasmtime_environ::cache_config;
use wasmtime_jit::{Compiler, Features}; use wasmtime_jit::{Compiler, Features};
use wasmtime_wast::WastContext; use wasmtime_wast::WastContext;
@@ -41,18 +41,19 @@ const USAGE: &str = "
Wast test runner. Wast test runner.
Usage: Usage:
wast [-do] [--enable-simd] [--cache] [--cache-dir=<cache_dir>] [--cache-compression-level=<compr_level>] <file>... wast [-do] [--enable-simd] [--cache | --cache-config=<cache_config_file>] [--create-cache-config] <file>...
wast --help | --version wast --help | --version
Options: Options:
-h, --help print this help message -h, --help print this help message
--version print the Cranelift version --version print the Cranelift version
-o, --optimize runs optimization passes on the translated functions -o, --optimize runs optimization passes on the translated functions
-c, --cache enable caching system, use default cache directory -c, --cache enable caching system, use default configuration
--cache-dir=<cache_dir> --cache-config=<cache_config_file>
enable caching system, use specified cache directory enable caching system, use specified cache configuration
--cache-compression-level=<compr_level> --create-cache-config
enable caching system, use custom compression level for new cache, values 1-21 used with --cache or --cache-config, creates default configuration and writes it to the disk,
will fail if specified file already exists (or default file if used with --cache)
-d, --debug enable debug output on stderr/stdout -d, --debug enable debug output on stderr/stdout
--enable-simd enable proposed SIMD instructions --enable-simd enable proposed SIMD instructions
"; ";
@@ -63,9 +64,9 @@ struct Args {
flag_debug: bool, flag_debug: bool,
flag_function: Option<String>, flag_function: Option<String>,
flag_optimize: bool, flag_optimize: bool,
flag_cache: bool, flag_cache: bool, // TODO change to disable cache after implementing cache eviction
flag_cache_dir: Option<String>, flag_cache_config_file: Option<String>,
flag_cache_compression_level: Option<i32>, flag_create_cache_config: bool,
flag_enable_simd: bool, flag_enable_simd: bool,
} }
@@ -85,14 +86,20 @@ fn main() {
wasmtime::init_file_per_thread_logger("cranelift.dbg."); wasmtime::init_file_per_thread_logger("cranelift.dbg.");
} }
cache_conf::init( let errors = cache_config::init(
args.flag_cache args.flag_cache || args.flag_cache_config_file.is_some(),
|| args.flag_cache_dir.is_some() args.flag_cache_config_file.as_ref(),
|| args.flag_cache_compression_level.is_some(), args.flag_create_cache_config,
args.flag_cache_dir.as_ref(),
args.flag_cache_compression_level,
); );
if !errors.is_empty() {
eprintln!("Cache initialization failed. Errors:");
for e in errors {
eprintln!("-> {}", e);
}
process::exit(1);
}
let isa_builder = cranelift_native::builder().unwrap_or_else(|_| { let isa_builder = cranelift_native::builder().unwrap_or_else(|_| {
panic!("host machine is not a supported target"); panic!("host machine is not a supported target");
}); });

View File

@@ -29,6 +29,7 @@ lazy_static = "1.3.0"
spin = "0.5.0" spin = "0.5.0"
log = { version = "0.4.8", default-features = false } log = { version = "0.4.8", default-features = false }
zstd = "0.4" zstd = "0.4"
toml = "0.5"
[dev-dependencies] [dev-dependencies]
tempfile = "3" tempfile = "3"

View File

@@ -15,138 +15,8 @@ use std::io;
use std::path::PathBuf; use std::path::PathBuf;
use std::string::{String, ToString}; use std::string::{String, ToString};
/// Module for configuring the cache system. pub mod config;
pub mod conf { use config as cache_config; // so we have namespaced methods
use directories::ProjectDirs;
use log::{debug, warn};
use spin::Once;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
struct Config {
pub cache_enabled: bool,
pub cache_dir: PathBuf,
pub compression_level: i32,
}
// Private static, so only internal function can access it.
static CONFIG: Once<Config> = Once::new();
static INIT_CALLED: AtomicBool = AtomicBool::new(false);
static DEFAULT_COMPRESSION_LEVEL: i32 = 0; // 0 for zstd means "use default level"
/// Returns true if and only if the cache is enabled.
pub fn cache_enabled() -> bool {
// Not everyone knows about the cache system, i.e. the tests,
// so the default is cache disabled.
CONFIG
.call_once(|| Config::new_cache_disabled())
.cache_enabled
}
/// Returns path to the cache directory.
///
/// Panics if the cache is disabled.
pub fn cache_directory() -> &'static PathBuf {
&CONFIG
.r#try()
.expect("Cache system must be initialized")
.cache_dir
}
/// Returns cache compression level.
///
/// Panics if the cache is disabled.
pub fn compression_level() -> i32 {
CONFIG
.r#try()
.expect("Cache system must be initialized")
.compression_level
}
/// Initializes the cache system. Should be called exactly once,
/// and before using the cache system. Otherwise it can panic.
pub fn init<P: AsRef<Path>>(enabled: bool, dir: Option<P>, compression_level: Option<i32>) {
INIT_CALLED
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.expect("Cache system init must be called at most once");
assert!(
CONFIG.r#try().is_none(),
"Cache system init must be called before using the system."
);
let conf = CONFIG.call_once(|| {
Config::new(
enabled,
dir,
compression_level.unwrap_or(DEFAULT_COMPRESSION_LEVEL),
)
});
debug!(
"Cache init(): enabled={}, cache-dir={:?}, compression-level={}",
conf.cache_enabled, conf.cache_dir, conf.compression_level,
);
}
impl Config {
pub fn new_cache_disabled() -> Self {
Self {
cache_enabled: false,
cache_dir: PathBuf::new(),
compression_level: DEFAULT_COMPRESSION_LEVEL,
}
}
pub fn new<P: AsRef<Path>>(enabled: bool, dir: Option<P>, compression_level: i32) -> Self {
if enabled {
match dir {
Some(dir) => Self::new_step2(dir.as_ref(), compression_level),
None => match ProjectDirs::from("", "CraneStation", "wasmtime") {
Some(proj_dirs) => {
Self::new_step2(proj_dirs.cache_dir(), compression_level)
}
None => {
warn!("Cache directory not specified and failed to find the default. Disabling cache.");
Self::new_cache_disabled()
}
},
}
} else {
Self::new_cache_disabled()
}
}
fn new_step2(dir: &Path, compression_level: i32) -> Self {
// On Windows, if we want long paths, we need '\\?\' prefix, but it doesn't work
// with relative paths. One way to get absolute path (the only one?) is to use
// fs::canonicalize, but it requires that given path exists. The extra advantage
// of this method is fact that the method prepends '\\?\' on Windows.
match fs::create_dir_all(dir) {
Ok(()) => match fs::canonicalize(dir) {
Ok(p) => Self {
cache_enabled: true,
cache_dir: p,
compression_level,
},
Err(err) => {
warn!(
"Failed to canonicalize the cache directory. Disabling cache. \
Message: {}",
err
);
Self::new_cache_disabled()
}
},
Err(err) => {
warn!(
"Failed to create the cache directory. Disabling cache. Message: {}",
err
);
Self::new_cache_disabled()
}
}
}
}
}
lazy_static! { lazy_static! {
static ref SELF_MTIME: String = { static ref SELF_MTIME: String = {
@@ -205,7 +75,7 @@ impl ModuleCacheEntry {
compiler_name: &str, compiler_name: &str,
generate_debug_info: bool, generate_debug_info: bool,
) -> Self { ) -> Self {
let mod_cache_path = if conf::cache_enabled() { let mod_cache_path = if cache_config::enabled() {
let hash = Sha256Hasher::digest(module, function_body_inputs); let hash = Sha256Hasher::digest(module, function_body_inputs);
let compiler_dir = if cfg!(debug_assertions) { let compiler_dir = if cfg!(debug_assertions) {
format!( format!(
@@ -227,7 +97,7 @@ impl ModuleCacheEntry {
mod_dbg = if generate_debug_info { ".d" } else { "" }, mod_dbg = if generate_debug_info { ".d" } else { "" },
); );
Some( Some(
conf::cache_directory() cache_config::directory()
.join(isa.triple().to_string()) .join(isa.triple().to_string())
.join(compiler_dir) .join(compiler_dir)
.join(mod_filename), .join(mod_filename),
@@ -261,7 +131,10 @@ impl ModuleCacheEntry {
let serialized_data = bincode::serialize(&data) let serialized_data = bincode::serialize(&data)
.map_err(|err| warn!("Failed to serialize cached code: {}", err)) .map_err(|err| warn!("Failed to serialize cached code: {}", err))
.ok()?; .ok()?;
let compressed_data = zstd::encode_all(&serialized_data[..], conf::compression_level()) let compressed_data = zstd::encode_all(
&serialized_data[..],
cache_config::baseline_compression_level(),
)
.map_err(|err| warn!("Failed to compress cached code: {}", err)) .map_err(|err| warn!("Failed to compress cached code: {}", err))
.ok()?; .ok()?;

340
wasmtime-environ/src/cache/config.rs vendored Normal file
View File

@@ -0,0 +1,340 @@
//! Module for configuring the cache system.
use directories::ProjectDirs;
use lazy_static::lazy_static;
use log::{debug, error, trace};
use serde::{Deserialize, Serialize};
use spin::Once;
use std::fmt::Debug;
use std::fs;
use std::mem;
use std::path::{Path, PathBuf};
use std::string::{String, ToString};
use std::sync::atomic::{AtomicBool, Ordering};
use std::vec::Vec;
// wrapped, so we have named section in config,
// also, for possible future compatibility
#[derive(Serialize, Deserialize)]
struct Config {
cache: CacheConfig,
}
#[derive(Serialize, Deserialize)]
struct CacheConfig {
#[serde(skip)]
pub errors: Vec<String>,
pub enabled: bool,
pub directory: Option<PathBuf>,
#[serde(rename = "baseline-compression-level")]
pub baseline_compression_level: Option<i32>,
}
// Private static, so only internal function can access it.
static CONFIG: Once<CacheConfig> = Once::new();
static INIT_CALLED: AtomicBool = AtomicBool::new(false);
/// Returns true if and only if the cache is enabled.
pub fn enabled() -> bool {
// Not everyone knows about the cache system, i.e. the tests,
// so the default is cache disabled.
CONFIG
.call_once(|| CacheConfig::new_cache_disabled())
.enabled
}
/// Returns path to the cache directory.
///
/// Panics if the cache is disabled.
pub fn directory() -> &'static PathBuf {
&CONFIG
.r#try()
.expect("Cache system must be initialized")
.directory
.as_ref()
.unwrap()
}
/// Returns cache compression level.
///
/// Panics if the cache is disabled.
pub fn baseline_compression_level() -> i32 {
CONFIG
.r#try()
.expect("Cache system must be initialized")
.baseline_compression_level
.unwrap()
}
/// Initializes the cache system. Should be called exactly once,
/// and before using the cache system. Otherwise it can panic.
/// Returns list of errors. If empty, initialization succeeded.
pub fn init<P: AsRef<Path> + Debug>(
enabled: bool,
config_file: Option<P>,
create_new_config: bool,
) -> &'static Vec<String> {
INIT_CALLED
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.expect("Cache system init must be called at most once");
assert!(
CONFIG.r#try().is_none(),
"Cache system init must be called before using the system."
);
let conf_file_str = format!("{:?}", config_file);
let conf = CONFIG.call_once(|| CacheConfig::from_file(enabled, config_file, create_new_config));
if conf.errors.is_empty() {
debug!(
"Cache init(\"{}\"): enabled={}, directory={:?}, baseline-compression-level={:?}",
conf_file_str, conf.enabled, conf.directory, conf.baseline_compression_level,
)
} else {
error!(
"Cache init(\"{}\"): errors: {:#?}",
conf_file_str, conf.errors,
)
}
&conf.errors
}
// permitted levels from: https://docs.rs/zstd/0.4.28+zstd.1.4.3/zstd/stream/write/struct.Encoder.html
const ZSTD_COMPRESSION_LEVELS: std::ops::RangeInclusive<i32> = 0..=21;
lazy_static! {
static ref PROJECT_DIRS: Option<ProjectDirs> =
ProjectDirs::from("", "CraneStation", "wasmtime");
}
impl CacheConfig {
pub fn new_cache_disabled() -> Self {
Self {
errors: Vec::new(),
enabled: false,
directory: None,
baseline_compression_level: None,
}
}
fn new_cache_enabled_template() -> Self {
let mut conf = Self::new_cache_disabled();
conf.enabled = true;
conf
}
fn new_cache_with_errors(errors: Vec<String>) -> Self {
let mut conf = Self::new_cache_disabled();
conf.errors = errors;
conf
}
pub fn from_file<P: AsRef<Path>>(
enabled: bool,
config_file: Option<P>,
create_new_config: bool,
) -> Self {
if !enabled {
return Self::new_cache_disabled();
}
let (mut config, path_if_flush_to_disk) =
match Self::load_and_parse_file(config_file, create_new_config) {
Ok(data) => data,
Err(err) => return Self::new_cache_with_errors(vec![err]),
};
// validate values and fill in defaults
config.validate_cache_directory_or_default();
config.validate_baseline_compression_level_or_default();
path_if_flush_to_disk.map(|p| config.flush_to_disk(p));
config.disable_if_any_error();
config
}
fn load_and_parse_file<P: AsRef<Path>>(
config_file: Option<P>,
create_new_config: bool,
) -> Result<(Self, Option<PathBuf>), String> {
// get config file path
let (config_file, user_custom_file) = match config_file {
Some(p) => (PathBuf::from(p.as_ref()), true),
None => match &*PROJECT_DIRS {
Some(proj_dirs) => (
proj_dirs.config_dir().join("wasmtime-cache-config.toml"),
false,
),
None => Err("Config file not specified and failed to get the default".to_string())?,
},
};
// read config, or create an empty one
let entity_exists = config_file.exists();
match (create_new_config, entity_exists, user_custom_file) {
(true, true, _) => Err(format!(
"Tried to create a new config, but given entity already exists, path: {}",
config_file.display()
)),
(true, false, _) => Ok((Self::new_cache_enabled_template(), Some(config_file))),
(false, false, false) => Ok((Self::new_cache_enabled_template(), None)),
(false, _, _) => match fs::read(&config_file) {
Ok(bytes) => match toml::from_slice::<Config>(&bytes[..]) {
Ok(config) => Ok((config.cache, None)),
Err(err) => Err(format!(
"Failed to parse config file, path: {}, error: {}",
config_file.display(),
err
)),
},
Err(err) => Err(format!(
"Failed to read config file, path: {}, error: {}",
config_file.display(),
err
)),
},
}
}
fn validate_cache_directory_or_default(&mut self) {
if self.directory.is_none() {
match &*PROJECT_DIRS {
Some(proj_dirs) => self.directory = Some(proj_dirs.cache_dir().to_path_buf()),
None => {
self.errors.push(
"Cache directory not specified and failed to get the default".to_string(),
);
return;
}
}
}
// On Windows, if we want long paths, we need '\\?\' prefix, but it doesn't work
// with relative paths. One way to get absolute path (the only one?) is to use
// fs::canonicalize, but it requires that given path exists. The extra advantage
// of this method is fact that the method prepends '\\?\' on Windows.
let cache_dir = self.directory.as_ref().unwrap();
if !cache_dir.is_absolute() {
self.errors.push(format!(
"Cache directory path has to be absolute, path: {}",
cache_dir.display(),
));
return;
}
match fs::create_dir_all(cache_dir) {
Ok(()) => (),
Err(err) => {
self.errors.push(format!(
"Failed to create the cache directory, path: {}, error: {}",
cache_dir.display(),
err
));
return;
}
};
match fs::canonicalize(cache_dir) {
Ok(p) => self.directory = Some(p),
Err(err) => {
self.errors.push(format!(
"Failed to canonicalize the cache directory, path: {}, error: {}",
cache_dir.display(),
err
));
}
}
}
fn validate_baseline_compression_level_or_default(&mut self) {
if self.baseline_compression_level.is_none() {
self.baseline_compression_level = Some(zstd::DEFAULT_COMPRESSION_LEVEL);
}
if !ZSTD_COMPRESSION_LEVELS.contains(&self.baseline_compression_level.unwrap()) {
self.errors.push(format!(
"Invalid baseline compression level: {} not in {:#?}",
self.baseline_compression_level.unwrap(),
ZSTD_COMPRESSION_LEVELS
));
}
}
fn flush_to_disk(&mut self, path: PathBuf) {
if !self.errors.is_empty() {
return;
}
trace!(
"Flushing cache config file to the disk, path: {}",
path.display()
);
let parent_dir = match path.parent() {
Some(p) => p,
None => {
self.errors
.push(format!("Invalid cache config path: {}", path.display()));
return;
}
};
match fs::create_dir_all(parent_dir) {
Ok(()) => (),
Err(err) => {
self.errors.push(format!(
"Failed to create config directory, config path: {}, error: {}",
path.display(),
err
));
return;
}
};
let serialized = match self.exec_as_config(|config| toml::to_string_pretty(&config)) {
Ok(data) => data,
Err(err) => {
self.errors.push(format!(
"Failed to serialize config, (unused) path: {}, msg: {}",
path.display(),
err
));
return;
}
};
let header = "# Automatically generated with defaults.\n\
# Comment out certain fields to use default values.\n\n";
let content = format!("{}{}", header, serialized);
match fs::write(&path, &content) {
Ok(()) => (),
Err(err) => {
self.errors.push(format!(
"Failed to flush config to the disk, path: {}, msg: {}",
path.display(),
err
));
return;
}
}
}
fn disable_if_any_error(&mut self) {
if !self.errors.is_empty() {
let mut conf = Self::new_cache_disabled();
mem::swap(self, &mut conf);
mem::swap(&mut self.errors, &mut conf.errors);
}
}
fn exec_as_config<T>(&mut self, closure: impl FnOnce(&mut Config) -> T) -> T {
let mut config = Config {
cache: CacheConfig::new_cache_disabled(),
};
mem::swap(self, &mut config.cache);
let ret = closure(&mut config);
mem::swap(self, &mut config.cache);
ret
}
}

View File

@@ -25,15 +25,33 @@ use tempfile;
fn test_write_read_cache() { fn test_write_read_cache() {
pretty_env_logger::init(); pretty_env_logger::init();
let dir = tempfile::tempdir().expect("Can't create temporary directory"); let dir = tempfile::tempdir().expect("Can't create temporary directory");
let compression_level = 5;
conf::init(true, Some(dir.path()), Some(compression_level)); let cache_dir = dir.path().join("cache-dir");
assert!(conf::cache_enabled()); let baseline_compression_level = 5;
let config_path = dir.path().join("cache-config.toml");
let config_content = format!(
"[cache]\n\
enabled = true\n\
directory = {}\n\
baseline-compression-level = {}\n",
toml::to_string_pretty(&format!("{}", cache_dir.display())).unwrap(),
baseline_compression_level,
);
fs::write(&config_path, config_content).expect("Failed to write test config file");
let errors = cache_config::init(true, Some(&config_path), false);
assert!(errors.is_empty());
assert!(cache_config::enabled());
// assumption: config init creates cache directory and returns canonicalized path // assumption: config init creates cache directory and returns canonicalized path
assert_eq!( assert_eq!(
*conf::cache_directory(), *cache_config::directory(),
fs::canonicalize(dir.path()).unwrap() fs::canonicalize(cache_dir).unwrap()
);
assert_eq!(
cache_config::baseline_compression_level(),
baseline_compression_level
); );
assert_eq!(conf::compression_level(), compression_level);
let mut rng = SmallRng::from_seed([ let mut rng = SmallRng::from_seed([
0x42, 0x04, 0xF3, 0x44, 0x11, 0x22, 0x33, 0x44, 0x67, 0x68, 0xFF, 0x00, 0x44, 0x23, 0x7F, 0x42, 0x04, 0xF3, 0x44, 0x11, 0x22, 0x33, 0x44, 0x67, 0x68, 0xFF, 0x00, 0x44, 0x23, 0x7F,

View File

@@ -54,7 +54,7 @@ pub mod lightbeam;
pub use crate::address_map::{ pub use crate::address_map::{
FunctionAddressMap, InstructionAddressMap, ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges, FunctionAddressMap, InstructionAddressMap, ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges,
}; };
pub use crate::cache::conf as cache_conf; pub use crate::cache::config as cache_config;
pub use crate::compilation::{ pub use crate::compilation::{
Compilation, CompileError, Compiler, Relocation, RelocationTarget, Relocations, Compilation, CompileError, Compiler, Relocation, RelocationTarget, Relocations,
}; };

View File

@@ -0,0 +1,7 @@
use wasmtime_environ::cache_config;
#[test]
fn test_cache_default_config_in_memory() {
let errors = cache_config::init::<&str>(true, None, false);
assert!(errors.is_empty());
}

View File

@@ -1,10 +1,26 @@
use std::fs;
use tempfile; use tempfile;
use wasmtime_environ::cache_conf; use wasmtime_environ::cache_config;
#[test] #[test]
#[should_panic] #[should_panic]
fn test_fail_calling_init_twice() { fn test_cache_fail_calling_init_twice() {
let dir = tempfile::tempdir().expect("Can't create temporary directory"); let dir = tempfile::tempdir().expect("Can't create temporary directory");
cache_conf::init(true, Some(dir.path()), Some(5)); let cache_dir = dir.path().join("cache-dir");
cache_conf::init(true, Some(dir.path()), Some(5)); let baseline_compression_level = 5;
let config_path = dir.path().join("cache-config.toml");
let config_content = format!(
"[cache]\n\
enabled = true\n\
directory = {}\n\
baseline-compression-level = {}\n",
toml::to_string_pretty(&format!("{}", cache_dir.display())).unwrap(),
baseline_compression_level,
);
fs::write(&config_path, config_content).expect("Failed to write test config file");
let errors = cache_config::init(true, Some(&config_path), false);
assert!(errors.is_empty());
let _errors = cache_config::init(true, Some(&config_path), false);
} }

View File

@@ -0,0 +1,23 @@
use std::fs;
use tempfile;
use wasmtime_environ::cache_config;
#[test]
fn test_cache_fail_invalid_config() {
let dir = tempfile::tempdir().expect("Can't create temporary directory");
let baseline_compression_level = -4;
let config_path = dir.path().join("cache-config.toml");
let config_content = format!(
"[cache]\n\
enabled = true\n\
directory = {}\n\
baseline-compression-level = {}\n",
toml::to_string_pretty(&format!("{}", config_path.display())).unwrap(), // directory is a file -- incorrect!
baseline_compression_level,
);
fs::write(&config_path, config_content).expect("Failed to write test config file");
let errors = cache_config::init(true, Some(&config_path), false);
assert!(!errors.is_empty());
}

View File

@@ -0,0 +1,10 @@
use tempfile;
use wasmtime_environ::cache_config;
#[test]
fn test_cache_fail_invalid_path_to_config() {
let dir = tempfile::tempdir().expect("Can't create temporary directory");
let config_path = dir.path().join("cache-config.toml"); // doesn't exist
let errors = cache_config::init(true, Some(&config_path), false);
assert!(!errors.is_empty());
}

View File

@@ -1,7 +1,15 @@
use wasmtime_environ::cache_conf; // These tests doesn't call init(), so we can test a multiple certain things here
use wasmtime_environ::cache_config;
#[test] #[test]
#[should_panic] #[should_panic]
fn test_fail_usage_without_init() { fn test_cache_fail_usage_without_init_directory() {
let _ = cache_conf::cache_directory(); let _ = cache_config::directory();
}
#[test]
#[should_panic]
fn test_cache_fail_usage_without_init_baseline_compression_level() {
let _ = cache_config::baseline_compression_level();
} }

View File

@@ -0,0 +1,12 @@
use tempfile;
use wasmtime_environ::cache_config;
#[test]
fn test_cache_write_default_config() {
let dir = tempfile::tempdir().expect("Can't create temporary directory");
let config_path = dir.path().join("cache-config.toml");
let errors = cache_config::init(true, Some(&config_path), true);
assert!(errors.is_empty());
assert!(config_path.exists());
}