use denet::ebpf::{OffCpuProfiler, OffCpuStats};
use std::collections::HashMap;
use std::process::{Command, Stdio};
use std::thread;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
if unsafe { libc::geteuid() } != 0 {
eprintln!("error: requires root or CAP_BPF/CAP_PERFMON");
eprintln!(" sudo cargo run --example offcpu_basic --features ebpf");
std::process::exit(1);
}
let mut child = Command::new("dd")
.args(["if=/dev/zero", "of=/dev/null", "bs=4k", "count=100000"])
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()?;
let pid = child.id();
println!("Monitoring PID {pid} for 2 seconds...");
let profiler = OffCpuProfiler::new(vec![pid])?;
thread::sleep(Duration::from_secs(2));
let stats: HashMap<(u32, u32), OffCpuStats> = profiler.get_stats();
if stats.is_empty() {
println!("No off-CPU events recorded (process may have exited early).");
} else {
println!("Threads with off-CPU events: {}", stats.len());
let mut entries: Vec<_> = stats.iter().collect();
entries.sort_by(|a, b| b.1.total_time_ns.cmp(&a.1.total_time_ns));
for ((pid, tid), s) in entries {
println!(
" pid={pid} tid={tid}: events={} total={:.1}ms avg={:.1}ms max={:.1}ms",
s.count,
s.total_time_ns as f64 / 1_000_000.0,
s.avg_time_ns as f64 / 1_000_000.0,
s.max_time_ns as f64 / 1_000_000.0,
);
}
}
child.kill().ok();
Ok(())
}