From fda1763d8b3240d97925ca1eeba316008e1ca7c5 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sun, 9 Mar 2025 19:41:46 +0100 Subject: Use sysfs for reading system power --- src/e_core_selector.rs | 2 +- src/energy.rs | 26 +-------- src/energy/rapl.rs | 155 ++++++++++++++++++++++--------------------------- 3 files changed, 71 insertions(+), 112 deletions(-) (limited to 'src') diff --git a/src/e_core_selector.rs b/src/e_core_selector.rs index 251cbe8..48bfa8c 100644 --- a/src/e_core_selector.rs +++ b/src/e_core_selector.rs @@ -26,6 +26,6 @@ impl ECoreSelector for RoundRobinSelector { return previous_cpu; } self.last_used += 1; - (self.offset + (self.last_used % self.num_cores)) as i32 + (self.offset + (self.last_used % self.num_cores.max(1))) as i32 } } diff --git a/src/energy.rs b/src/energy.rs index 8400dd7..9fe37b5 100644 --- a/src/energy.rs +++ b/src/energy.rs @@ -10,10 +10,7 @@ use std::time::Duration; use crate::Pid; use dashmap::DashMap; -use crate::energy::rapl::PackageEnergy; pub use budget::BudgetPolicy; -use perf_event::events::Event; -use perf_event::{Builder, Counter}; pub use trackers::{Estimator, KernelDriver, PerfEstimator}; pub enum Request { @@ -35,8 +32,6 @@ pub struct EnergyService { shared_budgets: Arc>, request_receiver: mpsc::Receiver, update_interval: Duration, - package_energy: PackageEnergy, - package_energy_counter: Counter, } impl EnergyService { @@ -46,8 +41,6 @@ impl EnergyService { shared_budgets: Arc>, request_receiver: mpsc::Receiver, update_interval: Duration, - package_energy: PackageEnergy, - package_energy_counter: Counter, ) -> Self { Self { estimator, @@ -57,8 +50,6 @@ impl EnergyService { shared_budgets, request_receiver, update_interval, - package_energy, - package_energy_counter, } } @@ -142,8 +133,7 @@ impl EnergyService { } fn package_energy(&mut self) -> f64 { - self.package_energy - .to_joules(self.package_energy_counter.read().unwrap_or(0)) + rapl::read_package_energy().unwrap() } pub fn process_energy(&self, pid: Pid) -> Option { @@ -179,18 +169,6 @@ pub fn start_energy_service( // shouldn't be a problem because we are privileged // if PackageEnergy::check_paranoid().unwrap_or(3) > 0 {} - let package_energy = PackageEnergy::new().unwrap(); - - // Any cpu should be fine because the package power should be the same for every cpu - let mut package_energy_counter = Builder::new() - .kind(Event::Raw( - package_energy.get_type(), - package_energy.get_event_config(), - )) - .any_cpu() - .build()?; - package_energy_counter.enable()?; - // Create and start the energy service let service = EnergyService::new( estimator, @@ -198,8 +176,6 @@ pub fn start_energy_service( shared_budgets, request_receiver, Duration::from_millis(50), // 50ms update interval - package_energy, - package_energy_counter, ); service.run(); 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 { - // 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::() - .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 { + 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::() + .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::() - .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 { + 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::() + .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 { + 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 { - 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::() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; +/// List all available RAPL domains with their indices +pub fn list_domains() -> io::Result> { + 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) } -- cgit v1.3.1