summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDennis Kobert <dennis@kobert.dev>2025-03-09 19:41:46 +0100
committerDennis Kobert <dennis@kobert.dev>2025-03-09 19:41:46 +0100
commitfda1763d8b3240d97925ca1eeba316008e1ca7c5 (patch)
tree4002d993f4bcf31b644593e47f034a0854cd20b6 /src
parent8626d53bf481c23025d388583c3904fa33741541 (diff)
Use sysfs for reading system power
Diffstat (limited to 'src')
-rw-r--r--src/e_core_selector.rs2
-rw-r--r--src/energy.rs26
-rw-r--r--src/energy/rapl.rs155
3 files changed, 71 insertions, 112 deletions
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<DashMap<Pid, u64>>,
request_receiver: mpsc::Receiver<Request>,
update_interval: Duration,
- package_energy: PackageEnergy,
- package_energy_counter: Counter,
}
impl EnergyService {
@@ -46,8 +41,6 @@ impl EnergyService {
shared_budgets: Arc<DashMap<Pid, u64>>,
request_receiver: mpsc::Receiver<Request>,
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<u64> {
@@ -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<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)
}