diff --git a/Cargo.toml b/Cargo.toml index e55b521..2dacace 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ path = "src/main.rs" [dependencies] chrono = "0.4.20" clap = { version = "3.2.16", features = ["derive"] } +sysinfo = "0.25.1" [package.metadata.deb] depends = "fio" diff --git a/src/main.rs b/src/main.rs index 663424a..36264ab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod benchmarks; +mod stress; mod text; use clap::{Args, Parser, Subcommand}; @@ -10,12 +11,15 @@ struct Cli { #[clap(subcommand)] command: Commands, - #[clap(short = 'l', default_value_t = 1, help = "Number of times to run test. Default = 1", global = true)] + #[clap(short = 'l', long, default_value_t = 1, help = "Number of times to run test. Default = 1", global = true)] loopcount: u8, } #[derive(Subcommand)] enum Commands { + // CPU benchmarks subcommand + #[clap(name = "cpu", about = "CPU benchmarks and stress tests.")] + Cpu(Cpu), // disk benchmarks subcommand #[clap(name = "disk", about = "Hard drive and SSD benchmarks.")] Disk(Disk), @@ -24,6 +28,22 @@ enum Commands { Net(Net), } +#[derive(Args)] +struct Cpu { + #[clap(subcommand)] + cpu_commands: CpuCommands, +} + +#[derive(Subcommand)] +enum CpuCommands { + // CPU stress test subcommand + #[clap(name = "stress", about = "Stress test the CPU with math!")] + StressTest { + #[clap(short = 't', long, default_value_t = 0, help = "Number of threads to use; defaults to CPU's max thread count.")] + threads: usize, + }, +} + #[derive(Args)] struct Disk { #[clap(subcommand)] @@ -35,36 +55,36 @@ enum DiskCommands { // sequential disk read subcommand #[clap(name = "read_seq", about = "Sequential disk read speed test.")] ReadSeqTest { - #[clap(short = 'f', default_value_t = String::from("/tmp/disk-test.tmp"))] + #[clap(short = 'f', long, default_value_t = String::from("/tmp/disk-test.tmp"))] tempfile: String, - #[clap(short = 's', default_value_t = 15)] + #[clap(short = 's', long, default_value_t = 15)] size: u8, }, // random disk read subcommand #[clap(name = "read_rand", about = "Random 4K disk read speed test.")] ReadRandTest { - #[clap(short = 'f', default_value_t = String::from("/tmp/disk-test.tmp"))] + #[clap(short = 'f', long, default_value_t = String::from("/tmp/disk-test.tmp"))] tempfile: String, - #[clap(short = 's', default_value_t = 15)] + #[clap(short = 's', long, default_value_t = 15)] size: u8, }, // sequential disk write subcommand #[clap(name = "write_seq", about = "Write a large file to determine sequential disk write speeds.")] WriteSeqTest { - #[clap(short = 't', default_value_t = String::from("/tmp/disk-test.tmp"))] + #[clap(short = 't', long, default_value_t = String::from("/tmp/disk-test.tmp"))] tempfile: String, - #[clap(short = 's', default_value_t = 15)] + #[clap(short = 's', long, default_value_t = 15)] size: u8, }, // random 4K disk write subcommand #[clap(name = "write_rand", about = "Write a bunch of smallfiles to determine random disk write speeds.")] WriteRandTest { - #[clap(short = 't', default_value_t = String::from("/tmp/disk-test.tmp"))] + #[clap(short = 't', long, default_value_t = String::from("/tmp/disk-test.tmp"))] tempfile: String, - #[clap(short = 's', default_value_t = 15)] + #[clap(short = 's', long, default_value_t = 15)] size: u8, }, } @@ -80,18 +100,18 @@ enum NetCommands { // ping subcommand #[clap(name = "ping", about = "Ping a host to determine network latency.")] Ping { - #[clap(short = 't', default_value_t = String::from("8.8.8.8"))] + #[clap(short = 't', long, default_value_t = String::from("8.8.8.8"))] host: String, - #[clap(short = 'c', default_value_t = 30)] + #[clap(short = 'c', long, default_value_t = 30)] count: u16, }, // bandwidth test subcommand #[clap(name = "bandwidth", about = "Downloads a remote file to determine network bandwidth.")] Bandwidth { - #[clap(short = 'd', default_value_t = String::from("https://www.bitgoblin.tech/hardware-tests/export-01.mp4"))] + #[clap(short = 'd', long, default_value_t = String::from("https://www.bitgoblin.tech/hardware-tests/export-01.mp4"))] download: String, - #[clap(short = 'o', default_value_t = String::from("./tempfile"))] + #[clap(short = 'o', long, default_value_t = String::from("./tempfile"))] output: String, }, } @@ -101,6 +121,10 @@ fn main() { // map subcommands back to the main command match &cli.command { + Commands::Cpu(args) => match &args.cpu_commands { + CpuCommands::StressTest { threads } => stress::cpu::cpu_stress_math(*threads), + }, + Commands::Disk(args) => match &args.disk_commands { DiskCommands::ReadSeqTest { tempfile, size } => { for i in 0..cli.loopcount { diff --git a/src/stress/cpu.rs b/src/stress/cpu.rs new file mode 100644 index 0000000..b0c2e13 --- /dev/null +++ b/src/stress/cpu.rs @@ -0,0 +1,34 @@ +use std::thread; +use sysinfo::{System,SystemExt}; + +pub fn cpu_stress_math(threads: usize) { + // fetch system information + let mut sys = System::new_all(); + sys.refresh_all(); + let num_cpus = sys.cpus().len(); + + let mut num_threads = threads; + if num_threads == 0 { + println!("Number of threads not specified, defaulting to CPU's thread count of {}.", num_cpus); + num_threads = num_cpus; + } else { + println!("Using specified thread count of {}", num_threads); + } + + for i in 1..num_threads { + println!("Spawning thread number {}", i); + thread::spawn (|| { + worker(); + }); + } + println!("Using main as last thread"); + worker(); +} + +fn worker() { + let mut _x = 0; + loop { + _x += 1; + _x -= 1; + } +} diff --git a/src/stress/mod.rs b/src/stress/mod.rs new file mode 100644 index 0000000..3bfb62f --- /dev/null +++ b/src/stress/mod.rs @@ -0,0 +1 @@ +pub mod cpu;