use crate::consts::*;
use crate::types::*;
use byteorder::{ByteOrder, ReadBytesExt};
use std::io;
use std::io::Read;
use std::num::NonZeroU64;
#[derive(Debug, Clone, Copy)]
pub struct PerfEventHeader {
pub type_: u32,
pub misc: u16,
pub size: u16,
}
impl PerfEventHeader {
pub const STRUCT_SIZE: usize = 4 + 2 + 2;
pub fn parse<R: Read, T: ByteOrder>(mut reader: R) -> Result<Self, std::io::Error> {
let type_ = reader.read_u32::<T>()?;
let misc = reader.read_u16::<T>()?;
let size = reader.read_u16::<T>()?;
Ok(Self { type_, misc, size })
}
}
#[derive(Debug, Clone, Copy)]
pub struct PerfEventAttr {
pub type_: PerfEventType,
pub sampling_policy: SamplingPolicy,
pub sample_format: SampleFormat,
pub read_format: ReadFormat,
pub flags: AttrFlags,
pub wakeup_policy: WakeupPolicy,
pub branch_sample_format: BranchSampleFormat,
pub sample_regs_user: u64,
pub sample_stack_user: u32,
pub clock: PerfClock,
pub sample_regs_intr: u64,
pub aux_watermark: u32,
pub sample_max_stack: u16,
pub aux_sample_size: u32,
pub sig_data: u64,
}
impl PerfEventAttr {
pub fn parse<R: Read, T: ByteOrder>(
mut reader: R,
size: Option<u32>,
) -> Result<Self, std::io::Error> {
let type_ = reader.read_u32::<T>()?;
let self_described_size = reader.read_u32::<T>()?;
let config = reader.read_u64::<T>()?;
let size = size.unwrap_or(self_described_size);
if size < PERF_ATTR_SIZE_VER0 {
return Err(io::ErrorKind::InvalidInput.into());
}
let sampling_period_or_frequency = reader.read_u64::<T>()?;
let sample_type = reader.read_u64::<T>()?;
let read_format = reader.read_u64::<T>()?;
let flags = reader.read_u64::<T>()?;
let wakeup_events_or_watermark = reader.read_u32::<T>()?;
let bp_type = reader.read_u32::<T>()?;
let bp_addr_or_kprobe_func_or_uprobe_func_or_config1 = reader.read_u64::<T>()?;
let bp_len_or_kprobe_addr_or_probe_offset_or_config2 = if size >= PERF_ATTR_SIZE_VER1 {
reader.read_u64::<T>()?
} else {
0
};
let branch_sample_type = if size >= PERF_ATTR_SIZE_VER2 {
reader.read_u64::<T>()?
} else {
0
};
let (sample_regs_user, sample_stack_user, clockid) = if size >= PERF_ATTR_SIZE_VER3 {
let sample_regs_user = reader.read_u64::<T>()?;
let sample_stack_user = reader.read_u32::<T>()?;
let clockid = reader.read_u32::<T>()?;
(sample_regs_user, sample_stack_user, clockid)
} else {
(0, 0, 0)
};
let sample_regs_intr = if size >= PERF_ATTR_SIZE_VER4 {
reader.read_u64::<T>()?
} else {
0
};
let (aux_watermark, sample_max_stack) = if size >= PERF_ATTR_SIZE_VER5 {
let aux_watermark = reader.read_u32::<T>()?;
let sample_max_stack = reader.read_u16::<T>()?;
let __reserved_2 = reader.read_u16::<T>()?;
(aux_watermark, sample_max_stack)
} else {
(0, 0)
};
let aux_sample_size = if size >= PERF_ATTR_SIZE_VER6 {
let aux_sample_size = reader.read_u32::<T>()?;
let __reserved_3 = reader.read_u32::<T>()?;
aux_sample_size
} else {
0
};
let sig_data = if size >= PERF_ATTR_SIZE_VER7 {
reader.read_u64::<T>()?
} else {
0
};
if size > PERF_ATTR_SIZE_VER7 {
let remaining = size - PERF_ATTR_SIZE_VER7;
io::copy(&mut reader.by_ref().take(remaining.into()), &mut io::sink())?;
}
let flags = AttrFlags::from_bits_truncate(flags);
let type_ = PerfEventType::parse(
type_,
bp_type,
config,
bp_addr_or_kprobe_func_or_uprobe_func_or_config1,
bp_len_or_kprobe_addr_or_probe_offset_or_config2,
)
.ok_or(io::ErrorKind::InvalidInput)?;
let sampling_policy = if flags.contains(AttrFlags::FREQ) {
SamplingPolicy::Frequency(sampling_period_or_frequency)
} else if let Some(period) = NonZeroU64::new(sampling_period_or_frequency) {
SamplingPolicy::Period(period)
} else {
SamplingPolicy::NoSampling
};
let wakeup_policy = if flags.contains(AttrFlags::WATERMARK) {
WakeupPolicy::Watermark(wakeup_events_or_watermark)
} else {
WakeupPolicy::EventCount(wakeup_events_or_watermark)
};
let clock = if flags.contains(AttrFlags::USE_CLOCKID) {
let clockid = ClockId::from_u32(clockid).ok_or(io::ErrorKind::InvalidInput)?;
PerfClock::ClockId(clockid)
} else {
PerfClock::Default
};
Ok(Self {
type_,
sampling_policy,
sample_format: SampleFormat::from_bits_truncate(sample_type),
read_format: ReadFormat::from_bits_truncate(read_format),
flags,
wakeup_policy,
branch_sample_format: BranchSampleFormat::from_bits_truncate(branch_sample_type),
sample_regs_user,
sample_stack_user,
clock,
sample_regs_intr,
aux_watermark,
sample_max_stack,
aux_sample_size,
sig_data,
})
}
}
#[derive(Debug, Clone, Copy)]
pub enum PerfEventType {
Hardware(HardwareEventId, PmuTypeId),
Software(SoftwareCounterType),
Tracepoint(u64),
HwCache(
HardwareCacheId,
HardwareCacheOp,
HardwareCacheOpResult,
PmuTypeId,
),
Breakpoint(HwBreakpointType, HwBreakpointAddr, HwBreakpointLen),
DynamicPmu(u32, u64, u64, u64),
}
#[derive(Debug, Clone, Copy)]
pub struct PmuTypeId(pub u32);
#[derive(Debug, Clone, Copy)]
pub struct HwBreakpointAddr(pub u64);
#[derive(Debug, Clone, Copy)]
pub struct HwBreakpointLen(pub u64);
impl PerfEventType {
pub fn parse(
type_: u32,
bp_type: u32,
config: u64,
config1: u64,
config2: u64,
) -> Option<Self> {
let t = match type_ {
PERF_TYPE_HARDWARE => {
let hardware_event_id = (config & 0xff) as u8;
let pmu_type = PmuTypeId((config >> 32) as u32);
Self::Hardware(HardwareEventId::parse(hardware_event_id)?, pmu_type)
}
PERF_TYPE_SOFTWARE => Self::Software(SoftwareCounterType::parse(config)?),
PERF_TYPE_TRACEPOINT => Self::Tracepoint(config),
PERF_TYPE_HW_CACHE => {
let cache_id = config as u8;
let cache_op_id = (config >> 8) as u8;
let cache_op_result = (config >> 16) as u8;
let pmu_type = PmuTypeId((config >> 32) as u32);
Self::HwCache(
HardwareCacheId::parse(cache_id)?,
HardwareCacheOp::parse(cache_op_id)?,
HardwareCacheOpResult::parse(cache_op_result)?,
pmu_type,
)
}
PERF_TYPE_BREAKPOINT => {
let bp_type = HwBreakpointType::from_bits_truncate(bp_type);
Self::Breakpoint(bp_type, HwBreakpointAddr(config1), HwBreakpointLen(config2))
}
_ => Self::DynamicPmu(type_, config, config1, config2),
};
Some(t)
}
}
#[derive(Debug, Clone, Copy)]
pub enum HardwareEventId {
CpuCycles,
Instructions,
CacheReferences,
CacheMisses,
BranchInstructions,
BranchMisses,
BusCycles,
StalledCyclesFrontend,
StalledCyclesBackend,
RefCpuCycles,
}
impl HardwareEventId {
pub fn parse(hardware_event_id: u8) -> Option<Self> {
let t = match hardware_event_id {
PERF_COUNT_HW_CPU_CYCLES => Self::CpuCycles,
PERF_COUNT_HW_INSTRUCTIONS => Self::Instructions,
PERF_COUNT_HW_CACHE_REFERENCES => Self::CacheReferences,
PERF_COUNT_HW_CACHE_MISSES => Self::CacheMisses,
PERF_COUNT_HW_BRANCH_INSTRUCTIONS => Self::BranchInstructions,
PERF_COUNT_HW_BRANCH_MISSES => Self::BranchMisses,
PERF_COUNT_HW_BUS_CYCLES => Self::BusCycles,
PERF_COUNT_HW_STALLED_CYCLES_FRONTEND => Self::StalledCyclesFrontend,
PERF_COUNT_HW_STALLED_CYCLES_BACKEND => Self::StalledCyclesBackend,
PERF_COUNT_HW_REF_CPU_CYCLES => Self::RefCpuCycles,
_ => return None,
};
Some(t)
}
}
#[derive(Debug, Clone, Copy)]
pub enum SoftwareCounterType {
CpuClock,
TaskClock,
PageFaults,
ContextSwitches,
CpuMigrations,
PageFaultsMin,
PageFaultsMaj,
AlignmentFaults,
EmulationFaults,
Dummy,
BpfOutput,
CgroupSwitches,
}
impl SoftwareCounterType {
pub fn parse(config: u64) -> Option<Self> {
let t = match config {
PERF_COUNT_SW_CPU_CLOCK => Self::CpuClock,
PERF_COUNT_SW_TASK_CLOCK => Self::TaskClock,
PERF_COUNT_SW_PAGE_FAULTS => Self::PageFaults,
PERF_COUNT_SW_CONTEXT_SWITCHES => Self::ContextSwitches,
PERF_COUNT_SW_CPU_MIGRATIONS => Self::CpuMigrations,
PERF_COUNT_SW_PAGE_FAULTS_MIN => Self::PageFaultsMin,
PERF_COUNT_SW_PAGE_FAULTS_MAJ => Self::PageFaultsMaj,
PERF_COUNT_SW_ALIGNMENT_FAULTS => Self::AlignmentFaults,
PERF_COUNT_SW_EMULATION_FAULTS => Self::EmulationFaults,
PERF_COUNT_SW_DUMMY => Self::Dummy,
PERF_COUNT_SW_BPF_OUTPUT => Self::BpfOutput,
PERF_COUNT_SW_CGROUP_SWITCHES => Self::CgroupSwitches,
_ => return None,
};
Some(t)
}
}
#[derive(Debug, Clone, Copy)]
pub enum HardwareCacheId {
L1d,
L1i,
Ll,
Dtlb,
Itlb,
Bpu,
Node,
}
impl HardwareCacheId {
pub fn parse(cache_id: u8) -> Option<Self> {
let rv = match cache_id {
PERF_COUNT_HW_CACHE_L1D => Self::L1d,
PERF_COUNT_HW_CACHE_L1I => Self::L1i,
PERF_COUNT_HW_CACHE_LL => Self::Ll,
PERF_COUNT_HW_CACHE_DTLB => Self::Dtlb,
PERF_COUNT_HW_CACHE_ITLB => Self::Itlb,
PERF_COUNT_HW_CACHE_BPU => Self::Bpu,
PERF_COUNT_HW_CACHE_NODE => Self::Node,
_ => return None,
};
Some(rv)
}
}
#[derive(Debug, Clone, Copy)]
pub enum HardwareCacheOp {
Read,
Write,
Prefetch,
}
impl HardwareCacheOp {
pub fn parse(cache_op: u8) -> Option<Self> {
match cache_op {
PERF_COUNT_HW_CACHE_OP_READ => Some(Self::Read),
PERF_COUNT_HW_CACHE_OP_WRITE => Some(Self::Write),
PERF_COUNT_HW_CACHE_OP_PREFETCH => Some(Self::Prefetch),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum HardwareCacheOpResult {
Access,
Miss,
}
impl HardwareCacheOpResult {
pub fn parse(cache_op_result: u8) -> Option<Self> {
match cache_op_result {
PERF_COUNT_HW_CACHE_RESULT_ACCESS => Some(Self::Access),
PERF_COUNT_HW_CACHE_RESULT_MISS => Some(Self::Miss),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum SamplingPolicy {
NoSampling,
Period(NonZeroU64),
Frequency(u64),
}
#[derive(Debug, Clone, Copy)]
pub enum WakeupPolicy {
EventCount(u32),
Watermark(u32),
}
#[derive(Debug, Clone, Copy)]
pub enum PerfClock {
Default,
ClockId(ClockId),
}