summaryrefslogtreecommitdiff
path: root/src/energy
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/energy
parent8626d53bf481c23025d388583c3904fa33741541 (diff)
Use sysfs for reading system power
Diffstat (limited to 'src/energy')
-rw-r--r--src/energy/rapl.rs155
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)
}