81 lines
2.3 KiB
Python
81 lines
2.3 KiB
Python
import glob
|
|
|
|
import psutil
|
|
|
|
from app.sample import Sample
|
|
from app.utils import sysfs
|
|
|
|
_temp_path: str | None = None
|
|
_temp_checked = False
|
|
|
|
|
|
def _find_temp_path() -> str | None:
|
|
"""Find the sysfs file reporting CPU temperature, in millidegrees.
|
|
|
|
Prefers hwmon sensors named k10temp (AMD), coretemp (Intel), or
|
|
cpu_thermal (ARM), taking the first temp*_input of the first matching
|
|
hwmon; falls back to the acpitz thermal zone. The result is cached by
|
|
temp() for the process lifetime.
|
|
|
|
Returns:
|
|
The sysfs file to read, or None if no suitable sensor exists.
|
|
"""
|
|
for hwmon in sorted(glob.glob("/sys/class/hwmon/hwmon*")):
|
|
name = (sysfs.read_str(f"{hwmon}/name") or "").lower()
|
|
if name in ("k10temp", "coretemp", "cpu_thermal"):
|
|
for t in sorted(glob.glob(f"{hwmon}/temp*_input")):
|
|
return t
|
|
return None
|
|
for zone in sorted(glob.glob("/sys/class/thermal/thermal_zone*")):
|
|
if (sysfs.read_str(f"{zone}/type") or "").lower() == "acpitz":
|
|
return f"{zone}/temp"
|
|
return None
|
|
|
|
|
|
def temp() -> float | None:
|
|
"""Read the CPU temperature in degrees Celsius.
|
|
|
|
The sensor path is resolved once via _find_temp_path. Sysfs reports
|
|
millidegrees; the value is converted and rounded to 0.1 °C.
|
|
|
|
Returns:
|
|
Temperature in °C, or None if no sensor or unreadable value.
|
|
"""
|
|
global _temp_path, _temp_checked
|
|
if not _temp_checked:
|
|
_temp_checked = True
|
|
_temp_path = _find_temp_path()
|
|
if _temp_path is None:
|
|
return None
|
|
v = sysfs.read_float(_temp_path)
|
|
if v is None:
|
|
return None
|
|
return round(v / 1000.0, 1)
|
|
|
|
|
|
def prime() -> None:
|
|
"""Prime psutil's CPU percent counter so the next call has a real delta.
|
|
|
|
psutil.cpu_percent(None) returns 0.0 on its first call; sampler_loop
|
|
invokes this before the first sample for that reason.
|
|
"""
|
|
_ = psutil.cpu_percent(None)
|
|
|
|
|
|
def core_count() -> int:
|
|
"""Number of logical CPU cores (at least 1)."""
|
|
return psutil.cpu_count(logical=True) or 1
|
|
|
|
|
|
def fill(s: Sample) -> None:
|
|
"""Fill the cpu, cpu_temp, and load-average fields of a Sample.
|
|
|
|
Args:
|
|
s: sample to fill.
|
|
"""
|
|
s.cpu = psutil.cpu_percent(None)
|
|
s.cpu_temp = temp()
|
|
l1, l5, l15 = psutil.getloadavg()
|
|
s.load1 = l1
|
|
s.load5 = l5
|
|
s.load15 = l15
|