From 633dfa17eebebbddca45febb7d21730de264fd51 Mon Sep 17 00:00:00 2001 From: Artur Jamro Date: Fri, 23 Aug 2019 17:00:38 -0700 Subject: [PATCH] Cache config as a file --- src/bin/wasm2obj.rs | 39 +- src/bin/wasmtime.rs | 43 ++- src/bin/wast.rs | 39 +- wasmtime-environ/Cargo.toml | 1 + wasmtime-environ/src/cache.rs | 147 +------- wasmtime-environ/src/cache/config.rs | 340 ++++++++++++++++++ wasmtime-environ/src/cache/tests.rs | 30 +- wasmtime-environ/src/lib.rs | 2 +- .../tests/cache_default_config_in_memory.rs | 7 + .../tests/cache_fail_calling_init_twice.rs | 24 +- .../tests/cache_fail_invalid_config.rs | 23 ++ .../cache_fail_invalid_path_to_config.rs | 10 + .../tests/cache_fail_usage_without_init.rs | 14 +- .../tests/cache_write_default_config.rs | 12 + 14 files changed, 530 insertions(+), 201 deletions(-) create mode 100644 wasmtime-environ/src/cache/config.rs create mode 100644 wasmtime-environ/tests/cache_default_config_in_memory.rs create mode 100644 wasmtime-environ/tests/cache_fail_invalid_config.rs create mode 100644 wasmtime-environ/tests/cache_fail_invalid_path_to_config.rs create mode 100644 wasmtime-environ/tests/cache_write_default_config.rs diff --git a/src/bin/wasm2obj.rs b/src/bin/wasm2obj.rs index 308fd858ee..9afe4404ee 100644 --- a/src/bin/wasm2obj.rs +++ b/src/bin/wasm2obj.rs @@ -50,7 +50,7 @@ use std::str; use std::str::FromStr; use target_lexicon::Triple; use wasmtime_debug::{emit_debugsections, read_debuginfo}; -use wasmtime_environ::cache_conf; +use wasmtime_environ::cache_config; use wasmtime_environ::{ 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. Usage: - wasm2obj [--target TARGET] [-dg] [--cache] [--cache-dir=] [--cache-compression-level=] [--enable-simd] -o + wasm2obj [--target TARGET] [-dg] [--cache | --cache-config=] [--create-cache-config] [--enable-simd] -o wasm2obj --help | --version Options: @@ -71,11 +71,12 @@ Options: -h, --help print this help message --target build for the target triple; default is the host machine -g generate debug information - -c, --cache enable caching system, use default cache directory - --cache-dir= - enable caching system, use specified cache directory - --cache-compression-level= - enable caching system, use custom compression level for new cache, values 1-21 + -c, --cache enable caching system, use default configuration + --cache-config= + enable caching system, use specified cache configuration + --create-cache-config + 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 --version print the Cranelift version -d, --debug enable debug output on stderr/stdout @@ -88,9 +89,9 @@ struct Args { arg_target: Option, flag_g: bool, flag_debug: bool, - flag_cache: bool, - flag_cache_dir: Option, - flag_cache_compression_level: Option, + flag_cache: bool, // TODO change to disable cache after implementing cache eviction + flag_cache_config_file: Option, + flag_create_cache_config: bool, flag_enable_simd: bool, } @@ -117,14 +118,20 @@ fn main() { wasmtime::init_file_per_thread_logger("wasm2obj.dbg."); } - cache_conf::init( - args.flag_cache - || args.flag_cache_dir.is_some() - || args.flag_cache_compression_level.is_some(), - args.flag_cache_dir.as_ref(), - args.flag_cache_compression_level, + let errors = cache_config::init( + args.flag_cache || args.flag_cache_config_file.is_some(), + args.flag_cache_config_file.as_ref(), + args.flag_create_cache_config, ); + 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); match handle_module( path.to_path_buf(), diff --git a/src/bin/wasmtime.rs b/src/bin/wasmtime.rs index 4414fbebbf..2687fcfe6f 100644 --- a/src/bin/wasmtime.rs +++ b/src/bin/wasmtime.rs @@ -47,7 +47,7 @@ use std::rc::Rc; use wabt; use wasi_common::preopen_dir; 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_jit::Features; 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. Usage: - wasmtime [-odg] [--enable-simd] [--wasi-c] [--cache] [--cache-dir=] [--cache-compression-level=] [--preload=...] [--env=...] [--dir=...] [--mapdir=...] [...] - wasmtime [-odg] [--enable-simd] [--wasi-c] [--cache] [--cache-dir=] [--cache-compression-level=] [--env=...] [--dir=...] [--mapdir=...] --invoke= [...] + wasmtime [-odg] [--enable-simd] [--wasi-c] [--cache | --cache-config=] [--create-cache-config] [--preload=...] [--env=...] [--dir=...] [--mapdir=...] [...] + wasmtime [-odg] [--enable-simd] [--wasi-c] [--cache | --cache-config=] [--create-cache-config] [--env=...] [--dir=...] [--mapdir=...] --invoke= [...] wasmtime --help | --version Options: --invoke= name of function to run -o, --optimize runs optimization passes on the translated functions - -c, --cache enable caching system, use default cache directory - --cache-dir= - enable caching system, use specified cache directory - --cache-compression-level= - enable caching system, use custom compression level for new cache, values 1-21 + -c, --cache enable caching system, use default configuration + --cache-config= + enable caching system, use specified cache configuration + --create-cache-config + 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 -d, --debug enable debug output on stderr/stdout --enable-simd enable proposed SIMD instructions @@ -94,9 +95,9 @@ struct Args { arg_file: String, arg_arg: Vec, flag_optimize: bool, - flag_cache: bool, - flag_cache_dir: Option, - flag_cache_compression_level: Option, + flag_cache: bool, // TODO change to disable cache after implementing cache eviction + flag_cache_config_file: Option, + flag_create_cache_config: bool, flag_debug: bool, flag_g: bool, flag_enable_simd: bool, @@ -201,7 +202,7 @@ fn main() { for cause in err.iter_causes() { eprintln!(" caused by: {}", cause); } - std::process::exit(1); + exit(1); } fn rmain() -> Result<(), Error> { @@ -220,14 +221,20 @@ fn rmain() -> Result<(), Error> { wasmtime::init_file_per_thread_logger("wasmtime.dbg."); } - cache_conf::init( - args.flag_cache - || args.flag_cache_dir.is_some() - || args.flag_cache_compression_level.is_some(), - args.flag_cache_dir.as_ref(), - args.flag_cache_compression_level, + let errors = cache_config::init( + args.flag_cache || args.flag_cache_config_file.is_some(), + args.flag_cache_config_file.as_ref(), + args.flag_create_cache_config, ); + 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 features: Features = Default::default(); diff --git a/src/bin/wast.rs b/src/bin/wast.rs index a576e704d1..e9cc6fc706 100644 --- a/src/bin/wast.rs +++ b/src/bin/wast.rs @@ -33,7 +33,7 @@ use pretty_env_logger; use serde::Deserialize; use std::path::Path; use std::process; -use wasmtime_environ::cache_conf; +use wasmtime_environ::cache_config; use wasmtime_jit::{Compiler, Features}; use wasmtime_wast::WastContext; @@ -41,18 +41,19 @@ const USAGE: &str = " Wast test runner. Usage: - wast [-do] [--enable-simd] [--cache] [--cache-dir=] [--cache-compression-level=] ... + wast [-do] [--enable-simd] [--cache | --cache-config=] [--create-cache-config] ... wast --help | --version Options: -h, --help print this help message --version print the Cranelift version -o, --optimize runs optimization passes on the translated functions - -c, --cache enable caching system, use default cache directory - --cache-dir= - enable caching system, use specified cache directory - --cache-compression-level= - enable caching system, use custom compression level for new cache, values 1-21 + -c, --cache enable caching system, use default configuration + --cache-config= + enable caching system, use specified cache configuration + --create-cache-config + 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 --enable-simd enable proposed SIMD instructions "; @@ -63,9 +64,9 @@ struct Args { flag_debug: bool, flag_function: Option, flag_optimize: bool, - flag_cache: bool, - flag_cache_dir: Option, - flag_cache_compression_level: Option, + flag_cache: bool, // TODO change to disable cache after implementing cache eviction + flag_cache_config_file: Option, + flag_create_cache_config: bool, flag_enable_simd: bool, } @@ -85,14 +86,20 @@ fn main() { wasmtime::init_file_per_thread_logger("cranelift.dbg."); } - cache_conf::init( - args.flag_cache - || args.flag_cache_dir.is_some() - || args.flag_cache_compression_level.is_some(), - args.flag_cache_dir.as_ref(), - args.flag_cache_compression_level, + let errors = cache_config::init( + args.flag_cache || args.flag_cache_config_file.is_some(), + args.flag_cache_config_file.as_ref(), + args.flag_create_cache_config, ); + 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(|_| { panic!("host machine is not a supported target"); }); diff --git a/wasmtime-environ/Cargo.toml b/wasmtime-environ/Cargo.toml index 6310e5ac8b..c1a4b44752 100644 --- a/wasmtime-environ/Cargo.toml +++ b/wasmtime-environ/Cargo.toml @@ -29,6 +29,7 @@ lazy_static = "1.3.0" spin = "0.5.0" log = { version = "0.4.8", default-features = false } zstd = "0.4" +toml = "0.5" [dev-dependencies] tempfile = "3" diff --git a/wasmtime-environ/src/cache.rs b/wasmtime-environ/src/cache.rs index c6139d07e0..42b2645427 100644 --- a/wasmtime-environ/src/cache.rs +++ b/wasmtime-environ/src/cache.rs @@ -15,138 +15,8 @@ use std::io; use std::path::PathBuf; use std::string::{String, ToString}; -/// Module for configuring the cache system. -pub mod conf { - 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 = 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>(enabled: bool, dir: Option

, compression_level: Option) { - 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>(enabled: bool, dir: Option

, 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() - } - } - } - } -} +pub mod config; +use config as cache_config; // so we have namespaced methods lazy_static! { static ref SELF_MTIME: String = { @@ -205,7 +75,7 @@ impl ModuleCacheEntry { compiler_name: &str, generate_debug_info: bool, ) -> 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 compiler_dir = if cfg!(debug_assertions) { format!( @@ -227,7 +97,7 @@ impl ModuleCacheEntry { mod_dbg = if generate_debug_info { ".d" } else { "" }, ); Some( - conf::cache_directory() + cache_config::directory() .join(isa.triple().to_string()) .join(compiler_dir) .join(mod_filename), @@ -261,9 +131,12 @@ impl ModuleCacheEntry { let serialized_data = bincode::serialize(&data) .map_err(|err| warn!("Failed to serialize cached code: {}", err)) .ok()?; - let compressed_data = zstd::encode_all(&serialized_data[..], conf::compression_level()) - .map_err(|err| warn!("Failed to compress cached code: {}", err)) - .ok()?; + let compressed_data = zstd::encode_all( + &serialized_data[..], + cache_config::baseline_compression_level(), + ) + .map_err(|err| warn!("Failed to compress cached code: {}", err)) + .ok()?; // 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. diff --git a/wasmtime-environ/src/cache/config.rs b/wasmtime-environ/src/cache/config.rs new file mode 100644 index 0000000000..aaf84dea59 --- /dev/null +++ b/wasmtime-environ/src/cache/config.rs @@ -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, + + pub enabled: bool, + pub directory: Option, + #[serde(rename = "baseline-compression-level")] + pub baseline_compression_level: Option, +} + +// Private static, so only internal function can access it. +static CONFIG: Once = 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 + Debug>( + enabled: bool, + config_file: Option

