Skip to main content

cbtop/frequency_control/
controller.rs

1use std::path::PathBuf;
2
3use super::governor::CpuGovernor;
4use super::info::CpuFrequencyInfo;
5use super::reading::FrequencyReading;
6use super::variance::FrequencyVariance;
7
8/// Frequency controller for reading and managing CPU frequencies
9#[derive(Debug)]
10pub struct FrequencyController {
11    /// Sysfs base path
12    pub(crate) sysfs_path: PathBuf,
13    /// Number of CPUs
14    cpu_count: usize,
15    /// Mock mode for testing
16    pub(crate) mock_mode: bool,
17    /// Mock frequency value
18    mock_frequency: u64,
19    /// Mock governor
20    mock_governor: CpuGovernor,
21}
22
23impl Default for FrequencyController {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29impl FrequencyController {
30    /// Create new controller
31    pub fn new() -> Self {
32        let cpu_count = super::num_cpus();
33        Self {
34            sysfs_path: PathBuf::from("/sys/devices/system/cpu"),
35            cpu_count,
36            mock_mode: false,
37            mock_frequency: 3_000_000, // 3 GHz default
38            mock_governor: CpuGovernor::Performance,
39        }
40    }
41
42    /// Enable mock mode for testing
43    pub fn with_mock(mut self, frequency_khz: u64, governor: CpuGovernor) -> Self {
44        self.mock_mode = true;
45        self.mock_frequency = frequency_khz;
46        self.mock_governor = governor;
47        self
48    }
49
50    /// Get CPU count
51    pub fn cpu_count(&self) -> usize {
52        self.cpu_count
53    }
54
55    /// Read frequency for single CPU
56    pub fn read_cpu_frequency(&self, cpu_id: usize) -> Option<CpuFrequencyInfo> {
57        Some(if self.mock_mode {
58            CpuFrequencyInfo::mock(cpu_id, self.mock_frequency, self.mock_governor)
59        } else {
60            CpuFrequencyInfo::from_sysfs(cpu_id, &self.sysfs_path)
61        })
62    }
63
64    /// Read all CPU frequencies
65    pub fn read_all_frequencies(&self) -> FrequencyReading {
66        let timestamp_ns = std::time::SystemTime::now()
67            .duration_since(std::time::UNIX_EPOCH)
68            .map(|d| d.as_nanos() as u64)
69            .unwrap_or(0);
70
71        let cpus: Vec<CpuFrequencyInfo> = (0..self.cpu_count)
72            .filter_map(|id| self.read_cpu_frequency(id))
73            .collect();
74
75        FrequencyReading { cpus, timestamp_ns }
76    }
77
78    /// Detect current governor
79    pub fn detect_governor(&self) -> CpuGovernor {
80        self.read_all_frequencies().common_governor()
81    }
82
83    /// Check if frequency can be controlled
84    pub fn can_control(&self) -> bool {
85        if self.mock_mode {
86            return true;
87        }
88
89        let reading = self.read_all_frequencies();
90        if reading.cpus.is_empty() {
91            return false;
92        }
93
94        let govs = &reading.cpus[0].available_governors;
95        govs.contains(&CpuGovernor::Userspace) || govs.contains(&CpuGovernor::Performance)
96    }
97
98    /// Measure frequency variance over time
99    pub fn measure_variance(&self, samples: usize, interval_ms: u64) -> FrequencyVariance {
100        let mut readings = Vec::with_capacity(samples);
101        for _ in 0..samples {
102            readings.push(self.read_all_frequencies().average_mhz());
103            std::thread::sleep(std::time::Duration::from_millis(interval_ms));
104        }
105        FrequencyVariance::from_samples(&readings)
106    }
107}