The CPU, cores (with performance levels where the OS reports them), OS, arch, build profile, load averages and power source, as the OS reports them; native only. wai_decode_bench's machine and load lines now come from it, unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
114 lines
5.1 KiB
Rust
114 lines
5.1 KiB
Rust
//! The machine a measurement ran on (`extensions/staged-measurement.md` §7.4, §11.1):
|
|
//! its processor, cores, operating system and architecture, the build's profile, its
|
|
//! load, and its power source, as the operating system reports them. Native only: a
|
|
//! browser reports none of these.
|
|
//!
|
|
//! Every reading is what the OS says, unparsed beyond trimming; a reading the OS does
|
|
//! not give is `None` (or "unknown"), never estimated.
|
|
|
|
/// The machine and build a measurement ran on.
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct MachineRecord {
|
|
/// The processor's name as the OS reports it, or `unknown CPU`.
|
|
pub cpu: String,
|
|
/// `N logical cores`, followed, where the OS reports performance levels, by each
|
|
/// level's core count and name in parentheses.
|
|
pub cores: String,
|
|
pub os: &'static str,
|
|
pub arch: &'static str,
|
|
/// `release`, or `DEBUG (numbers not meaningful)` for a build with debug assertions.
|
|
pub build: &'static str,
|
|
}
|
|
|
|
impl std::fmt::Display for MachineRecord {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}, {}; {}-{}; {} build", self.cpu, self.cores, self.os, self.arch, self.build)
|
|
}
|
|
}
|
|
|
|
fn sh(cmd: &str, args: &[&str]) -> Option<String> {
|
|
let o = std::process::Command::new(cmd).args(args).output().ok()?;
|
|
o.status.success().then(|| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
|
}
|
|
|
|
/// The core types as the OS reports them, e.g. `6 Super + 12 Performance`: each macOS performance
|
|
/// level's logical-core count and its own name (`hw.perflevelN.name`), in the OS's
|
|
/// order. Names are reported as given, not inferred from the level's index, since what
|
|
/// level 0 and 1 are differs between chips. `None` where the OS does not report levels.
|
|
fn core_levels() -> Option<String> {
|
|
let n: usize = sh("sysctl", &["-n", "hw.nperflevels"])?.parse().ok()?;
|
|
let parts: Vec<String> = (0..n)
|
|
.map(|i| {
|
|
let cpus = sh("sysctl", &["-n", &format!("hw.perflevel{i}.logicalcpu")])?;
|
|
let name = sh("sysctl", &["-n", &format!("hw.perflevel{i}.name")]).unwrap_or_else(|| format!("level-{i}"));
|
|
Some(format!("{cpus} {name}"))
|
|
})
|
|
.collect::<Option<_>>()?;
|
|
(!parts.is_empty()).then(|| parts.join(" + "))
|
|
}
|
|
|
|
/// This machine and build.
|
|
pub fn machine() -> MachineRecord {
|
|
let cpu = sh("sysctl", &["-n", "machdep.cpu.brand_string"])
|
|
.or_else(|| {
|
|
std::fs::read_to_string("/proc/cpuinfo").ok()?.lines().find(|l| l.starts_with("model name")).map(|l| l.split(':').nth(1).unwrap_or("").trim().to_string())
|
|
})
|
|
.unwrap_or_else(|| "unknown CPU".into());
|
|
let levels = core_levels().map(|l| format!(" ({l})")).unwrap_or_default();
|
|
MachineRecord {
|
|
cpu,
|
|
cores: format!("{} logical cores{levels}", std::thread::available_parallelism().map(|n| n.get()).unwrap_or(0)),
|
|
os: std::env::consts::OS,
|
|
arch: std::env::consts::ARCH,
|
|
build: if cfg!(debug_assertions) { "DEBUG (numbers not meaningful)" } else { "release" },
|
|
}
|
|
}
|
|
|
|
/// The 1-, 5- and 15-minute load averages, as the OS writes them.
|
|
pub fn loadavg() -> Option<(String, String, String)> {
|
|
let line = sh("sysctl", &["-n", "vm.loadavg"])
|
|
.map(|s| s.trim_matches(|c| c == '{' || c == '}' || c == ' ').to_string())
|
|
.or_else(|| std::fs::read_to_string("/proc/loadavg").ok())?;
|
|
let mut it = line.split_whitespace().map(str::to_string);
|
|
Some((it.next()?, it.next()?, it.next()?))
|
|
}
|
|
|
|
/// Where the machine draws power from, as the OS names it: macOS's `pmset` line
|
|
/// (`AC Power`, `Battery Power`), or on Linux `AC Power` when a mains supply is
|
|
/// online and `Battery Power` when only a battery reports. `None` when the OS says
|
|
/// nothing.
|
|
pub fn power_source() -> Option<String> {
|
|
if let Some(out) = sh("pmset", &["-g", "batt"]) {
|
|
let first = out.lines().next()?;
|
|
let (_, rest) = first.split_once('\'')?;
|
|
let (name, _) = rest.split_once('\'')?;
|
|
return Some(name.to_string());
|
|
}
|
|
let dir = std::fs::read_dir("/sys/class/power_supply").ok()?;
|
|
let mut battery = false;
|
|
for e in dir.flatten() {
|
|
let p = e.path();
|
|
let kind = std::fs::read_to_string(p.join("type")).unwrap_or_default();
|
|
match kind.trim() {
|
|
"Mains" if std::fs::read_to_string(p.join("online")).is_ok_and(|s| s.trim() == "1") => return Some("AC Power".into()),
|
|
"Battery" => battery = true,
|
|
_ => {}
|
|
}
|
|
}
|
|
battery.then(|| "Battery Power".into())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn the_record_prints_as_the_decode_bench_printed_it() {
|
|
let m = MachineRecord { cpu: "X".into(), cores: "8 logical cores (4 A + 4 B)".into(), os: "macos", arch: "aarch64", build: "release" };
|
|
assert_eq!(m.to_string(), "X, 8 logical cores (4 A + 4 B); macos-aarch64; release build");
|
|
// On this machine: the record reads, and it names this OS and architecture.
|
|
let here = machine();
|
|
assert_eq!((here.os, here.arch), (std::env::consts::OS, std::env::consts::ARCH));
|
|
assert!(here.cores.ends_with(')') || here.cores.ends_with("cores"), "{}", here.cores);
|
|
}
|
|
}
|