Skip to main content

cbtop/frequency_control/
info.rs

1use super::governor::CpuGovernor;
2use super::sysfs::{read_sysfs_string, read_sysfs_value};
3
4/// CPU frequency info for a single CPU
5#[derive(Debug, Clone)]
6pub struct CpuFrequencyInfo {
7    /// CPU ID
8    pub cpu_id: usize,
9    /// Current frequency in kHz
10    pub current_khz: u64,
11    /// Minimum frequency in kHz
12    pub min_khz: u64,
13    /// Maximum frequency in kHz
14    pub max_khz: u64,
15    /// Current governor
16    pub governor: CpuGovernor,
17    /// Available governors
18    pub available_governors: Vec<CpuGovernor>,
19}
20
21impl CpuFrequencyInfo {
22    /// Convert kHz to MHz.
23    fn khz_to_mhz(khz: u64) -> f64 {
24        khz as f64 / 1_000.0
25    }
26
27    /// Get current frequency in MHz
28    pub fn current_mhz(&self) -> f64 {
29        Self::khz_to_mhz(self.current_khz)
30    }
31
32    /// Get current frequency in GHz
33    pub fn current_ghz(&self) -> f64 {
34        Self::khz_to_mhz(self.current_khz) / 1_000.0
35    }
36
37    /// Get frequency utilization (current / max)
38    pub fn utilization(&self) -> f64 {
39        if self.max_khz > 0 {
40            self.current_khz as f64 / self.max_khz as f64
41        } else {
42            1.0
43        }
44    }
45
46    /// Build a mock CpuFrequencyInfo for testing.
47    pub(crate) fn mock(cpu_id: usize, frequency_khz: u64, governor: CpuGovernor) -> Self {
48        Self {
49            cpu_id,
50            current_khz: frequency_khz,
51            min_khz: 800_000,
52            max_khz: 4_000_000,
53            governor,
54            available_governors: vec![CpuGovernor::Performance, CpuGovernor::Powersave],
55        }
56    }
57
58    /// Read CpuFrequencyInfo from sysfs for the given CPU.
59    pub(crate) fn from_sysfs(cpu_id: usize, sysfs_path: &std::path::Path) -> Self {
60        let cpu_path = sysfs_path.join(format!("cpu{cpu_id}/cpufreq"));
61
62        let read_khz = |name: &str| read_sysfs_value(&cpu_path.join(name)).unwrap_or(0);
63
64        let governor = read_sysfs_string(&cpu_path.join("scaling_governor"))
65            .map(|s| CpuGovernor::parse(&s))
66            .unwrap_or(CpuGovernor::Unknown);
67
68        let available_governors: Vec<CpuGovernor> =
69            read_sysfs_string(&cpu_path.join("scaling_available_governors"))
70                .unwrap_or_default()
71                .split_whitespace()
72                .map(CpuGovernor::parse)
73                .collect();
74
75        Self {
76            cpu_id,
77            current_khz: read_khz("scaling_cur_freq"),
78            min_khz: read_khz("scaling_min_freq"),
79            max_khz: read_khz("scaling_max_freq"),
80            governor,
81            available_governors,
82        }
83    }
84}