blob: 134e5bdb407eedf9e1fb5917253889ca9ae316dd [file] [log] [blame]
// 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(())
}