use std::os::fd::AsFd as _;
use aya_obj::generated::{
HW_BREAKPOINT_LEN_1, HW_BREAKPOINT_LEN_2, HW_BREAKPOINT_LEN_4, HW_BREAKPOINT_LEN_8,
HW_BREAKPOINT_R, HW_BREAKPOINT_RW, HW_BREAKPOINT_W, bpf_link_type,
bpf_prog_type::BPF_PROG_TYPE_PERF_EVENT, perf_hw_cache_id, perf_hw_cache_op_id,
perf_hw_cache_op_result_id, perf_hw_id, perf_sw_ids, perf_type_id,
};
use crate::{
programs::{
FdLink, LinkError, ProgramData, ProgramError, ProgramType, impl_try_into_fdlink,
links::define_link_wrapper,
load_program,
perf_attach::{PerfLinkIdInner, PerfLinkInner, perf_attach},
},
sys::{SyscallError, bpf_link_get_info_by_fd, perf_event_open},
};
#[doc(alias = "perf_type_id")]
#[derive(Debug, Clone, Copy)]
pub enum PerfEventConfig {
#[doc(alias = "PERF_TYPE_HARDWARE")]
Hardware(HardwareEvent),
#[doc(alias = "PERF_TYPE_SOFTWARE")]
Software(SoftwareEvent),
#[doc(alias = "PERF_TYPE_TRACEPOINT")]
TracePoint {
event_id: u64,
},
#[doc(alias = "PERF_TYPE_HW_CACHE")]
HwCache {
event: HwCacheEvent,
operation: HwCacheOp,
result: HwCacheResult,
},
#[doc(alias = "PERF_TYPE_RAW")]
Raw {
event_id: u64,
},
#[doc(alias = "PERF_TYPE_BREAKPOINT")]
Breakpoint(BreakpointConfig),
Pmu {
pmu_type: u32,
config: u64,
},
}
macro_rules! impl_to_u32 {
($($t:ty, $fn:ident),*) => {
$(pub(crate) const fn $fn(id: $t) -> u32 {
const _: [(); 4] = [(); std::mem::size_of::<$t>()];
id as u32
})*
};
}
impl_to_u32!(
perf_hw_id,
perf_hw_id_to_u32,
perf_sw_ids,
perf_sw_ids_to_u32,
perf_hw_cache_id,
perf_hw_cache_id_to_u32,
perf_hw_cache_op_id,
perf_hw_cache_op_id_to_u32,
perf_hw_cache_op_result_id,
perf_hw_cache_op_result_id_to_u32,
perf_type_id,
perf_type_id_to_u32
);
#[doc(alias = "perf_hw_id")]
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum HardwareEvent {
#[doc(alias = "PERF_COUNT_HW_CPU_CYCLES")]
CpuCycles = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_CPU_CYCLES),
#[doc(alias = "PERF_COUNT_HW_INSTRUCTIONS")]
Instructions = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_INSTRUCTIONS),
#[doc(alias = "PERF_COUNT_HW_CACHE_REFERENCES")]
CacheReferences = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_CACHE_REFERENCES),
#[doc(alias = "PERF_COUNT_HW_CACHE_MISSES")]
CacheMisses = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_CACHE_MISSES),
#[doc(alias = "PERF_COUNT_HW_BRANCH_INSTRUCTIONS")]
BranchInstructions = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_BRANCH_INSTRUCTIONS),
#[doc(alias = "PERF_COUNT_HW_BRANCH_MISSES")]
BranchMisses = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_BRANCH_MISSES),
#[doc(alias = "PERF_COUNT_HW_BUS_CYCLES")]
BusCycles = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_BUS_CYCLES),
#[doc(alias = "PERF_COUNT_HW_STALLED_CYCLES_FRONTEND")]
StalledCyclesFrontend = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_STALLED_CYCLES_FRONTEND),
#[doc(alias = "PERF_COUNT_HW_STALLED_CYCLES_BACKEND")]
StalledCyclesBackend = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_STALLED_CYCLES_BACKEND),
#[doc(alias = "PERF_COUNT_HW_REF_CPU_CYCLES")]
RefCpuCycles = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_REF_CPU_CYCLES),
}
impl HardwareEvent {
pub(crate) const fn into_primitive(self) -> u32 {
const _: [(); 4] = [(); size_of::<HardwareEvent>()];
self as u32
}
}
#[doc(alias = "perf_sw_ids")]
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum SoftwareEvent {
#[doc(alias = "PERF_COUNT_SW_CPU_CLOCK")]
CpuClock = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_CPU_CLOCK),
#[doc(alias = "PERF_COUNT_SW_TASK_CLOCK")]
TaskClock = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_TASK_CLOCK),
#[doc(alias = "PERF_COUNT_SW_PAGE_FAULTS")]
PageFaults = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_PAGE_FAULTS),
#[doc(alias = "PERF_COUNT_SW_CONTEXT_SWITCHES")]
ContextSwitches = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_CONTEXT_SWITCHES),
#[doc(alias = "PERF_COUNT_SW_CPU_MIGRATIONS")]
CpuMigrations = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_CPU_MIGRATIONS),
#[doc(alias = "PERF_COUNT_SW_PAGE_FAULTS_MIN")]
PageFaultsMin = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_PAGE_FAULTS_MIN),
#[doc(alias = "PERF_COUNT_SW_PAGE_FAULTS_MAJ")]
PageFaultsMaj = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_PAGE_FAULTS_MAJ),
#[doc(alias = "PERF_COUNT_SW_ALIGNMENT_FAULTS")]
AlignmentFaults = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_ALIGNMENT_FAULTS),
#[doc(alias = "PERF_COUNT_SW_EMULATION_FAULTS")]
EmulationFaults = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_EMULATION_FAULTS),
#[doc(alias = "PERF_COUNT_SW_DUMMY")]
Dummy = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_DUMMY),
#[doc(alias = "PERF_COUNT_SW_BPF_OUTPUT")]
BpfOutput = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_BPF_OUTPUT),
#[doc(alias = "PERF_COUNT_SW_CGROUP_SWITCHES")]
CgroupSwitches = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_CGROUP_SWITCHES),
}
impl SoftwareEvent {
pub(crate) const fn into_primitive(self) -> u32 {
const _: [(); 4] = [(); size_of::<SoftwareEvent>()];
self as u32
}
}
#[doc(alias = "perf_hw_cache_id")]
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum HwCacheEvent {
#[doc(alias = "PERF_COUNT_HW_CACHE_L1D")]
L1d = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_L1D),
#[doc(alias = "PERF_COUNT_HW_CACHE_L1I")]
L1i = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_L1I),
#[doc(alias = "PERF_COUNT_HW_CACHE_LL")]
Ll = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_LL),
#[doc(alias = "PERF_COUNT_HW_CACHE_DTLB")]
Dtlb = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_DTLB),
#[doc(alias = "PERF_COUNT_HW_CACHE_ITLB")]
Itlb = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_ITLB),
#[doc(alias = "PERF_COUNT_HW_CACHE_BPU")]
Bpu = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_BPU),
#[doc(alias = "PERF_COUNT_HW_CACHE_NODE")]
Node = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_NODE),
}
impl HwCacheEvent {
pub(crate) const fn into_primitive(self) -> u32 {
const _: [(); 4] = [(); size_of::<HwCacheEvent>()];
self as u32
}
}
#[doc(alias = "perf_hw_cache_op_id")]
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum HwCacheOp {
#[doc(alias = "PERF_COUNT_HW_CACHE_OP_READ")]
Read = perf_hw_cache_op_id_to_u32(perf_hw_cache_op_id::PERF_COUNT_HW_CACHE_OP_READ),
#[doc(alias = "PERF_COUNT_HW_CACHE_OP_WRITE")]
Write = perf_hw_cache_op_id_to_u32(perf_hw_cache_op_id::PERF_COUNT_HW_CACHE_OP_WRITE),
#[doc(alias = "PERF_COUNT_HW_CACHE_OP_PREFETCH")]
Prefetch = perf_hw_cache_op_id_to_u32(perf_hw_cache_op_id::PERF_COUNT_HW_CACHE_OP_PREFETCH),
}
impl HwCacheOp {
pub(crate) const fn into_primitive(self) -> u32 {
const _: [(); 4] = [(); size_of::<HwCacheOp>()];
self as u32
}
}
#[doc(alias = "perf_hw_cache_op_result_id")]
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum HwCacheResult {
#[doc(alias = "PERF_COUNT_HW_CACHE_RESULT_ACCESS")]
Access = perf_hw_cache_op_result_id_to_u32(
perf_hw_cache_op_result_id::PERF_COUNT_HW_CACHE_RESULT_ACCESS,
),
#[doc(alias = "PERF_COUNT_HW_CACHE_RESULT_MISS")]
Miss = perf_hw_cache_op_result_id_to_u32(
perf_hw_cache_op_result_id::PERF_COUNT_HW_CACHE_RESULT_MISS,
),
}
impl HwCacheResult {
pub(crate) const fn into_primitive(self) -> u32 {
const _: [(); 4] = [(); size_of::<HwCacheResult>()];
self as u32
}
}
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
pub enum PerfBreakpointType {
#[doc(alias = "HW_BREAKPOINT_R")]
Read = HW_BREAKPOINT_R,
#[doc(alias = "HW_BREAKPOINT_W")]
Write = HW_BREAKPOINT_W,
#[doc(alias = "HW_BREAKPOINT_RW")]
ReadWrite = HW_BREAKPOINT_RW,
}
impl PerfBreakpointType {
pub(crate) const fn into_primitive(self) -> u32 {
const _: [(); 4] = [(); size_of::<PerfBreakpointType>()];
self as u32
}
}
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
pub enum PerfBreakpointLength {
#[doc(alias = "HW_BREAKPOINT_LEN_1")]
Len1 = HW_BREAKPOINT_LEN_1,
#[doc(alias = "HW_BREAKPOINT_LEN_2")]
Len2 = HW_BREAKPOINT_LEN_2,
#[doc(alias = "HW_BREAKPOINT_LEN_4")]
Len4 = HW_BREAKPOINT_LEN_4,
#[doc(alias = "HW_BREAKPOINT_LEN_8")]
Len8 = HW_BREAKPOINT_LEN_8,
}
impl PerfBreakpointLength {
pub(crate) const fn into_primitive(self) -> u32 {
const _: [(); 4] = [(); size_of::<PerfBreakpointLength>()];
self as u32
}
}
#[derive(Debug, Clone, Copy)]
pub enum BreakpointConfig {
Data {
r#type: PerfBreakpointType,
address: u64,
length: PerfBreakpointLength,
},
#[doc(alias = "HW_BREAKPOINT_X")]
Instruction {
address: u64,
},
}
#[derive(Debug, Clone, Copy)]
pub enum SamplePolicy {
Period(u64),
Frequency(u64),
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum WakeupPolicy {
Events(u32),
#[expect(dead_code, reason = "TODO")]
Watermark(u32),
}
#[derive(Debug, Clone, Copy)]
pub enum PerfEventScope {
CallingProcess {
cpu: Option<u32>,
},
OneProcess {
pid: u32,
cpu: Option<u32>,
},
AllProcessesOneCpu {
cpu: u32,
},
}
#[derive(Debug)]
#[doc(alias = "BPF_PROG_TYPE_PERF_EVENT")]
pub struct PerfEvent {
pub(crate) data: ProgramData<PerfEventLink>,
}
impl PerfEvent {
pub const PROGRAM_TYPE: ProgramType = ProgramType::PerfEvent;
pub fn load(&mut self) -> Result<(), ProgramError> {
load_program(BPF_PROG_TYPE_PERF_EVENT, &mut self.data)
}
pub fn attach(
&mut self,
config: PerfEventConfig,
scope: PerfEventScope,
sample_policy: SamplePolicy,
inherit: bool,
) -> Result<PerfEventLinkId, ProgramError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let perf_fd = perf_event_open(
config,
scope,
sample_policy,
WakeupPolicy::Events(1),
inherit,
0,
)
.map_err(|io_error| SyscallError {
call: "perf_event_open",
io_error,
})?;
let link = perf_attach(prog_fd, perf_fd, None )?;
self.data.links.insert(PerfEventLink::new(link))
}
}
impl_try_into_fdlink!(PerfEventLink, PerfLinkInner);
impl TryFrom<FdLink> for PerfEventLink {
type Error = LinkError;
fn try_from(fd_link: FdLink) -> Result<Self, Self::Error> {
let info = bpf_link_get_info_by_fd(fd_link.fd.as_fd())?;
if info.type_ == (bpf_link_type::BPF_LINK_TYPE_PERF_EVENT as u32) {
return Ok(Self::new(PerfLinkInner::Fd(fd_link)));
}
Err(LinkError::InvalidLink)
}
}
define_link_wrapper!(
PerfEventLink,
PerfEventLinkId,
PerfLinkInner,
PerfLinkIdInner,
PerfEvent,
);