, + create_new_config: bool, +) -> &'static Vec { + 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 = 0..=21; +lazy_static! { + static ref PROJECT_DIRS: Option = + 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) -> Self { + let mut conf = Self::new_cache_disabled(); + conf.errors = errors; + conf + } + + pub fn from_file>( + enabled: bool, + config_file: Option

, + 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>( + config_file: Option

, + create_new_config: bool, + ) -> Result<(Self, Option), 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::(&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(&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 + } +} diff --git a/wasmtime-environ/src/cache/tests.rs b/wasmtime-environ/src/cache/tests.rs index 2401e45610..a6e7b5c609 100644 --- a/wasmtime-environ/src/cache/tests.rs +++ b/wasmtime-environ/src/cache/tests.rs @@ -25,15 +25,33 @@ use tempfile; fn test_write_read_cache() { pretty_env_logger::init(); let dir = tempfile::tempdir().expect("Can't create temporary directory"); - let compression_level = 5; - conf::init(true, Some(dir.path()), Some(compression_level)); - assert!(conf::cache_enabled()); + + let cache_dir = dir.path().join("cache-dir"); + 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 assert_eq!( - *conf::cache_directory(), - fs::canonicalize(dir.path()).unwrap() + *cache_config::directory(), + 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([ 0x42, 0x04, 0xF3, 0x44, 0x11, 0x22, 0x33, 0x44, 0x67, 0x68, 0xFF, 0x00, 0x44, 0x23, 0x7F, diff --git a/wasmtime-environ/src/lib.rs b/wasmtime-environ/src/lib.rs index 5b9c3abf8b..a4235f864a 100644 --- a/wasmtime-environ/src/lib.rs +++ b/wasmtime-environ/src/lib.rs @@ -54,7 +54,7 @@ pub mod lightbeam; pub use crate::address_map::{ 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::{ Compilation, CompileError, Compiler, Relocation, RelocationTarget, Relocations, }; diff --git a/wasmtime-environ/tests/cache_default_config_in_memory.rs b/wasmtime-environ/tests/cache_default_config_in_memory.rs new file mode 100644 index 0000000000..30fe195087 --- /dev/null +++ b/wasmtime-environ/tests/cache_default_config_in_memory.rs @@ -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()); +} diff --git a/wasmtime-environ/tests/cache_fail_calling_init_twice.rs b/wasmtime-environ/tests/cache_fail_calling_init_twice.rs index 951908b522..557faf2eef 100644 --- a/wasmtime-environ/tests/cache_fail_calling_init_twice.rs +++ b/wasmtime-environ/tests/cache_fail_calling_init_twice.rs @@ -1,10 +1,26 @@ +use std::fs; use tempfile; -use wasmtime_environ::cache_conf; +use wasmtime_environ::cache_config; #[test] #[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"); - cache_conf::init(true, Some(dir.path()), Some(5)); - cache_conf::init(true, Some(dir.path()), Some(5)); + let cache_dir = dir.path().join("cache-dir"); + 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); } diff --git a/wasmtime-environ/tests/cache_fail_invalid_config.rs b/wasmtime-environ/tests/cache_fail_invalid_config.rs new file mode 100644 index 0000000000..05a69ba568 --- /dev/null +++ b/wasmtime-environ/tests/cache_fail_invalid_config.rs @@ -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()); +} diff --git a/wasmtime-environ/tests/cache_fail_invalid_path_to_config.rs b/wasmtime-environ/tests/cache_fail_invalid_path_to_config.rs new file mode 100644 index 0000000000..8a469369ae --- /dev/null +++ b/wasmtime-environ/tests/cache_fail_invalid_path_to_config.rs @@ -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()); +} diff --git a/wasmtime-environ/tests/cache_fail_usage_without_init.rs b/wasmtime-environ/tests/cache_fail_usage_without_init.rs index b428d9f7fb..7538bfd505 100644 --- a/wasmtime-environ/tests/cache_fail_usage_without_init.rs +++ b/wasmtime-environ/tests/cache_fail_usage_without_init.rs @@ -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] #[should_panic] -fn test_fail_usage_without_init() { - let _ = cache_conf::cache_directory(); +fn test_cache_fail_usage_without_init_directory() { + let _ = cache_config::directory(); +} + +#[test] +#[should_panic] +fn test_cache_fail_usage_without_init_baseline_compression_level() { + let _ = cache_config::baseline_compression_level(); } diff --git a/wasmtime-environ/tests/cache_write_default_config.rs b/wasmtime-environ/tests/cache_write_default_config.rs new file mode 100644 index 0000000000..4a3cebef17 --- /dev/null +++ b/wasmtime-environ/tests/cache_write_default_config.rs @@ -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()); +}