use std::fs;
use std::io;
use std::ops;
use std::path::{Path, PathBuf};
use heim_common::prelude::{Error, Result, Stream};
use heim_common::units::{frequency, Frequency};
use heim_runtime as rt;
#[derive(Debug, Default)]
pub struct CpuFrequency {
current: Frequency,
min: Option<Frequency>,
max: Option<Frequency>,
}
impl CpuFrequency {
pub fn current(&self) -> Frequency {
self.current
}
pub fn min(&self) -> Option<Frequency> {
self.min
}
pub fn max(&self) -> Option<Frequency> {
self.max
}
}
impl ops::Add<CpuFrequency> for CpuFrequency {
type Output = CpuFrequency;
fn add(self, rhs: CpuFrequency) -> CpuFrequency {
let current = self.current + rhs.current;
let min = match (self.min, rhs.min) {
(Some(left), Some(right)) => Some(left + right),
(Some(left), None) => Some(left),
(None, Some(right)) => Some(right),
(None, None) => None,
};
let max = match (self.max, rhs.max) {
(Some(left), Some(right)) => Some(left + right),
(Some(left), None) => Some(left),
(None, Some(right)) => Some(right),
(None, None) => None,
};
CpuFrequency { current, max, min }
}
}
fn _frequencies() -> impl Iterator<Item = Result<CpuFrequency>> {
let path = rt::linux::sysfs_root().join("devices/system/cpu/cpu[0-9]/cpufreq/");
let entries = glob::glob(path.display().to_string().as_str()).expect("Incorrect glob pattern");
entries.map(|try_path| {
let path = try_path.map_err(|e| e.into_error())?;
let current = current_freq(&path)?;
let max = max_freq(&path)?;
let min = min_freq(&path)?;
Ok(CpuFrequency { current, max, min })
})
}
pub fn frequencies() -> impl Stream<Item = Result<CpuFrequency>> {
smol::stream::iter(_frequencies())
}
pub async fn frequency() -> Result<CpuFrequency> {
rt::spawn_blocking(|| {
let mut acc = CpuFrequency::default();
let mut amount = 0;
for freq in _frequencies() {
let freq = freq?;
acc = acc + freq;
amount += 1;
}
if amount > 0 {
Ok(CpuFrequency {
current: acc.current / amount,
min: acc.min.map(|value| value / amount),
max: acc.max.map(|value| value / amount),
})
} else {
let inner = io::Error::from(io::ErrorKind::InvalidData);
Err(Error::from(inner).with_message("No CPU frequencies was found, running in VM?"))
}
})
.await
}
#[allow(clippy::redundant_closure)]
fn read_freq(path: PathBuf) -> Result<Frequency> {
let contents = fs::read_to_string(path)?;
let value = contents.trim_end().parse::<u64>()?;
Ok(Frequency::new::<frequency::kilohertz>(value))
}
fn current_freq(path: &Path) -> Result<Frequency> {
read_freq(path.join("scaling_cur_freq"))
}
fn max_freq(path: &Path) -> Result<Option<Frequency>> {
let value = read_freq(path.join("scaling_max_freq"));
Ok(value.ok())
}
fn min_freq(path: &Path) -> Result<Option<Frequency>> {
let value = read_freq(path.join("scaling_min_freq"));
Ok(value.ok())
}