| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| mod args; |
| mod common_operations; |
| mod file_target; |
| mod generator; |
| mod io_packet; |
| mod issuer; |
| mod log; |
| mod operations; |
| mod sequential_io_generator; |
| mod target; |
| mod verifier; |
| |
| #[cfg(target_os = "fuchsia")] |
| mod fuchsia_utils; |
| |
| #[cfg(not(target_os = "fuchsia"))] |
| mod not_fuchsia_utils; |
| |
| use { |
| crate::generator::{run_load, GeneratorArgs}, |
| crate::log::{log_init, Stats}, |
| ::log::{debug, log_enabled, Level::Debug}, |
| anyhow::Error, |
| std::{ |
| fs::{metadata, remove_file, File, OpenOptions}, |
| io::prelude::*, |
| process, |
| sync::{Arc, Mutex}, |
| thread::spawn, |
| time::Instant, |
| }, |
| }; |
| |
| // Magic number that gets written in block header |
| static MAGIC_NUMBER: u64 = 0x4f6475346573742e; |
| |
| fn create_target(target_name: &String, target_length: u64) -> bool { |
| let metadata = metadata(&target_name); |
| |
| match metadata { |
| Ok(stats) => { |
| assert!(!stats.permissions().readonly()); |
| assert!(stats.len() >= target_length); |
| return false; |
| } |
| _ => {} |
| } |
| // TODO(auradkar): File should not be created here. It is generator/target's |
| // knowledge/job/responsibility. |
| let f = File::create(&target_name).unwrap(); |
| |
| // Note: Though the target length can be any arbitrary value, the IOs issued |
| // on the target's range vary depending on various parameters such as |
| // max_io_size, max_io_count, thread_count, align, etc. It may happen that |
| // some portion of the target may never gets IOs. |
| f.set_len(target_length).unwrap(); |
| true |
| } |
| |
| fn remove_target(target_name: &String) { |
| remove_file(target_name).unwrap(); |
| } |
| |
| fn output_config(generator_args_vec: &Vec<GeneratorArgs>, output_config_file: &String) { |
| let serialized = serde_json::to_string_pretty(&generator_args_vec).unwrap(); |
| let mut file = OpenOptions::new() |
| .create(true) |
| .write(true) |
| .truncate(true) |
| .open(&output_config_file) |
| .unwrap(); |
| file.write_all(serialized.as_bytes()).unwrap(); |
| |
| debug!("{}", serialized); |
| file.sync_data().unwrap(); |
| } |
| |
| fn main() -> Result<(), Error> { |
| let args = args::parse()?; |
| |
| log_init(args.log_ftrace)?; |
| let start_instant: Instant = Instant::now(); |
| |
| let mut thread_handles = vec![]; |
| let mut generator_args_vec = vec![]; |
| |
| let file_created = create_target(&args.target, args.target_length); |
| |
| let metadata = metadata(&args.target)?; |
| |
| let mut offset_start = 0 as u64; |
| let range_size = (metadata.len() / args.thread_count as u64) as u64; |
| |
| // To keep contention among threads low, each generator owns/updates their |
| // stats. The "main" thread holds a |
| // reference to these stats so that, when ready, it can print the |
| // progress/stats from time to time. |
| let mut stats_array = Vec::with_capacity(args.thread_count); |
| |
| for i in 0..args.thread_count { |
| let generator_args = GeneratorArgs::new( |
| MAGIC_NUMBER, |
| process::id() as u64, |
| i as u64, // generator id |
| args.block_size, |
| args.max_io_size, |
| args.align, |
| i as u64, // seed |
| args.target.clone(), |
| offset_start..(offset_start + range_size), |
| args.target_type, |
| args.operations.clone(), |
| args.queue_depth, |
| args.max_io_count, |
| args.sequential, |
| ); |
| generator_args_vec.push(generator_args.clone()); |
| |
| let stats = { |
| let mut stats = Stats::new(); |
| stats.start_clock(); |
| Arc::new(Mutex::new(stats)) |
| }; |
| stats_array.push(stats.clone()); |
| thread_handles.push(spawn(move || run_load(generator_args, start_instant, stats))); |
| offset_start += range_size; |
| } |
| |
| output_config(&generator_args_vec, &args.output_config_file.to_string()); |
| for handle in thread_handles { |
| handle.join().unwrap()?; |
| } |
| |
| let mut aggregate_stats = Stats::new(); |
| let mut i = 0; |
| |
| // How the summary stats. For long running IO load we should print the stats |
| // from time to time to show how IO are going. A TODO(auradkar). |
| for stat in stats_array { |
| let stat = stat.lock().unwrap(); |
| aggregate_stats.aggregate_summary(&stat); |
| if log_enabled!(Debug) { |
| debug!("===== For generator-{} =====", i); |
| stat.display_summary(); |
| i += 1; |
| } |
| } |
| println!("===== Aggregate Stats ====="); |
| aggregate_stats.display_summary(); |
| |
| // Cleanup if we have created the target and we are asked to. |
| if file_created && args.cleanup { |
| remove_target(&args.target); |
| } |
| |
| Ok(()) |
| } |