use std::error::Error;
use std::io;
use crate::bench_id::BenchId;
use crate::plugins::{EventListener, PerBenchData, PluginEvents};
use perf_event::Counter;
use perf_event::events::{Cache, CacheOp, CacheResult, Hardware, Software, WhichCache};
use perf_event::{Builder, Group};
use std::any::Any;
use yansi::Paint;
use super::{PERF_CNT_EVENT_LISTENER_NAME, PerfCounter, PerfCounterValues, default_perf_counters};
impl PerfCounter {
fn build(self, group: &mut Group) -> io::Result<Counter> {
let builder = Builder::new().group(group);
match self {
PerfCounter::CpuCycles => builder.kind(Hardware::CPU_CYCLES).build(),
PerfCounter::PageFaultsMinor => builder.kind(Software::PAGE_FAULTS_MIN).build(),
PerfCounter::PageFaultsMajor => builder.kind(Software::PAGE_FAULTS_MAJ).build(),
PerfCounter::PageFaults => builder.kind(Software::PAGE_FAULTS).build(),
PerfCounter::Branches => builder.kind(Hardware::BRANCH_INSTRUCTIONS).build(),
PerfCounter::MissedBranches => builder.kind(Hardware::BRANCH_MISSES).build(),
PerfCounter::L1DCacheAccess => builder
.kind(Cache {
which: WhichCache::L1D,
operation: CacheOp::READ,
result: CacheResult::ACCESS,
})
.build(),
PerfCounter::L1DCacheMiss => builder
.kind(Cache {
which: WhichCache::L1D,
operation: CacheOp::READ,
result: CacheResult::MISS,
})
.build(),
PerfCounter::TLBDataAccess => builder
.kind(Cache {
which: WhichCache::DTLB,
operation: CacheOp::READ,
result: CacheResult::ACCESS,
})
.build(),
PerfCounter::TLBDataMiss => builder
.kind(Cache {
which: WhichCache::DTLB,
operation: CacheOp::READ,
result: CacheResult::MISS,
})
.build(),
PerfCounter::InstructionsRetired => builder.kind(Hardware::INSTRUCTIONS).build(),
}
}
}
pub(crate) struct PerfCounterGroup {
group: Group,
counters: Vec<(PerfCounter, Counter)>, }
impl PerfCounterGroup {
pub fn new(counters_enum: &[PerfCounter]) -> Result<Self, Box<dyn Error>> {
let mut group = Group::new()?;
let mut counters: Vec<_> = Vec::new();
for counter in counters_enum {
match counter.build(&mut group) {
Ok(built_counter) => counters.push((*counter, built_counter)),
Err(e) => {
let warn = "Some counter combinations are incompatible".bold().red();
println!(
"{}. Disabling PerfCounter: {} \nError: {:?}",
warn, counter, e
);
}
}
}
group.disable()?;
Ok(PerfCounterGroup { group, counters })
}
}
impl PerfCounterGroup {
pub fn enable(&mut self) {
self.group.enable().unwrap();
}
pub fn disable(&mut self) {
self.group.disable().unwrap();
}
pub fn finish(&mut self, num_iter: u64) -> io::Result<PerfCounterValues> {
let num_iter = num_iter as f64;
let mut values = Vec::new();
for (counter_enum, counter) in &mut self.counters {
let count = counter.read().unwrap() as f64 / num_iter;
values.push((*counter_enum, count)); }
Ok(PerfCounterValues { values })
}
}
pub struct PerfCounterPlugin {
perf_per_bench: PerBenchData<Option<PerfCounterGroup>>,
enabled_perf_counters: Vec<PerfCounter>,
}
impl Default for PerfCounterPlugin {
fn default() -> Self {
PerfCounterPlugin {
perf_per_bench: PerBenchData::default(),
enabled_perf_counters: default_perf_counters().to_vec(),
}
}
}
impl PerfCounterPlugin {
pub fn new(perf_counters: Vec<PerfCounter>) -> Self {
PerfCounterPlugin {
perf_per_bench: PerBenchData::default(),
enabled_perf_counters: perf_counters,
}
}
pub(crate) fn get_by_bench_id_mut(
&mut self,
bench_id: &BenchId,
) -> Option<&mut PerfCounterGroup> {
self.perf_per_bench
.get_mut(bench_id)
.and_then(Option::as_mut)
}
}
impl EventListener for PerfCounterPlugin {
fn prio(&self) -> u32 {
u32::MAX
}
fn as_any(&mut self) -> &mut dyn Any {
self
}
fn name(&self) -> &'static str {
PERF_CNT_EVENT_LISTENER_NAME
}
fn on_event(&mut self, event: PluginEvents) {
match event {
PluginEvents::BenchStart { bench_id } => {
self.perf_per_bench.insert_if_absent(bench_id, || {
PerfCounterGroup::new(&self.enabled_perf_counters).ok()
});
let perf = self.perf_per_bench.get_mut(bench_id).unwrap();
if let Some(perf) = perf {
perf.enable();
}
}
PluginEvents::BenchStop { bench_id, .. } => {
let perf = self.perf_per_bench.get_mut(bench_id).unwrap();
if let Some(perf) = perf {
perf.disable();
}
}
_ => {}
}
}
}