trueno_gpu/monitor/device/
cpu.rs1use super::{ComputeDevice, DeviceId, DeviceType};
6use crate::GpuError;
7
8#[derive(Debug)]
14pub struct CpuDevice {
15 name: String,
16 core_count: u32,
17 total_memory: u64,
18 cpu_usage: f64,
20 memory_used: u64,
21 temperature: Option<f64>,
22}
23
24impl CpuDevice {
25 #[must_use]
27 pub fn new() -> Self {
28 let name = Self::read_cpu_name().unwrap_or_else(|| "Unknown CPU".to_string());
30 let core_count = Self::read_core_count();
31 let total_memory = Self::read_total_memory();
32
33 Self {
34 name,
35 core_count,
36 total_memory,
37 cpu_usage: 0.0,
38 memory_used: 0,
39 temperature: None,
40 }
41 }
42
43 fn read_cpu_name() -> Option<String> {
44 #[cfg(target_os = "linux")]
45 {
46 let content = std::fs::read_to_string("/proc/cpuinfo").ok()?;
47 for line in content.lines() {
48 if line.starts_with("model name") {
49 return line.split(':').nth(1).map(|s| s.trim().to_string());
50 }
51 }
52 }
53 None
54 }
55
56 fn read_core_count() -> u32 {
57 #[cfg(target_os = "linux")]
58 {
59 if let Ok(content) = std::fs::read_to_string("/proc/cpuinfo") {
60 return content
61 .lines()
62 .filter(|line| line.starts_with("processor"))
63 .count() as u32;
64 }
65 }
66 std::thread::available_parallelism()
68 .map(|n| n.get() as u32)
69 .unwrap_or(1)
70 }
71
72 fn read_total_memory() -> u64 {
73 #[cfg(target_os = "linux")]
74 {
75 if let Ok(content) = std::fs::read_to_string("/proc/meminfo") {
76 for line in content.lines() {
77 if line.starts_with("MemTotal:") {
78 let parts: Vec<&str> = line.split_whitespace().collect();
80 if parts.len() >= 2 {
81 if let Ok(kb) = parts[1].parse::<u64>() {
82 return kb * 1024; }
84 }
85 }
86 }
87 }
88 }
89 0
90 }
91
92 fn read_memory_used() -> u64 {
93 #[cfg(target_os = "linux")]
94 {
95 if let Ok(content) = std::fs::read_to_string("/proc/meminfo") {
96 let mut total = 0u64;
97 let mut available = 0u64;
98
99 for line in content.lines() {
100 let parts: Vec<&str> = line.split_whitespace().collect();
101 if parts.len() >= 2 {
102 if line.starts_with("MemTotal:") {
103 total = parts[1].parse().unwrap_or(0) * 1024;
104 } else if line.starts_with("MemAvailable:") {
105 available = parts[1].parse().unwrap_or(0) * 1024;
106 }
107 }
108 }
109 return total.saturating_sub(available);
110 }
111 }
112 0
113 }
114
115 fn read_cpu_usage() -> f64 {
116 #[cfg(target_os = "linux")]
117 {
118 if let Ok(content) = std::fs::read_to_string("/proc/stat") {
121 for line in content.lines() {
122 if line.starts_with("cpu ") {
123 let parts: Vec<&str> = line.split_whitespace().collect();
124 if parts.len() >= 5 {
125 let user: u64 = parts[1].parse().unwrap_or(0);
126 let nice: u64 = parts[2].parse().unwrap_or(0);
127 let system: u64 = parts[3].parse().unwrap_or(0);
128 let idle: u64 = parts[4].parse().unwrap_or(0);
129
130 let total = user + nice + system + idle;
131 let busy = user + nice + system;
132 if total > 0 {
133 return (busy as f64 / total as f64) * 100.0;
134 }
135 }
136 }
137 }
138 }
139 }
140 0.0
141 }
142
143 fn read_temperature() -> Option<f64> {
144 #[cfg(target_os = "linux")]
145 {
146 if let Ok(entries) = std::fs::read_dir("/sys/class/hwmon") {
148 for entry in entries.flatten() {
149 let temp_path = entry.path().join("temp1_input");
150 if let Ok(content) = std::fs::read_to_string(&temp_path) {
151 if let Ok(millidegrees) = content.trim().parse::<i64>() {
152 return Some(millidegrees as f64 / 1000.0);
153 }
154 }
155 }
156 }
157 if let Ok(content) = std::fs::read_to_string("/sys/class/thermal/thermal_zone0/temp") {
159 if let Ok(millidegrees) = content.trim().parse::<i64>() {
160 return Some(millidegrees as f64 / 1000.0);
161 }
162 }
163 }
164 None
165 }
166}
167
168impl Default for CpuDevice {
169 fn default() -> Self {
170 Self::new()
171 }
172}
173
174impl ComputeDevice for CpuDevice {
175 fn device_id(&self) -> DeviceId {
176 DeviceId::cpu()
177 }
178
179 fn device_name(&self) -> &str {
180 &self.name
181 }
182
183 fn device_type(&self) -> DeviceType {
184 DeviceType::Cpu
185 }
186
187 fn compute_utilization(&self) -> Result<f64, GpuError> {
188 Ok(self.cpu_usage)
189 }
190
191 fn compute_clock_mhz(&self) -> Result<u32, GpuError> {
192 #[cfg(target_os = "linux")]
194 {
195 if let Ok(content) =
196 std::fs::read_to_string("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq")
197 {
198 if let Ok(khz) = content.trim().parse::<u64>() {
199 return Ok((khz / 1000) as u32);
200 }
201 }
202 }
203 Err(GpuError::NotSupported(
204 "CPU frequency not available".to_string(),
205 ))
206 }
207
208 fn compute_temperature_c(&self) -> Result<f64, GpuError> {
209 self.temperature
210 .ok_or_else(|| GpuError::NotSupported("CPU temperature not available".to_string()))
211 }
212
213 fn compute_power_watts(&self) -> Result<f64, GpuError> {
214 Err(GpuError::NotSupported(
217 "CPU power not available".to_string(),
218 ))
219 }
220
221 fn compute_power_limit_watts(&self) -> Result<f64, GpuError> {
222 Err(GpuError::NotSupported(
223 "CPU power limit not available".to_string(),
224 ))
225 }
226
227 fn memory_used_bytes(&self) -> Result<u64, GpuError> {
228 Ok(self.memory_used)
229 }
230
231 fn memory_total_bytes(&self) -> Result<u64, GpuError> {
232 Ok(self.total_memory)
233 }
234
235 fn memory_bandwidth_gbps(&self) -> Result<f64, GpuError> {
236 Err(GpuError::NotSupported(
238 "Memory bandwidth not available".to_string(),
239 ))
240 }
241
242 fn compute_unit_count(&self) -> u32 {
243 self.core_count
244 }
245
246 fn active_compute_units(&self) -> Result<u32, GpuError> {
247 Ok(self.core_count)
249 }
250
251 fn pcie_tx_bytes_per_sec(&self) -> Result<u64, GpuError> {
252 Err(GpuError::NotSupported(
253 "CPU has no PCIe metrics".to_string(),
254 ))
255 }
256
257 fn pcie_rx_bytes_per_sec(&self) -> Result<u64, GpuError> {
258 Err(GpuError::NotSupported(
259 "CPU has no PCIe metrics".to_string(),
260 ))
261 }
262
263 fn pcie_generation(&self) -> u8 {
264 0 }
266
267 fn pcie_width(&self) -> u8 {
268 0 }
270
271 fn refresh(&mut self) -> Result<(), GpuError> {
272 self.cpu_usage = Self::read_cpu_usage();
273 self.memory_used = Self::read_memory_used();
274 self.temperature = Self::read_temperature();
275 Ok(())
276 }
277}