pub mod cpu_util;
pub mod kprobe;
pub mod perf;
use anyhow::Result;
use std::str::FromStr;
use std::sync::{Arc, RwLock};
pub use cpu_util::CpuUtilEvent;
pub use kprobe::{available_kprobe_events, KprobeEvent};
pub use perf::{available_perf_events, PerfEvent};
use crate::profiling_events::cpu_util::CpuUtilMetric;
use crate::CpuStatTracker;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum ProfilingEvent {
CpuUtil(CpuUtilEvent),
Perf(PerfEvent),
Kprobe(KprobeEvent),
}
impl ProfilingEvent {
pub fn event_name(&self) -> &str {
match self {
ProfilingEvent::CpuUtil(c) => c.event_name(),
ProfilingEvent::Perf(p) => p.event_name(),
ProfilingEvent::Kprobe(k) => &k.event_name,
}
}
pub fn initialize_for_cpu(&self, cpu: usize, process: i32) -> Result<ProfilingEvent> {
match self {
ProfilingEvent::Perf(p) => {
let mut p = p.clone();
p.cpu = cpu;
p.attach(process)?;
Ok(ProfilingEvent::Perf(p))
}
ProfilingEvent::Kprobe(k) => {
let mut k = k.clone();
k.cpu = cpu;
Ok(ProfilingEvent::Kprobe(k))
}
ProfilingEvent::CpuUtil(c) => {
let mut c = c.clone();
c.cpu = cpu;
Ok(ProfilingEvent::CpuUtil(c))
}
}
}
pub fn value(&mut self, reset: bool) -> Result<u64> {
match self {
ProfilingEvent::CpuUtil(c) => c.value(),
ProfilingEvent::Perf(p) => p.value(reset),
ProfilingEvent::Kprobe(k) => k.value(reset),
}
}
pub fn from_str_args(s: &str, tracker: Option<Arc<RwLock<CpuStatTracker>>>) -> Result<Self> {
let (source, event) = s
.split_once(':')
.ok_or(anyhow::anyhow!("Invalid profiling event: {}", s))?;
match source {
"cpu" => Ok(ProfilingEvent::CpuUtil(CpuUtilEvent::new(
0,
CpuUtilMetric::from_str(event)?,
tracker.expect("CpuStatTracker not provided"),
))),
"perf" => Ok(ProfilingEvent::Perf(PerfEvent::from_str_args(event, 0)?)),
"kprobe" => Ok(ProfilingEvent::Kprobe(KprobeEvent::new(
event.to_string(),
0,
))),
_ => Err(anyhow::anyhow!("Invalid profiling event: {}", s)),
}
}
}
pub fn get_default_events(tracker: Arc<RwLock<CpuStatTracker>>) -> Vec<ProfilingEvent> {
let default_perf_events = PerfEvent::default_events()
.into_iter()
.map(ProfilingEvent::Perf);
let default_cpu_util_events = CpuUtilEvent::default_events(tracker)
.into_iter()
.map(ProfilingEvent::CpuUtil);
default_perf_events
.chain(default_cpu_util_events)
.collect::<Vec<ProfilingEvent>>()
}
#[cfg(test)]
mod tests {
use super::*;
fn dummy_tracker() -> Arc<RwLock<CpuStatTracker>> {
Arc::new(RwLock::new(CpuStatTracker::default()))
}
#[test]
fn test_cpu_event_parsing() {
let tracker = dummy_tracker();
let result =
ProfilingEvent::from_str_args("cpu:cpu_total_util_percent", Some(tracker.clone()))
.unwrap();
let expected = ProfilingEvent::CpuUtil(CpuUtilEvent::new(0, CpuUtilMetric::Total, tracker));
assert_eq!(result, expected);
}
#[test]
fn test_perf_event_parsing() {
let result = ProfilingEvent::from_str_args("perf:hw:cycles", None).unwrap();
let expected =
ProfilingEvent::Perf(PerfEvent::new("hw".to_string(), "cycles".to_string(), 0));
assert_eq!(result, expected);
}
#[test]
fn test_invalid_format_missing_colon() {
let tracker = dummy_tracker();
let err = ProfilingEvent::from_str_args("invalid_format", None);
assert!(err.is_err());
let err = ProfilingEvent::from_str_args("invalid_format", Some(tracker));
assert!(err.is_err());
}
#[test]
fn test_invalid_source_type() {
let tracker = dummy_tracker();
let err = ProfilingEvent::from_str_args("foo:bar", None);
assert!(err.is_err());
let err = ProfilingEvent::from_str_args("foo:bar", Some(tracker));
assert!(err.is_err());
}
#[test]
fn test_invalid_cpu_metric() {
let tracker = dummy_tracker();
let result = ProfilingEvent::from_str_args("cpu:not_a_real_metric", Some(tracker));
assert!(result.is_err());
}
#[test]
fn test_invalid_perf_event() {
let result = ProfilingEvent::from_str_args("perf:", None);
assert!(result.is_err());
}
}