use bcc::perf_event::{Event, HardwareEvent};
use bcc::BccError;
use bcc::PerfEvent;
use bcc::BPF;
use clap::{App, Arg};
use core::sync::atomic::{AtomicBool, Ordering};
use std::collections::HashMap;
use std::sync::Arc;
use std::{mem, ptr, str, thread, time};
const DEFAULT_SAMPLE_PERIOD: u64 = 100; const DEFAULT_DURATION: u64 = 10;
#[repr(C)]
struct key_t {
cpu: i32,
pid: i32,
name: [u8; 16],
}
fn do_main(runnable: Arc<AtomicBool>) -> Result<(), BccError> {
let matches = App::new("cpudist")
.arg(
Arg::with_name("sample_period")
.long("sample_period")
.short("c")
.help("Sample one in this many number of cache reference / miss events")
.takes_value(true),
)
.arg(
Arg::with_name("duration")
.long("duration")
.short("d")
.help("Duration in seconds to run")
.takes_value(true),
)
.get_matches();
let sample_period: u64 = matches
.value_of("sample_period")
.map(|v| v.parse().expect("Invalid sample period"))
.unwrap_or(DEFAULT_SAMPLE_PERIOD);
let duration: u64 = matches
.value_of("duration")
.map(|v| v.parse().expect("Invalid duration"))
.unwrap_or(DEFAULT_DURATION);
let code = include_str!("llcstat.c").to_string();
let mut bpf = BPF::new(&code)?;
PerfEvent::new()
.handler("on_cache_miss")
.event(Event::Hardware(HardwareEvent::CacheMisses))
.sample_period(Some(sample_period))
.attach(&mut bpf)?;
PerfEvent::new()
.handler("on_cache_ref")
.event(Event::Hardware(HardwareEvent::CacheReferences))
.sample_period(Some(sample_period))
.attach(&mut bpf)?;
println!("Running for {} seconds", duration);
let mut elapsed = 0;
while runnable.load(Ordering::SeqCst) {
if elapsed == duration {
break;
}
thread::sleep(time::Duration::new(1, 0));
elapsed += 1;
}
let mut miss_table = bpf.table("miss_count")?;
let miss_map = to_map(&mut miss_table);
let mut ref_table = bpf.table("ref_count")?;
let ref_map = to_map(&mut ref_table);
let mut total_hit = 0;
let mut total_miss = 0;
println!(
"{:<-8} {:<-16} {:<-4} {:>-12} {:>-12} {:>6}",
"PID", "NAME", "CPU", "REFERENCE", "MISS", "HIT%"
);
for (key, value) in ref_map.iter() {
let miss = miss_map.get(key).unwrap_or(&0);
let hit = if value > miss { value - miss } else { 0 };
total_hit += hit;
total_miss += miss;
println!(
"{:<-8} {:<-16} {:<-4} {:>-12} {:>-12} {:>6}%",
key.1, key.2, key.0, value,
miss,
hit as f64 / (*value) as f64 * 100.0
);
}
println!("Total hit: {}\nTotal miss: {}", total_hit, total_miss);
Ok(())
}
fn to_map(table: &mut bcc::table::Table) -> HashMap<(i32, i32, String), u64> {
let mut map = HashMap::new();
for entry in table.iter() {
let key = parse_struct(&entry.key);
let value = parse_u64(entry.value);
let name = str::from_utf8(&key.name).unwrap_or("").to_string();
map.insert((key.cpu, key.pid, name), value);
}
map
}
fn parse_u64(x: Vec<u8>) -> u64 {
let mut v = [0_u8; 8];
for i in 0..8 {
v[i] = *x.get(i).unwrap_or(&0);
}
unsafe { mem::transmute(v) }
}
fn parse_struct(x: &[u8]) -> key_t {
unsafe { ptr::read_unaligned(x.as_ptr() as *const key_t) }
}
fn main() {
let runnable = Arc::new(AtomicBool::new(true));
let r = runnable.clone();
ctrlc::set_handler(move || {
r.store(false, Ordering::SeqCst);
})
.expect("Failed to set handler for SIGINT / SIGTERM");
if let Err(x) = do_main(runnable) {
eprintln!("Error: {}", x);
std::process::exit(1);
}
}