mirror of
https://github.com/RGBCube/superfreq
synced 2025-08-03 04:17:45 +00:00
Compare commits
No commits in common. "2dbc7478686543222255a4f10c3e1403eb7da5f8" and "15bcdd269cf9208ed756f1ce07ad6a1fca926158" have entirely different histories.
2dbc747868
...
15bcdd269c
6 changed files with 179 additions and 184 deletions
|
@ -53,10 +53,9 @@ impl CpuDelta {
|
||||||
let mut cpus = match &self.for_ {
|
let mut cpus = match &self.for_ {
|
||||||
Some(numbers) => {
|
Some(numbers) => {
|
||||||
let mut cpus = Vec::with_capacity(numbers.len());
|
let mut cpus = Vec::with_capacity(numbers.len());
|
||||||
let cache = cpu::CpuRescanCache::default();
|
|
||||||
|
|
||||||
for &number in numbers {
|
for &number in numbers {
|
||||||
cpus.push(cpu::Cpu::new(number, &cache)?);
|
cpus.push(cpu::Cpu::new(number)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
cpus
|
cpus
|
||||||
|
|
272
src/cpu.rs
272
src/cpu.rs
|
@ -1,50 +1,11 @@
|
||||||
use anyhow::{Context, bail};
|
use anyhow::{Context, bail};
|
||||||
use yansi::Paint as _;
|
use yansi::Paint as _;
|
||||||
|
|
||||||
use std::{cell::OnceCell, collections::HashMap, fmt, string::ToString};
|
use std::{fmt, string::ToString};
|
||||||
|
|
||||||
use crate::fs;
|
use crate::fs;
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq)]
|
|
||||||
pub struct CpuRescanCache {
|
|
||||||
stat: OnceCell<HashMap<u32, CpuStat>>,
|
|
||||||
temperatures: OnceCell<HashMap<u32, f64>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct CpuStat {
|
|
||||||
pub user: u64,
|
|
||||||
pub nice: u64,
|
|
||||||
pub system: u64,
|
|
||||||
pub idle: u64,
|
|
||||||
pub iowait: u64,
|
|
||||||
pub irq: u64,
|
|
||||||
pub softirq: u64,
|
|
||||||
pub steal: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CpuStat {
|
|
||||||
pub fn total(&self) -> u64 {
|
|
||||||
self.user
|
|
||||||
+ self.nice
|
|
||||||
+ self.system
|
|
||||||
+ self.idle
|
|
||||||
+ self.iowait
|
|
||||||
+ self.irq
|
|
||||||
+ self.softirq
|
|
||||||
+ self.steal
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn idle(&self) -> u64 {
|
|
||||||
self.idle + self.iowait
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn usage(&self) -> f64 {
|
|
||||||
1.0 - self.idle() as f64 / self.total() as f64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct Cpu {
|
pub struct Cpu {
|
||||||
pub number: u32,
|
pub number: u32,
|
||||||
|
|
||||||
|
@ -63,9 +24,35 @@ pub struct Cpu {
|
||||||
pub available_epbs: Vec<String>,
|
pub available_epbs: Vec<String>,
|
||||||
pub epb: Option<String>,
|
pub epb: Option<String>,
|
||||||
|
|
||||||
pub stat: CpuStat,
|
pub time_user: u64,
|
||||||
|
pub time_nice: u64,
|
||||||
|
pub time_system: u64,
|
||||||
|
pub time_idle: u64,
|
||||||
|
pub time_iowait: u64,
|
||||||
|
pub time_irq: u64,
|
||||||
|
pub time_softirq: u64,
|
||||||
|
pub time_steal: u64,
|
||||||
|
}
|
||||||
|
|
||||||
pub temperature: Option<f64>,
|
impl Cpu {
|
||||||
|
pub fn time_total(&self) -> u64 {
|
||||||
|
self.time_user
|
||||||
|
+ self.time_nice
|
||||||
|
+ self.time_system
|
||||||
|
+ self.time_idle
|
||||||
|
+ self.time_iowait
|
||||||
|
+ self.time_irq
|
||||||
|
+ self.time_softirq
|
||||||
|
+ self.time_steal
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn time_idle(&self) -> u64 {
|
||||||
|
self.time_idle + self.time_iowait
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn usage(&self) -> f64 {
|
||||||
|
1.0 - self.time_idle() as f64 / self.time_total() as f64
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Cpu {
|
impl fmt::Display for Cpu {
|
||||||
|
@ -77,7 +64,7 @@ impl fmt::Display for Cpu {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cpu {
|
impl Cpu {
|
||||||
pub fn new(number: u32, cache: &CpuRescanCache) -> anyhow::Result<Self> {
|
pub fn new(number: u32) -> anyhow::Result<Self> {
|
||||||
let mut cpu = Self {
|
let mut cpu = Self {
|
||||||
number,
|
number,
|
||||||
has_cpufreq: false,
|
has_cpufreq: false,
|
||||||
|
@ -95,20 +82,16 @@ impl Cpu {
|
||||||
available_epbs: Vec::new(),
|
available_epbs: Vec::new(),
|
||||||
epb: None,
|
epb: None,
|
||||||
|
|
||||||
stat: CpuStat {
|
time_user: 0,
|
||||||
user: 0,
|
time_nice: 0,
|
||||||
nice: 0,
|
time_system: 0,
|
||||||
system: 0,
|
time_idle: 0,
|
||||||
idle: 0,
|
time_iowait: 0,
|
||||||
iowait: 0,
|
time_irq: 0,
|
||||||
irq: 0,
|
time_softirq: 0,
|
||||||
softirq: 0,
|
time_steal: 0,
|
||||||
steal: 0,
|
|
||||||
},
|
|
||||||
|
|
||||||
temperature: None,
|
|
||||||
};
|
};
|
||||||
cpu.rescan(cache)?;
|
cpu.rescan()?;
|
||||||
|
|
||||||
Ok(cpu)
|
Ok(cpu)
|
||||||
}
|
}
|
||||||
|
@ -118,14 +101,11 @@ impl Cpu {
|
||||||
const PATH: &str = "/sys/devices/system/cpu";
|
const PATH: &str = "/sys/devices/system/cpu";
|
||||||
|
|
||||||
let mut cpus = vec![];
|
let mut cpus = vec![];
|
||||||
let cache = CpuRescanCache::default();
|
|
||||||
|
|
||||||
for entry in fs::read_dir(PATH)
|
for entry in fs::read_dir(PATH)
|
||||||
.with_context(|| format!("failed to read CPU entries from '{PATH}'"))?
|
.with_context(|| format!("failed to read contents of '{PATH}'"))?
|
||||||
.with_context(|| format!("'{PATH}' doesn't exist, are you on linux?"))?
|
.flatten()
|
||||||
{
|
{
|
||||||
let entry = entry.with_context(|| format!("failed to read entry of '{PATH}'"))?;
|
|
||||||
|
|
||||||
let entry_file_name = entry.file_name();
|
let entry_file_name = entry.file_name();
|
||||||
|
|
||||||
let Some(name) = entry_file_name.to_str() else {
|
let Some(name) = entry_file_name.to_str() else {
|
||||||
|
@ -141,13 +121,13 @@ impl Cpu {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
cpus.push(Self::new(number, &cache)?);
|
cpus.push(Self::new(number)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back if sysfs iteration above fails to find any cpufreq CPUs.
|
// Fall back if sysfs iteration above fails to find any cpufreq CPUs.
|
||||||
if cpus.is_empty() {
|
if cpus.is_empty() {
|
||||||
for number in 0..num_cpus::get() as u32 {
|
for number in 0..num_cpus::get() as u32 {
|
||||||
cpus.push(Self::new(number, &cache)?);
|
cpus.push(Self::new(number)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +135,7 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rescan CPU, tuning local copy of settings.
|
/// Rescan CPU, tuning local copy of settings.
|
||||||
pub fn rescan(&mut self, cache: &CpuRescanCache) -> anyhow::Result<()> {
|
pub fn rescan(&mut self) -> anyhow::Result<()> {
|
||||||
let Self { number, .. } = self;
|
let Self { number, .. } = self;
|
||||||
|
|
||||||
if !fs::exists(format!("/sys/devices/system/cpu/cpu{number}")) {
|
if !fs::exists(format!("/sys/devices/system/cpu/cpu{number}")) {
|
||||||
|
@ -164,6 +144,8 @@ impl Cpu {
|
||||||
|
|
||||||
self.has_cpufreq = fs::exists(format!("/sys/devices/system/cpu/cpu{number}/cpufreq"));
|
self.has_cpufreq = fs::exists(format!("/sys/devices/system/cpu/cpu{number}/cpufreq"));
|
||||||
|
|
||||||
|
self.rescan_times()?;
|
||||||
|
|
||||||
if self.has_cpufreq {
|
if self.has_cpufreq {
|
||||||
self.rescan_governor()?;
|
self.rescan_governor()?;
|
||||||
self.rescan_frequency()?;
|
self.rescan_frequency()?;
|
||||||
|
@ -171,8 +153,66 @@ impl Cpu {
|
||||||
self.rescan_epb()?;
|
self.rescan_epb()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.rescan_stat(cache)?;
|
Ok(())
|
||||||
self.rescan_temperature(cache)?;
|
}
|
||||||
|
|
||||||
|
fn rescan_times(&mut self) -> anyhow::Result<()> {
|
||||||
|
// TODO: Don't read this per CPU. Share the read or
|
||||||
|
// find something in /sys/.../cpu{N} that does it.
|
||||||
|
let content = fs::read("/proc/stat")
|
||||||
|
.context("failed to read CPU stat")?
|
||||||
|
.context("/proc/stat does not exist")?;
|
||||||
|
|
||||||
|
let cpu_name = format!("cpu{number}", number = self.number);
|
||||||
|
|
||||||
|
let mut stats = content
|
||||||
|
.lines()
|
||||||
|
.find_map(|line| {
|
||||||
|
line.starts_with(&cpu_name)
|
||||||
|
.then(|| line.split_whitespace().skip(1))
|
||||||
|
})
|
||||||
|
.with_context(|| format!("failed to find {self} in CPU stats"))?;
|
||||||
|
|
||||||
|
self.time_user = stats
|
||||||
|
.next()
|
||||||
|
.with_context(|| format!("failed to parse {self} user time"))?
|
||||||
|
.parse()
|
||||||
|
.with_context(|| format!("failed to find {self} user time"))?;
|
||||||
|
self.time_nice = stats
|
||||||
|
.next()
|
||||||
|
.with_context(|| format!("failed to parse {self} nice time"))?
|
||||||
|
.parse()
|
||||||
|
.with_context(|| format!("failed to find {self} nice time"))?;
|
||||||
|
self.time_system = stats
|
||||||
|
.next()
|
||||||
|
.with_context(|| format!("failed to parse {self} system time"))?
|
||||||
|
.parse()
|
||||||
|
.with_context(|| format!("failed to find {self} system time"))?;
|
||||||
|
self.time_idle = stats
|
||||||
|
.next()
|
||||||
|
.with_context(|| format!("failed to parse {self} idle time"))?
|
||||||
|
.parse()
|
||||||
|
.with_context(|| format!("failed to find {self} idle time"))?;
|
||||||
|
self.time_iowait = stats
|
||||||
|
.next()
|
||||||
|
.with_context(|| format!("failed to parse {self} iowait time"))?
|
||||||
|
.parse()
|
||||||
|
.with_context(|| format!("failed to find {self} iowait time"))?;
|
||||||
|
self.time_irq = stats
|
||||||
|
.next()
|
||||||
|
.with_context(|| format!("failed to parse {self} irq time"))?
|
||||||
|
.parse()
|
||||||
|
.with_context(|| format!("failed to find {self} irq time"))?;
|
||||||
|
self.time_softirq = stats
|
||||||
|
.next()
|
||||||
|
.with_context(|| format!("failed to parse {self} softirq time"))?
|
||||||
|
.parse()
|
||||||
|
.with_context(|| format!("failed to find {self} softirq time"))?;
|
||||||
|
self.time_steal = stats
|
||||||
|
.next()
|
||||||
|
.with_context(|| format!("failed to parse {self} steal time"))?
|
||||||
|
.parse()
|
||||||
|
.with_context(|| format!("failed to find {self} steal time"))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -301,106 +341,6 @@ impl Cpu {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rescan_stat(&mut self, cache: &CpuRescanCache) -> anyhow::Result<()> {
|
|
||||||
// OnceCell::get_or_try_init is unstable. Cope:
|
|
||||||
let stat = match cache.stat.get() {
|
|
||||||
Some(stat) => stat,
|
|
||||||
|
|
||||||
None => {
|
|
||||||
let content = fs::read("/proc/stat")
|
|
||||||
.context("failed to read CPU stat")?
|
|
||||||
.context("/proc/stat does not exist")?;
|
|
||||||
|
|
||||||
cache
|
|
||||||
.stat
|
|
||||||
.set(HashMap::from_iter(content.lines().skip(1).filter_map(
|
|
||||||
|line| {
|
|
||||||
let mut parts = line.strip_prefix("cpu")?.split_whitespace();
|
|
||||||
|
|
||||||
let number = parts.next()?.parse().ok()?;
|
|
||||||
|
|
||||||
let stat = CpuStat {
|
|
||||||
user: parts.next()?.parse().ok()?,
|
|
||||||
nice: parts.next()?.parse().ok()?,
|
|
||||||
system: parts.next()?.parse().ok()?,
|
|
||||||
idle: parts.next()?.parse().ok()?,
|
|
||||||
iowait: parts.next()?.parse().ok()?,
|
|
||||||
irq: parts.next()?.parse().ok()?,
|
|
||||||
softirq: parts.next()?.parse().ok()?,
|
|
||||||
steal: parts.next()?.parse().ok()?,
|
|
||||||
};
|
|
||||||
|
|
||||||
Some((number, stat))
|
|
||||||
},
|
|
||||||
)))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
cache.stat.get().unwrap()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.stat = stat
|
|
||||||
.get(&self.number)
|
|
||||||
.with_context(|| format!("failed to get stat of {self}"))?
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rescan_temperature(&mut self, cache: &CpuRescanCache) -> anyhow::Result<()> {
|
|
||||||
// OnceCell::get_or_try_init is unstable. Cope:
|
|
||||||
let temperatures = match cache.temperatures.get() {
|
|
||||||
Some(temperature) => temperature,
|
|
||||||
|
|
||||||
None => {
|
|
||||||
const PATH: &str = "/sys/class/hwmon";
|
|
||||||
|
|
||||||
let temperatures = HashMap::new();
|
|
||||||
|
|
||||||
for entry in fs::read_dir(PATH)
|
|
||||||
.with_context(|| format!("failed to read hardware information from '{PATH}'"))?
|
|
||||||
.with_context(|| format!("'{PATH}' doesn't exist, are you on linux?"))?
|
|
||||||
{
|
|
||||||
let entry =
|
|
||||||
entry.with_context(|| format!("failed to read entry of '{PATH}'"))?;
|
|
||||||
|
|
||||||
let entry_path = entry.path();
|
|
||||||
|
|
||||||
let Some(name) = fs::read(entry_path.join("name")).with_context(|| {
|
|
||||||
format!(
|
|
||||||
"failed to read name of hardware entry at '{path}'",
|
|
||||||
path = entry_path.display(),
|
|
||||||
)
|
|
||||||
})?
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
match &*name {
|
|
||||||
// Intel CPU temperature driver
|
|
||||||
"coretemp" => todo!(),
|
|
||||||
|
|
||||||
// AMD CPU temperature driver
|
|
||||||
// TODO: 'zenergy' can also report those stats, I think?
|
|
||||||
"k10temp" | "zenpower" | "amdgpu" => todo!(),
|
|
||||||
|
|
||||||
// Other CPU temperature drivers
|
|
||||||
_ if name.contains("cpu") || name.contains("temp") => todo!(),
|
|
||||||
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cache.temperatures.set(temperatures).unwrap();
|
|
||||||
cache.temperatures.get().unwrap()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.temperature = temperatures.get(&self.number).copied();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_governor(&mut self, governor: &str) -> anyhow::Result<()> {
|
pub fn set_governor(&mut self, governor: &str) -> anyhow::Result<()> {
|
||||||
let Self {
|
let Self {
|
||||||
number,
|
number,
|
||||||
|
|
18
src/fs.rs
18
src/fs.rs
|
@ -8,19 +8,11 @@ pub fn exists(path: impl AsRef<Path>) -> bool {
|
||||||
path.exists()
|
path.exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_dir(path: impl AsRef<Path>) -> anyhow::Result<Option<fs::ReadDir>> {
|
pub fn read_dir(path: impl AsRef<Path>) -> anyhow::Result<fs::ReadDir> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
|
|
||||||
match fs::read_dir(path) {
|
fs::read_dir(path)
|
||||||
Ok(entries) => Ok(Some(entries)),
|
.with_context(|| format!("failed to read directory '{path}'", path = path.display()))
|
||||||
|
|
||||||
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(None),
|
|
||||||
|
|
||||||
Err(error) => Err(error).context(format!(
|
|
||||||
"failed to read directory '{path}'",
|
|
||||||
path = path.display()
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(path: impl AsRef<Path>) -> anyhow::Result<Option<String>> {
|
pub fn read(path: impl AsRef<Path>) -> anyhow::Result<Option<String>> {
|
||||||
|
@ -31,7 +23,9 @@ pub fn read(path: impl AsRef<Path>) -> anyhow::Result<Option<String>> {
|
||||||
|
|
||||||
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(None),
|
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(None),
|
||||||
|
|
||||||
Err(error) => Err(error).context(format!("failed to read '{path}", path = path.display())),
|
Err(error) => {
|
||||||
|
Err(error).with_context(|| format!("failed to read '{path}", path = path.display()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,11 @@ pub fn get_system_info() -> SystemInfo {
|
||||||
SystemInfo { cpu_model }
|
SystemInfo { cpu_model }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cpu_core_info(core_id: u32) -> anyhow::Result<CpuCoreInfo> {
|
pub fn get_cpu_core_info(
|
||||||
|
core_id: u32,
|
||||||
|
prev_times: &CpuTimes,
|
||||||
|
current_times: &CpuTimes,
|
||||||
|
) -> anyhow::Result<CpuCoreInfo> {
|
||||||
// Temperature detection.
|
// Temperature detection.
|
||||||
// Should be generic enough to be able to support for multiple hardware sensors
|
// Should be generic enough to be able to support for multiple hardware sensors
|
||||||
// with the possibility of extending later down the road.
|
// with the possibility of extending later down the road.
|
||||||
|
@ -183,6 +187,65 @@ fn get_fallback_temperature(hw_path: &Path) -> Option<f32> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_all_cpu_core_info() -> anyhow::Result<Vec<CpuCoreInfo>> {
|
||||||
|
let initial_cpu_times = read_all_cpu_times()?;
|
||||||
|
thread::sleep(Duration::from_millis(250)); // interval for CPU usage calculation
|
||||||
|
let final_cpu_times = read_all_cpu_times()?;
|
||||||
|
|
||||||
|
let num_cores = get_real_cpus()
|
||||||
|
.map_err(|_| SysMonitorError::ReadError("Could not get the number of cores".to_string()))?;
|
||||||
|
|
||||||
|
let mut core_infos = Vec::with_capacity(num_cores as usize);
|
||||||
|
|
||||||
|
for core_id in 0..num_cores {
|
||||||
|
if let (Some(prev), Some(curr)) = (
|
||||||
|
initial_cpu_times.get(&core_id),
|
||||||
|
final_cpu_times.get(&core_id),
|
||||||
|
) {
|
||||||
|
match get_cpu_core_info(core_id, prev, curr) {
|
||||||
|
Ok(info) => core_infos.push(info),
|
||||||
|
Err(e) => {
|
||||||
|
// Log or handle error for a single core, maybe push a partial info or skip
|
||||||
|
eprintln!("Error getting info for core {core_id}: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Log or handle missing times for a core
|
||||||
|
eprintln!("Missing CPU time data for core {core_id}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(core_infos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo {
|
||||||
|
// Calculate average CPU temperature from the core temperatures
|
||||||
|
let average_temperature_celsius = if cpu_cores.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// Filter cores with temperature readings, then calculate average
|
||||||
|
let cores_with_temp: Vec<&CpuCoreInfo> = cpu_cores
|
||||||
|
.iter()
|
||||||
|
.filter(|core| core.temperature_celsius.is_some())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if cores_with_temp.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// Sum up all temperatures and divide by count
|
||||||
|
let sum: f32 = cores_with_temp
|
||||||
|
.iter()
|
||||||
|
.map(|core| core.temperature_celsius.unwrap())
|
||||||
|
.sum();
|
||||||
|
Some(sum / cores_with_temp.len() as f32)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return the constructed CpuGlobalInfo
|
||||||
|
CpuGlobalInfo {
|
||||||
|
average_temperature_celsius,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_cpu_model() -> anyhow::Result<String> {
|
pub fn get_cpu_model() -> anyhow::Result<String> {
|
||||||
let path = Path::new("/proc/cpuinfo");
|
let path = Path::new("/proc/cpuinfo");
|
||||||
let content = fs::read_to_string(path).map_err(|_| {
|
let content = fs::read_to_string(path).map_err(|_| {
|
||||||
|
|
|
@ -155,7 +155,6 @@ impl PowerSupply {
|
||||||
|
|
||||||
for entry in fs::read_dir(POWER_SUPPLY_PATH)
|
for entry in fs::read_dir(POWER_SUPPLY_PATH)
|
||||||
.with_context(|| format!("failed to read '{POWER_SUPPLY_PATH}'"))?
|
.with_context(|| format!("failed to read '{POWER_SUPPLY_PATH}'"))?
|
||||||
.with_context(|| format!("'{POWER_SUPPLY_PATH}' doesn't exist, are you on linux?"))?
|
|
||||||
{
|
{
|
||||||
let entry = match entry {
|
let entry = match entry {
|
||||||
Ok(entry) => entry,
|
Ok(entry) => entry,
|
||||||
|
|
|
@ -99,8 +99,8 @@ impl System {
|
||||||
|
|
||||||
fn rescan_load_average(&mut self) -> anyhow::Result<()> {
|
fn rescan_load_average(&mut self) -> anyhow::Result<()> {
|
||||||
let content = fs::read("/proc/loadavg")
|
let content = fs::read("/proc/loadavg")
|
||||||
.context("failed to read load average from '/proc/loadavg'")?
|
.context("failed to read load average")?
|
||||||
.context("'/proc/loadavg' doesn't exist, are you on linux?")?;
|
.context("load average file doesn't exist, are you on linux?")?;
|
||||||
|
|
||||||
let mut parts = content.split_whitespace();
|
let mut parts = content.split_whitespace();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue