monitoring kit
Diffstat (limited to 'cpu/src/lib.rs')
-rw-r--r--cpu/src/lib.rs173
1 files changed, 173 insertions, 0 deletions
diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs
new file mode 100644
index 0000000..5ddb34c
--- /dev/null
+++ b/cpu/src/lib.rs
@@ -0,0 +1,173 @@
+#![feature(
+ let_chains,
+ iter_array_chunks,
+ array_chunks,
+ generic_const_exprs,
+ portable_simd,
+ iter_chain
+)]
+use anyhow::*;
+use atools::prelude::*;
+use collar::CollectArray;
+use parking_lot::Mutex;
+use std::collections::HashMap;
+use std::fmt::Display;
+use std::fs::{read_to_string as read, File};
+use std::io::Read;
+use std::io::Seek;
+use std::mem::replace;
+use std::path::Path;
+use std::sync::OnceLock;
+
+#[derive(Copy, Clone, Debug)]
+pub enum ViewCore {
+ All(u64),
+ One(u64),
+}
+impl Display for ViewCore {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ ViewCore::All(x) => write!(f, "..#{x}"),
+ ViewCore::One(x) => write!(f, "#{x}"),
+ }
+ }
+}
+
+pub static CORE: OnceLock<ViewCore> = OnceLock::new();
+
+#[derive(Debug, Clone, Default, PartialEq)]
+pub struct CpuInfo {
+ pub name: String,
+ pub speed: f64,
+ pub count: u64,
+}
+
+pub struct Temps(File);
+impl Temps {
+ pub fn load() -> Result<Self> {
+ for hwmon in std::fs::read_dir("/sys/class/hwmon/")?.filter_map(Result::ok) {
+ if !read(Path::join(&hwmon.path(), "name"))?.starts_with("coretemp") {
+ continue;
+ }
+ for f in std::fs::read_dir(hwmon.path())?.filter_map(Result::ok) {
+ if let Some(n) = f.file_name().to_str()
+ && n.starts_with("temp")
+ && n.ends_with("input")
+ {
+ let i = n
+ .bytes()
+ .filter(u8::is_ascii_digit)
+ .fold(0, |acc, x| acc * 10 + (x - b'0') as u64);
+
+ let name = read(Path::join(&hwmon.path(), format!("temp{i}_label")))
+ .context("alas")?;
+ if let Some(c) = core() {
+ if !name.contains(&c.to_string()) {
+ continue;
+ }
+ } else if !name.contains("Package") {
+ continue;
+ }
+
+ let f = File::open(f.path())?;
+ return Ok(Self(f));
+ }
+ }
+ }
+ bail!("h")
+ }
+ pub fn read(&mut self) -> Result<f64> {
+ let mut o = String::default();
+ self.0.seek(std::io::SeekFrom::Start(0))?;
+ self.0.read_to_string(&mut o)?;
+ Ok(o.trim().parse::<f64>().context("reading temps")? / 1000.0)
+ }
+}
+
+impl CpuInfo {
+ pub fn read() -> Result<Self> {
+ let x = String::from_utf8(
+ std::process::Command::new("lscpu")
+ .env("LC_ALL", "C")
+ .output()
+ .context("lscpuless")?
+ .stdout,
+ )
+ .context("unable to parse lscpu output to UTF-8")?;
+ let x = x
+ .lines()
+ .filter_map(|l| l.split_once(":").map(|(a, b)| (a, b.trim())))
+ .collect::<HashMap<_, _>>();
+ let name = x["Model name"]
+ .replace("Core", "")
+ .replace("(TM)", "")
+ .replace("Intel", "")
+ .replace("(R)", "");
+ let name = regex::Regex::new(r"@ [0-9]+\.[0-9]+GHz")
+ .unwrap()
+ .replace(&name, "");
+ let name = regex::Regex::new(r"[0-9]+th Gen")
+ .unwrap()
+ .replace(&name, "")
+ .replace(" ", " ")
+ .trim()
+ .replace(" ", " ")
+ .to_lowercase();
+ Ok(Self {
+ name: name,
+ speed: x["CPU max MHz"].parse::<f64>().context("cpu mhz??")? / 1000.0,
+ count: x["CPU(s)"].parse()?,
+ })
+ }
+}
+
+pub fn sped(core: u64) -> Result<f64> {
+ Ok(read(format!(
+ "/sys/devices/system/cpu/cpu{core}/cpufreq/scaling_cur_freq"
+ ))
+ .context("speed")?
+ .replace('\n', "")
+ .parse::<u64>()
+ .context("reading speeds")? as f64
+ / 1e6)
+}
+
+pub fn speed() -> Result<f64> {
+ Ok(match *CORE.get().unwrap() {
+ ViewCore::All(x) => (0..x).map(sped).sum::<Result<f64, _>>()? / x as f64,
+ ViewCore::One(x) => sped(x)?,
+ })
+}
+
+pub fn core() -> Option<u64> {
+ CORE.get().copied().and_then(|x| match x {
+ ViewCore::One(x) => Some(x),
+ _ => None,
+ })
+}
+
+pub fn usage() -> Result<f64> {
+ let x = read("/proc/stat")?;
+ let x = x
+ .lines()
+ .nth(core().map_or(0, |x| x as usize + 1))
+ .ok_or(anyhow!("no procstat"))?;
+
+ // https://www.linuxhowtos.org/System/procstat.htm
+ let x @ [_user, _nice, _system, idle, iowait, _irq, _softirq] = x
+ .split_whitespace()
+ .skip(1)
+ .map(|x| x.parse::<u64>())
+ .try_collect_array()?;
+
+ static LAST: Mutex<[u64; 2]> = Mutex::new([0; 2]);
+ let [pi, pt] = &mut *LAST.lock();
+
+ let idle = idle + iowait;
+ let tot = x.sum();
+
+ let idle = (idle - replace(pi, idle)) as f64;
+ let tot = (tot - replace(pt, tot)) as f64;
+ let r = (tot - idle) / tot;
+ Ok((r == r).then_some(r).unwrap_or(0.0))
+}