use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use argh::FromArgs;
use libtracecmd::Event;
use libtracecmd::Handler;
use libtracecmd::Input;
use libtracecmd::Record;
use once_cell::sync::OnceCell;
static CONFIG: OnceCell<Config> = OnceCell::new();
#[derive(FromArgs, Debug)]
struct Config {
#[argh(option)]
input: String,
#[argh(option)]
n: usize,
#[argh(option)]
prefix: Option<String>,
}
#[derive(Default, Debug)]
struct StatsData {
cnt: u32,
stats: BTreeMap<String, u32>,
}
impl StatsData {
fn print_top_n_events(&self) {
let cfg = CONFIG.get().unwrap();
let n = cfg.n;
let mut vec: Vec<(u32, String)> = vec![];
for (name, count) in &self.stats {
vec.push((*count, name.clone()));
}
vec.sort();
vec.reverse();
let n = std::cmp::min(vec.len(), n);
println!("Top {n} events:");
for i in 0..n {
println!("#{}: {}: {} times", i + 1, vec[i].1, vec[i].0);
}
}
}
struct TopNStats;
impl Handler for TopNStats {
type AccumulatedData = StatsData;
fn callback(
input: &mut Input,
rec: &mut Record,
_cpu: i32,
data: &mut Self::AccumulatedData,
) -> i32 {
let event: Event = input.find_event(rec).unwrap();
let name = event.name;
let cfg = CONFIG.get().unwrap();
let name = if let Some(pre) = &cfg.prefix {
if !name.starts_with(pre) {
return 0;
}
name.trim_start_matches(pre).to_string()
} else {
name
};
match data.stats.entry(name) {
Entry::Vacant(o) => {
o.insert(1);
}
Entry::Occupied(mut o) => {
*o.get_mut() += 1;
}
}
data.cnt += 1;
0
}
}
fn main() {
let cfg: Config = argh::from_env();
let input = cfg.input.clone();
CONFIG.set(cfg).unwrap();
let mut input = Input::new(&input).unwrap();
let stats = TopNStats::process(&mut input).unwrap();
stats.print_top_n_events();
}