863de1eae106015be7a8227a36b61027bdba2219 / src/main.rs · 3792 bytes · raw
mod config;
mod sensors;
mod workload;
fn main() {
let cfg = config::load();
let args: Vec<String> = std::env::args().collect();
// `adlerbench --dump` = one-shot sensor snapshot (spike/verification
// tool, mirrors adlermon's dump mode). Default = config summary.
if args.iter().any(|a| a == "--dump") {
let s = sensors::snapshot(None, 0.0);
println!("vCore : {}", opt_volts(s.vcore));
println!("Pkg temp : {}", opt_temp(s.pkg_temp));
println!("Pkg power : {} (None on first tick — no delta yet)", opt_watts(s.pkg_power));
println!("Peak clock : {}", opt_khz(s.peak_clock_khz));
println!("RAPL energy: {}", opt_uj(s.energy_uj()));
println!("Cores : {} logical", s.core_freqs.len());
return;
}
// `adlerbench --smoke-workload <name>` = run one workload for 3s and
// print its verdict. Verifies the Workload trait end-to-end without
// needing the run loop / TUI.
if let Some(idx) = args.iter().position(|a| a == "--smoke-workload") {
if let Some(name) = args.get(idx + 1) {
smoke_workload(name, &cfg);
return;
}
}
println!("adlerbench — {} workloads configured", cfg.workloads.len());
for w in &cfg.workloads {
println!(" {} {}s cores={}", w.name, w.duration_secs, w.cores);
}
}
fn smoke_workload(name: &str, cfg: &config::Config) {
use std::thread;
use std::time::Duration;
// Build a 3-second spec for the requested workload, regardless of the
// configured duration — this is a smoke test, not a real run.
let spec = config::WorkloadSpec {
name: name.to_string(),
duration_secs: 3,
cores: "0-15".to_string(),
};
let Some(mut w) = workload::from_spec(&spec, &cfg.bin_dir) else {
eprintln!("unknown or not-yet-wired workload: {name}");
return;
};
println!("smoke: {} params={}", w.name(), w.params());
match w.start() {
Ok(()) => {
// Poll is_running every 250ms; stop after the spec's duration
// plus a 1s grace (some workloads self-exit right at the
// boundary; the grace avoids a Stopped verdict when the
// workload actually finished on its own).
let ticks = spec.duration_secs * 4 + 4;
let mut elapsed = 0u64;
while elapsed < ticks {
thread::sleep(Duration::from_millis(250));
elapsed += 1;
if !w.is_running() {
println!("smoke: exited after {:.1}s", elapsed as f64 / 4.0);
break;
}
}
if w.is_running() {
println!("smoke: still running — calling stop()");
let _ = w.stop();
}
let verdict = w.wait();
println!("smoke: verdict = {:?}", verdict);
}
Err(e) => println!("smoke: start failed: {e}"),
}
}
fn opt_volts(v: Option<f64>) -> String {
match v {
Some(x) => format!("{:.3} V", x),
None => "unreadable".to_string(),
}
}
fn opt_temp(v: Option<f64>) -> String {
match v {
Some(x) => format!("{:.1} C", x),
None => "unreadable".to_string(),
}
}
fn opt_watts(v: Option<f64>) -> String {
match v {
Some(x) => format!("{:.1} W", x),
None => "unreadable".to_string(),
}
}
fn opt_khz(v: Option<u64>) -> String {
match v {
Some(x) => format!("{:.3} GHz", x as f64 / 1_000_000.0),
None => "unreadable".to_string(),
}
}
fn opt_uj(v: Option<u64>) -> String {
match v {
Some(x) => format!("{} uJ", x),
None => "unreadable (root-only without adlermon udev rule + adm group)".to_string(),
}
}