Skip to content

Commit

Permalink
Merge pull request #443 from nokyan/better-cpu-temp-detection
Browse files Browse the repository at this point in the history
Better CPU temp detection
  • Loading branch information
nokyan authored Feb 5, 2025
2 parents 9021487 + 0c03fed commit cd92d1e
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 36 deletions.
93 changes: 60 additions & 33 deletions src/utils/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use glob::glob;
use lazy_regex::{lazy_regex, Lazy, Regex};
use log::{debug, trace, warn};
use std::{
collections::HashMap,
path::{Path, PathBuf},
sync::LazyLock,
};
Expand All @@ -28,54 +29,80 @@ static RE_LSCPU_VIRTUALIZATION: Lazy<Regex> = lazy_regex!(r"Virtualization:\s*(.
static RE_LSCPU_MAX_MHZ: Lazy<Regex> = lazy_regex!(r"CPU max MHz:\s*(.*)");

static RE_PROC_STAT: Lazy<Regex> = lazy_regex!(
r"cpu[0-9]+ *(?P<user>[0-9]*) *(?P<nice>[0-9]*) *(?P<system>[0-9]*) *(?P<idle>[0-9]*) *(?P<iowait>[0-9]*) *(?P<irq>[0-9]*) *(?P<softirq>[0-9]*) *(?P<steal>[0-9]*) *(?P<guest>[0-9]*) *(?P<guest_nice>[0-9]*)"
r"cpu\d+ *(?P<user>\d*) *(?P<nice>\d*) *(?P<system>\d*) *(?P<idle>\d*) *(?P<iowait>\d*) *(?P<irq>\d*) *(?P<softirq>\d*) *(?P<steal>\d*) *(?P<guest>\d*) *(?P<guest_nice>\d*)"
);

static CPU_TEMPERATURE_PATH: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
let cpu_temperature_path =
search_for_hwmons(KNOWN_HWMONS).or_else(|| search_for_thermal_zones(KNOWN_THERMAL_ZONES));

if let Some((sensor, path)) = &cpu_temperature_path {
debug!(
"CPU temperature sensor located at {} ({sensor})",
path.display()
);
} else {
warn!("No sensor for CPU temperature found!");
}

cpu_temperature_path.map(|(_, path)| path)
search_for_hwmons(KNOWN_HWMONS)
.or_else(|| search_for_thermal_zones(KNOWN_THERMAL_ZONES))
.or_else(|| {
warn!("No CPU temperature sensor found!");
None
})
});

/// Looks for hwmons with the given names.
/// This function is a bit inefficient since the `names` array is considered to be ordered by priority.
fn search_for_hwmons(names: &[&'static str]) -> Option<(&'static str, PathBuf)> {
for temp_name in names {
for path in (glob("/sys/class/hwmon/hwmon*").unwrap()).flatten() {
if let Ok(read_name) = std::fs::read_to_string(path.join("name")) {
if &read_name.trim_end() == temp_name {
return Some((temp_name, path.join("temp1_input")));
}
fn search_for_first_temp<S: AsRef<str>>(base_path: S) -> Option<PathBuf> {
glob(&format!("{}/temp*_input", base_path.as_ref()))
.unwrap()
.flatten()
.next()
}

fn search_for_hwmons(names: &[&'static str]) -> Option<PathBuf> {
let mut hwmon_map = HashMap::new();

trace!("Collecting hwmons for CPU temperature…");

for path in (glob("/sys/class/hwmon/hwmon*").unwrap()).flatten() {
trace!("Found hwmon {path:?}");
if let Ok(read_name) = std::fs::read_to_string(path.join("name")) {
let read_name = read_name.trim_end().to_string();
trace!("{path:?} is a hwmon for {read_name}");
if let Some(first_temp) = search_for_first_temp(path.to_string_lossy()) {
hwmon_map.insert(read_name, first_temp);
}
}
}

for name in names {
if let Some(hwmon) = hwmon_map.remove(*name) {
debug!(
"CPU temperature sensor located at {:?} ({name}, type: hwmon)",
hwmon
);
return Some(hwmon);
}
}

trace!("No hwmon CPU temperature sensor found");
None
}

/// Looks for thermal zones with the given types.
/// This function is a bit inefficient since the `types` array is considered to be ordered by priority.
fn search_for_thermal_zones(types: &[&'static str]) -> Option<(&'static str, PathBuf)> {
for temp_type in types {
for path in (glob("/sys/class/thermal/thermal_zone*").unwrap()).flatten() {
if let Ok(read_type) = std::fs::read_to_string(path.join("type")) {
if &read_type.trim_end() == temp_type {
return Some((temp_type, path.join("temp")));
}
}
fn search_for_thermal_zones(types: &[&'static str]) -> Option<PathBuf> {
let mut thermal_zone_map = HashMap::new();

trace!("Collecting thermal zones for CPU temperature…");

for path in (glob("/sys/class/thermal/thermal_zone*").unwrap()).flatten() {
trace!("Found thermal zone {path:?}");
if let Ok(read_type) = std::fs::read_to_string(path.join("type")) {
let read_type = read_type.trim_end().to_string();
trace!("{path:?} is a thermal zone for {read_type}");
thermal_zone_map.insert(read_type, path.join("temp"));
}
}

for r#type in types {
if let Some(hwmon) = thermal_zone_map.remove(*r#type) {
debug!(
"CPU temperature sensor located at {:?} ({}, type: thermal zone)",
hwmon, r#type
);
return Some(hwmon);
}
}

trace!("No thermal zone CPU temperature sensor found");
None
}

Expand Down
4 changes: 1 addition & 3 deletions src/utils/gpu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,7 @@ impl Gpu {
let mut hwmon_vec: Vec<PathBuf> = Vec::new();
for hwmon in glob(&format!(
"{}/hwmon/hwmon?",
sysfs_device_path
.to_str()
.context("error transforming PathBuf to str")?
sysfs_device_path.to_string_lossy()
))?
.flatten()
{
Expand Down

0 comments on commit cd92d1e

Please sign in to comment.