use super::{ComputeDevice, DeviceId, DeviceType};
use crate::GpuError;
#[derive(Debug)]
pub struct CpuDevice {
name: String,
core_count: u32,
total_memory: u64,
cpu_usage: f64,
memory_used: u64,
temperature: Option<f64>,
}
impl CpuDevice {
#[must_use]
pub fn new() -> Self {
let name = Self::read_cpu_name().unwrap_or_else(|| "Unknown CPU".to_string());
let core_count = Self::read_core_count();
let total_memory = Self::read_total_memory();
Self { name, core_count, total_memory, cpu_usage: 0.0, memory_used: 0, temperature: None }
}
fn read_cpu_name() -> Option<String> {
#[cfg(target_os = "linux")]
{
let content = std::fs::read_to_string("/proc/cpuinfo").ok()?;
for line in content.lines() {
if line.starts_with("model name") {
return line.split(':').nth(1).map(|s| s.trim().to_string());
}
}
}
None
}
fn read_core_count() -> u32 {
#[cfg(target_os = "linux")]
{
if let Ok(content) = std::fs::read_to_string("/proc/cpuinfo") {
return content.lines().filter(|line| line.starts_with("processor")).count() as u32;
}
}
std::thread::available_parallelism().map(|n| n.get() as u32).unwrap_or(1)
}
fn read_total_memory() -> u64 {
#[cfg(target_os = "linux")]
{
if let Ok(content) = std::fs::read_to_string("/proc/meminfo") {
for line in content.lines() {
if line.starts_with("MemTotal:") {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 {
if let Ok(kb) = parts[1].parse::<u64>() {
return kb * 1024; }
}
}
}
}
}
0
}
fn read_memory_used() -> u64 {
#[cfg(target_os = "linux")]
{
if let Ok(content) = std::fs::read_to_string("/proc/meminfo") {
let mut total = 0u64;
let mut available = 0u64;
for line in content.lines() {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 {
if line.starts_with("MemTotal:") {
total = parts[1].parse().unwrap_or(0) * 1024;
} else if line.starts_with("MemAvailable:") {
available = parts[1].parse().unwrap_or(0) * 1024;
}
}
}
return total.saturating_sub(available);
}
}
0
}
fn read_cpu_usage() -> f64 {
#[cfg(target_os = "linux")]
{
if let Ok(content) = std::fs::read_to_string("/proc/stat") {
for line in content.lines() {
if line.starts_with("cpu ") {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 5 {
let user: u64 = parts[1].parse().unwrap_or(0);
let nice: u64 = parts[2].parse().unwrap_or(0);
let system: u64 = parts[3].parse().unwrap_or(0);
let idle: u64 = parts[4].parse().unwrap_or(0);
let total = user + nice + system + idle;
let busy = user + nice + system;
if total > 0 {
return (busy as f64 / total as f64) * 100.0;
}
}
}
}
}
}
0.0
}
fn read_temperature() -> Option<f64> {
#[cfg(target_os = "linux")]
{
if let Ok(entries) = std::fs::read_dir("/sys/class/hwmon") {
for entry in entries.flatten() {
let temp_path = entry.path().join("temp1_input");
if let Ok(content) = std::fs::read_to_string(&temp_path) {
if let Ok(millidegrees) = content.trim().parse::<i64>() {
return Some(millidegrees as f64 / 1000.0);
}
}
}
}
if let Ok(content) = std::fs::read_to_string("/sys/class/thermal/thermal_zone0/temp") {
if let Ok(millidegrees) = content.trim().parse::<i64>() {
return Some(millidegrees as f64 / 1000.0);
}
}
}
None
}
}
impl Default for CpuDevice {
fn default() -> Self {
Self::new()
}
}
impl ComputeDevice for CpuDevice {
fn device_id(&self) -> DeviceId {
DeviceId::cpu()
}
fn device_name(&self) -> &str {
&self.name
}
fn device_type(&self) -> DeviceType {
DeviceType::Cpu
}
fn compute_utilization(&self) -> Result<f64, GpuError> {
Ok(self.cpu_usage)
}
fn compute_clock_mhz(&self) -> Result<u32, GpuError> {
#[cfg(target_os = "linux")]
{
if let Ok(content) =
std::fs::read_to_string("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq")
{
if let Ok(khz) = content.trim().parse::<u64>() {
return Ok((khz / 1000) as u32);
}
}
}
Err(GpuError::NotSupported("CPU frequency not available".to_string()))
}
fn compute_temperature_c(&self) -> Result<f64, GpuError> {
self.temperature
.ok_or_else(|| GpuError::NotSupported("CPU temperature not available".to_string()))
}
fn compute_power_watts(&self) -> Result<f64, GpuError> {
Err(GpuError::NotSupported("CPU power not available".to_string()))
}
fn compute_power_limit_watts(&self) -> Result<f64, GpuError> {
Err(GpuError::NotSupported("CPU power limit not available".to_string()))
}
fn memory_used_bytes(&self) -> Result<u64, GpuError> {
Ok(self.memory_used)
}
fn memory_total_bytes(&self) -> Result<u64, GpuError> {
Ok(self.total_memory)
}
fn memory_bandwidth_gbps(&self) -> Result<f64, GpuError> {
Err(GpuError::NotSupported("Memory bandwidth not available".to_string()))
}
fn compute_unit_count(&self) -> u32 {
self.core_count
}
fn active_compute_units(&self) -> Result<u32, GpuError> {
Ok(self.core_count)
}
fn pcie_tx_bytes_per_sec(&self) -> Result<u64, GpuError> {
Err(GpuError::NotSupported("CPU has no PCIe metrics".to_string()))
}
fn pcie_rx_bytes_per_sec(&self) -> Result<u64, GpuError> {
Err(GpuError::NotSupported("CPU has no PCIe metrics".to_string()))
}
fn pcie_generation(&self) -> u8 {
0 }
fn pcie_width(&self) -> u8 {
0 }
fn refresh(&mut self) -> Result<(), GpuError> {
self.cpu_usage = Self::read_cpu_usage();
self.memory_used = Self::read_memory_used();
self.temperature = Self::read_temperature();
Ok(())
}
}