mirror of
https://github.com/RGBCube/superfreq
synced 2025-07-27 17:07:44 +00:00
cpu: cpu times
This commit is contained in:
parent
137f801d2b
commit
1ab9aceced
4 changed files with 131 additions and 137 deletions
105
src/cpu.rs
105
src/cpu.rs
|
@ -5,10 +5,37 @@ use std::{fmt, string::ToString};
|
|||
|
||||
use crate::fs;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Cpu {
|
||||
pub number: u32,
|
||||
|
||||
pub has_cpufreq: bool,
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Cpu {
|
||||
|
@ -24,6 +51,15 @@ impl Cpu {
|
|||
let mut cpu = Self {
|
||||
number,
|
||||
has_cpufreq: false,
|
||||
|
||||
time_user: 0,
|
||||
time_nice: 0,
|
||||
time_system: 0,
|
||||
time_idle: 0,
|
||||
time_iowait: 0,
|
||||
time_irq: 0,
|
||||
time_softirq: 0,
|
||||
time_steal: 0,
|
||||
};
|
||||
cpu.rescan()?;
|
||||
|
||||
|
@ -76,9 +112,68 @@ impl Cpu {
|
|||
bail!("{self} does not exist");
|
||||
}
|
||||
|
||||
let 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.has_cpufreq = has_cpufreq;
|
||||
self.rescan_times()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rescan_times(&mut self) -> anyhow::Result<()> {
|
||||
let content = fs::read("/proc/stat")
|
||||
.context("/proc/stat does not exist")?
|
||||
.context("failed to read CPU stat")?;
|
||||
|
||||
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 find {self} user time"))?
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse {self} user time"))?;
|
||||
self.time_nice = stats
|
||||
.next()
|
||||
.with_context(|| format!("failed to find {self} nice time"))?
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse {self} nice time"))?;
|
||||
self.time_system = stats
|
||||
.next()
|
||||
.with_context(|| format!("failed to find {self} system time"))?
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse {self} system time"))?;
|
||||
self.time_idle = stats
|
||||
.next()
|
||||
.with_context(|| format!("failed to find {self} idle time"))?
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse {self} idle time"))?;
|
||||
self.time_iowait = stats
|
||||
.next()
|
||||
.with_context(|| format!("failed to find {self} iowait time"))?
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse {self} iowait time"))?;
|
||||
self.time_irq = stats
|
||||
.next()
|
||||
.with_context(|| format!("failed to find {self} irq time"))?
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse {self} irq time"))?;
|
||||
self.time_softirq = stats
|
||||
.next()
|
||||
.with_context(|| format!("failed to find {self} softirq time"))?
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse {self} softirq time"))?;
|
||||
self.time_steal = stats
|
||||
.next()
|
||||
.with_context(|| format!("failed to find {self} steal time"))?
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse {self} steal time"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -232,7 +327,7 @@ impl Cpu {
|
|||
fn validate_frequency_minimum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> {
|
||||
let Self { number, .. } = self;
|
||||
|
||||
let Ok(minimum_frequency_khz) = fs::read_u64(format!(
|
||||
let Some(Ok(minimum_frequency_khz)) = fs::read_u64(format!(
|
||||
"/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"
|
||||
)) else {
|
||||
// Just let it pass if we can't find anything.
|
||||
|
@ -270,7 +365,7 @@ impl Cpu {
|
|||
fn validate_frequency_maximum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> {
|
||||
let Self { number, .. } = self;
|
||||
|
||||
let Ok(maximum_frequency_khz) = fs::read_u64(format!(
|
||||
let Some(Ok(maximum_frequency_khz)) = fs::read_u64(format!(
|
||||
"/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"
|
||||
)) else {
|
||||
// Just let it pass if we can't find anything.
|
||||
|
|
105
src/monitor.rs
105
src/monitor.rs
|
@ -43,111 +43,6 @@ pub fn get_system_info() -> SystemInfo {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct CpuTimes {
|
||||
user: u64,
|
||||
nice: u64,
|
||||
system: u64,
|
||||
idle: u64,
|
||||
iowait: u64,
|
||||
irq: u64,
|
||||
softirq: u64,
|
||||
steal: u64,
|
||||
}
|
||||
|
||||
impl CpuTimes {
|
||||
const fn total_time(&self) -> u64 {
|
||||
self.user
|
||||
+ self.nice
|
||||
+ self.system
|
||||
+ self.idle
|
||||
+ self.iowait
|
||||
+ self.irq
|
||||
+ self.softirq
|
||||
+ self.steal
|
||||
}
|
||||
|
||||
const fn idle_time(&self) -> u64 {
|
||||
self.idle + self.iowait
|
||||
}
|
||||
}
|
||||
|
||||
fn read_all_cpu_times() -> anyhow::Result<HashMap<u32, CpuTimes>> {
|
||||
let content = fs::read_to_string("/proc/stat").map_err(SysMonitorError::Io)?;
|
||||
let mut cpu_times_map = HashMap::new();
|
||||
|
||||
for line in content.lines() {
|
||||
if line.starts_with("cpu") && line.chars().nth(3).is_some_and(|c| c.is_ascii_digit()) {
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.len() < 11 {
|
||||
return Err(SysMonitorError::ProcStatParseError(format!(
|
||||
"Line too short: {line}"
|
||||
)));
|
||||
}
|
||||
|
||||
let core_id_str = &parts[0][3..];
|
||||
let core_id = core_id_str.parse::<u32>().map_err(|_| {
|
||||
SysMonitorError::ProcStatParseError(format!(
|
||||
"Failed to parse core_id: {core_id_str}"
|
||||
))
|
||||
})?;
|
||||
|
||||
let times = CpuTimes {
|
||||
user: parts[1].parse().map_err(|_| {
|
||||
SysMonitorError::ProcStatParseError(format!(
|
||||
"Failed to parse user time: {}",
|
||||
parts[1]
|
||||
))
|
||||
})?,
|
||||
nice: parts[2].parse().map_err(|_| {
|
||||
SysMonitorError::ProcStatParseError(format!(
|
||||
"Failed to parse nice time: {}",
|
||||
parts[2]
|
||||
))
|
||||
})?,
|
||||
system: parts[3].parse().map_err(|_| {
|
||||
SysMonitorError::ProcStatParseError(format!(
|
||||
"Failed to parse system time: {}",
|
||||
parts[3]
|
||||
))
|
||||
})?,
|
||||
idle: parts[4].parse().map_err(|_| {
|
||||
SysMonitorError::ProcStatParseError(format!(
|
||||
"Failed to parse idle time: {}",
|
||||
parts[4]
|
||||
))
|
||||
})?,
|
||||
iowait: parts[5].parse().map_err(|_| {
|
||||
SysMonitorError::ProcStatParseError(format!(
|
||||
"Failed to parse iowait time: {}",
|
||||
parts[5]
|
||||
))
|
||||
})?,
|
||||
irq: parts[6].parse().map_err(|_| {
|
||||
SysMonitorError::ProcStatParseError(format!(
|
||||
"Failed to parse irq time: {}",
|
||||
parts[6]
|
||||
))
|
||||
})?,
|
||||
softirq: parts[7].parse().map_err(|_| {
|
||||
SysMonitorError::ProcStatParseError(format!(
|
||||
"Failed to parse softirq time: {}",
|
||||
parts[7]
|
||||
))
|
||||
})?,
|
||||
steal: parts[8].parse().map_err(|_| {
|
||||
SysMonitorError::ProcStatParseError(format!(
|
||||
"Failed to parse steal time: {}",
|
||||
parts[8]
|
||||
))
|
||||
})?,
|
||||
};
|
||||
cpu_times_map.insert(core_id, times);
|
||||
}
|
||||
}
|
||||
Ok(cpu_times_map)
|
||||
}
|
||||
|
||||
pub fn get_cpu_core_info(
|
||||
core_id: u32,
|
||||
prev_times: &CpuTimes,
|
||||
|
|
|
@ -146,7 +146,7 @@ impl PowerSupply {
|
|||
bail!("{self} does not exist");
|
||||
}
|
||||
|
||||
let threshold_config = self
|
||||
self.threshold_config = self
|
||||
.get_type()
|
||||
.with_context(|| format!("failed to determine what type of power supply '{self}' is"))?
|
||||
.eq("Battery")
|
||||
|
@ -163,8 +163,6 @@ impl PowerSupply {
|
|||
})
|
||||
.flatten();
|
||||
|
||||
self.threshold_config = threshold_config;
|
||||
|
||||
self.is_from_peripheral = 'is_from_peripheral: {
|
||||
let name_lower = self.name.to_lowercase();
|
||||
|
||||
|
|
|
@ -26,17 +26,13 @@ impl System {
|
|||
}
|
||||
|
||||
pub fn rescan(&mut self) -> anyhow::Result<()> {
|
||||
self.is_desktop = self.is_desktop()?;
|
||||
|
||||
let (load_average_1min, load_average_5min, load_average_15min) = self.load_average()?;
|
||||
self.load_average_1min = load_average_1min;
|
||||
self.load_average_5min = load_average_5min;
|
||||
self.load_average_15min = load_average_15min;
|
||||
self.rescan_is_desktop()?;
|
||||
self.rescan_load_average()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_desktop(&self) -> anyhow::Result<bool> {
|
||||
fn rescan_is_desktop(&mut self) -> anyhow::Result<()> {
|
||||
if let Some(chassis_type) = fs::read("/sys/class/dmi/id/chassis_type") {
|
||||
let chassis_type = chassis_type.context("failed to read chassis type")?;
|
||||
|
||||
|
@ -45,9 +41,16 @@ impl System {
|
|||
// 14=Sub Notebook, 15=Space-saving, 16=Lunch Box, 17=Main Server Chassis
|
||||
match chassis_type.trim() {
|
||||
// Desktop form factors.
|
||||
"3" | "4" | "5" | "6" | "7" | "15" | "16" | "17" => return Ok(true),
|
||||
"3" | "4" | "5" | "6" | "7" | "15" | "16" | "17" => {
|
||||
self.is_desktop = true;
|
||||
return Ok(());
|
||||
}
|
||||
// Laptop form factors.
|
||||
"9" | "10" | "14" => return Ok(false),
|
||||
"9" | "10" | "14" => {
|
||||
self.is_desktop = false;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Unknown, continue with other checks
|
||||
_ => {}
|
||||
}
|
||||
|
@ -58,7 +61,8 @@ impl System {
|
|||
|| fs::exists("/sys/devices/system/cpu/cpufreq/conservative");
|
||||
|
||||
if !power_saving_exists {
|
||||
return Ok(true); // Likely a desktop.
|
||||
self.is_desktop = true;
|
||||
return Ok(()); // Likely a desktop.
|
||||
}
|
||||
|
||||
// Check battery-specific ACPI paths that laptops typically have
|
||||
|
@ -70,15 +74,17 @@ impl System {
|
|||
|
||||
for path in laptop_acpi_paths {
|
||||
if fs::exists(path) {
|
||||
return Ok(false); // Likely a laptop.
|
||||
self.is_desktop = false; // Likely a laptop.
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// Default to assuming desktop if we can't determine.
|
||||
Ok(true)
|
||||
self.is_desktop = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_average(&self) -> anyhow::Result<(f64, f64, f64)> {
|
||||
fn rescan_load_average(&mut self) -> anyhow::Result<()> {
|
||||
let content = fs::read("/proc/loadavg")
|
||||
.context("load average file doesn't exist, are you on linux?")?
|
||||
.context("failed to read load average")?;
|
||||
|
@ -93,16 +99,16 @@ impl System {
|
|||
);
|
||||
};
|
||||
|
||||
Ok((
|
||||
load_average_1min
|
||||
self.load_average_1min = load_average_1min
|
||||
.parse()
|
||||
.context("failed to parse load average")?,
|
||||
load_average_5min
|
||||
.context("failed to parse load average")?;
|
||||
self.load_average_5min = load_average_5min
|
||||
.parse()
|
||||
.context("failed to parse load average")?,
|
||||
load_average_15min
|
||||
.context("failed to parse load average")?;
|
||||
self.load_average_15min = load_average_15min
|
||||
.parse()
|
||||
.context("failed to parse load average")?,
|
||||
))
|
||||
.context("failed to parse load average")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue