diff options
| author | Dennis Kobert <dennis@kobert.dev> | 2025-03-09 19:41:46 +0100 |
|---|---|---|
| committer | Dennis Kobert <dennis@kobert.dev> | 2025-03-09 19:41:46 +0100 |
| commit | fda1763d8b3240d97925ca1eeba316008e1ca7c5 (patch) | |
| tree | 4002d993f4bcf31b644593e47f034a0854cd20b6 /src/energy | |
| parent | 8626d53bf481c23025d388583c3904fa33741541 (diff) | |
Use sysfs for reading system power
Diffstat (limited to 'src/energy')
| -rw-r--r-- | src/energy/rapl.rs | 155 |
1 files changed, 69 insertions, 86 deletions
diff --git a/src/energy/rapl.rs b/src/energy/rapl.rs index 3e6af00..b88a959 100644 --- a/src/energy/rapl.rs +++ b/src/energy/rapl.rs @@ -2,105 +2,88 @@ use std::fs; use std::io; use std::path::Path; -/// Main structure for CPU package energy monitoring -pub struct PackageEnergy { - /// The perf event type - event_type: u32, - /// The event config - event_config: u64, - /// The scaling factor for joules - scale: f64, +/// Check if RAPL sysfs interface is available +pub fn is_rapl_available() -> bool { + Path::new("/sys/class/powercap/intel-rapl/intel-rapl:0").exists() } -impl PackageEnergy { - /// Initialize a new PackageEnergy monitor - pub fn new() -> io::Result<Self> { - // 1. Check if RAPL is supported via perf events - let type_path = Path::new("/sys/bus/event_source/devices/power/type"); - if !type_path.exists() { - return Err(io::Error::new( - io::ErrorKind::NotFound, - "RAPL perf event support not found (requires Linux 3.14 or newer)", - )); - } - - // 2. Read the perf event type for power - let event_type = fs::read_to_string(type_path)? - .trim() - .parse::<u32>() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - - // 3. Check if package energy domain is available - let event_path = "/sys/bus/event_source/devices/power/events/energy-pkg"; - if !Path::new(event_path).exists() { - return Err(io::Error::new( - io::ErrorKind::NotFound, - "Package energy monitoring not available", - )); - } +/// Read current package energy counter value in joules +pub fn read_package_energy() -> io::Result<f64> { + let energy_path = "/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj"; + let energy_str = fs::read_to_string(energy_path)?; + let energy_uj = energy_str + .trim() + .parse::<u64>() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - // 4. Read event config - let event_content = fs::read_to_string(event_path)?; - let event_config_str = event_content - .split("=0x") - .nth(1) - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid event format"))?; - let event_config = u64::from_str_radix(event_config_str.trim(), 16) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + // Convert from microjoules to joules + Ok(energy_uj as f64 / 1_000_000.0) +} - // 5. Read scale - let scale_path = format!("{}.scale", event_path); - let scale = if Path::new(&scale_path).exists() { - fs::read_to_string(&scale_path)? - .trim() - .parse::<f64>() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))? - } else { - 1.0 // Default scale if not available - }; +/// Read energy counter for a specific domain (core, uncore, dram) +pub fn read_domain_energy(domain_idx: usize) -> io::Result<f64> { + let domain_path = format!( + "/sys/class/powercap/intel-rapl/intel-rapl:0:{}/energy_uj", + domain_idx + ); - Ok(PackageEnergy { - event_type, - event_config, - scale, - }) + if !Path::new(&domain_path).exists() { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("Domain {} not available", domain_idx), + )); } - /// Get the scaling factor for joules - pub fn get_scale(&self) -> f64 { - self.scale - } + let energy_str = fs::read_to_string(domain_path)?; + let energy_uj = energy_str + .trim() + .parse::<u64>() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - /// Convert raw counter value to joules - pub fn to_joules(&self, raw_value: u64) -> f64 { - raw_value as f64 * self.scale - } + // Convert from microjoules to joules + Ok(energy_uj as f64 / 1_000_000.0) +} - /// Get the perf event type - pub fn get_type(&self) -> u32 { - self.event_type - } +/// Get domain name +pub fn get_domain_name(domain_idx: usize) -> io::Result<String> { + let name_path = format!( + "/sys/class/powercap/intel-rapl/intel-rapl:0:{}/name", + domain_idx + ); - /// Get the event config - pub fn get_event_config(&self) -> u64 { - self.event_config + if !Path::new(&name_path).exists() { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("Domain {} not available", domain_idx), + )); } - /// Check if perf_event_paranoid setting might cause permission issues - pub fn check_paranoid() -> io::Result<i32> { - let path = Path::new("/proc/sys/kernel/perf_event_paranoid"); - if !path.exists() { - return Err(io::Error::new( - io::ErrorKind::NotFound, - "perf_event_paranoid file not found", - )); - } + let name = fs::read_to_string(name_path)?; + Ok(name.trim().to_string()) +} - let value = fs::read_to_string(path)? - .trim() - .parse::<i32>() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; +/// List all available RAPL domains with their indices +pub fn list_domains() -> io::Result<Vec<(usize, String)>> { + let base_path = "/sys/class/powercap/intel-rapl/intel-rapl:0"; + let mut domains = Vec::new(); - Ok(value) + // Add package domain (always index 0) + let pkg_name_path = format!("{}/name", base_path); + let pkg_name = fs::read_to_string(pkg_name_path)?.trim().to_string(); + domains.push((0, pkg_name)); + + // Check for subdomains + for i in 0..10 { + let domain_path = format!("{}/intel-rapl:0:{}", base_path, i); + if Path::new(&domain_path).exists() { + let name_path = format!("{}/name", domain_path); + if let Ok(name) = fs::read_to_string(name_path) { + domains.push((i, name.trim().to_string())); + } + } else { + break; + } } + + Ok(domains) } |
