From 18c7f7ccd6d462516d94b8720d18f8b7b5d98361 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Fri, 7 Nov 2025 07:18:31 -0500 Subject: [PATCH] perf_event: push down type safety This makes it more difficult to mishandle callers of `perf_event_open`. Change `wakeup_events = 0` to 1; per `man 2 perf_event_open`: Prior to Linux 3.0, setting wakeup_events to 0 resulted in no overflow notifications; more recent kernels treat 0 the same as 1. --- aya/src/programs/mod.rs | 2 +- aya/src/programs/perf_event.rs | 82 +++++++++---------------------- aya/src/sys/perf_event.rs | 90 +++++++++++++++++++++++++--------- xtask/public-api/aya.txt | 81 ++---------------------------- 4 files changed, 96 insertions(+), 159 deletions(-) diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 2a9c0fda..3255381f 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -111,7 +111,7 @@ pub use crate::programs::{ lirc_mode2::LircMode2, lsm::Lsm, lsm_cgroup::LsmCgroup, - perf_event::{PerfEvent, PerfEventScope, SamplePolicy}, + perf_event::PerfEvent, probe::ProbeKind, raw_trace_point::RawTracePoint, sk_lookup::SkLookup, diff --git a/aya/src/programs/perf_event.rs b/aya/src/programs/perf_event.rs index 3ade1973..0b52bd04 100644 --- a/aya/src/programs/perf_event.rs +++ b/aya/src/programs/perf_event.rs @@ -3,14 +3,8 @@ use std::os::fd::AsFd as _; use aya_obj::generated::{ - 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, - perf_type_id::{ - PERF_TYPE_BREAKPOINT, PERF_TYPE_HARDWARE, PERF_TYPE_HW_CACHE, PERF_TYPE_RAW, - PERF_TYPE_SOFTWARE, PERF_TYPE_TRACEPOINT, - }, + 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::{ @@ -84,7 +78,7 @@ pub enum PerfEventConfig { macro_rules! impl_to_u32 { ($($t:ty, $fn:ident),*) => { - $(const fn $fn(id: $t) -> u32 { + $(pub(crate) const fn $fn(id: $t) -> u32 { const _: [(); 4] = [(); std::mem::size_of::<$t>()]; id as u32 })* @@ -144,7 +138,7 @@ pub enum HardwareEvent { } impl HardwareEvent { - const fn into_primitive(self) -> u32 { + pub(crate) const fn into_primitive(self) -> u32 { const _: [(); 4] = [(); std::mem::size_of::()]; self as u32 } @@ -194,7 +188,7 @@ pub enum SoftwareEvent { } impl SoftwareEvent { - const fn into_primitive(self) -> u32 { + pub(crate) const fn into_primitive(self) -> u32 { const _: [(); 4] = [(); std::mem::size_of::()]; self as u32 } @@ -229,7 +223,7 @@ pub enum HwCacheEvent { } impl HwCacheEvent { - const fn into_primitive(self) -> u32 { + pub(crate) const fn into_primitive(self) -> u32 { const _: [(); 4] = [(); std::mem::size_of::()]; self as u32 } @@ -252,7 +246,7 @@ pub enum HwCacheOp { } impl HwCacheOp { - const fn into_primitive(self) -> u32 { + pub(crate) const fn into_primitive(self) -> u32 { const _: [(); 4] = [(); std::mem::size_of::()]; self as u32 } @@ -276,14 +270,14 @@ pub enum HwCacheResult { } impl HwCacheResult { - const fn into_primitive(self) -> u32 { + pub(crate) const fn into_primitive(self) -> u32 { const _: [(); 4] = [(); std::mem::size_of::()]; self as u32 } } /// Sample Policy -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub enum SamplePolicy { /// Period Period(u64), @@ -291,8 +285,18 @@ pub enum SamplePolicy { Frequency(u64), } +/// Wakeup Policy +#[derive(Debug, Clone, Copy)] +pub(crate) enum WakeupPolicy { + /// Every N events + Events(u32), + /// Every N bytes + #[expect(dead_code)] + Watermark(u32), +} + /// The scope of a PerfEvent -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub enum PerfEventScope { /// calling process CallingProcess { @@ -381,7 +385,7 @@ impl PerfEvent { /// The returned value can be used to detach, see [PerfEvent::detach]. pub fn attach( &mut self, - perf_type: PerfEventConfig, + config: PerfEventConfig, scope: PerfEventScope, sample_policy: SamplePolicy, inherit: bool, @@ -389,49 +393,11 @@ impl PerfEvent { let prog_fd = self.fd()?; let prog_fd = prog_fd.as_fd(); - let (perf_type, config) = match perf_type { - PerfEventConfig::Pmu { pmu_type, config } => (pmu_type, config), - PerfEventConfig::Hardware(hw_event) => ( - perf_type_id_to_u32(PERF_TYPE_HARDWARE), - u64::from(hw_event.into_primitive()), - ), - PerfEventConfig::Software(sw_event) => ( - perf_type_id_to_u32(PERF_TYPE_SOFTWARE), - u64::from(sw_event.into_primitive()), - ), - PerfEventConfig::TracePoint { event_id } => { - (perf_type_id_to_u32(PERF_TYPE_TRACEPOINT), event_id) - } - PerfEventConfig::HwCache { - event, - operation, - result, - } => ( - perf_type_id_to_u32(PERF_TYPE_HW_CACHE), - u64::from(event.into_primitive()) - | (u64::from(operation.into_primitive()) << 8) - | (u64::from(result.into_primitive()) << 16), - ), - PerfEventConfig::Raw { event_id } => (perf_type_id_to_u32(PERF_TYPE_RAW), event_id), - PerfEventConfig::Breakpoint => (perf_type_id_to_u32(PERF_TYPE_BREAKPOINT), 0), - }; - let (sample_period, sample_frequency) = match sample_policy { - SamplePolicy::Period(period) => (period, None), - SamplePolicy::Frequency(frequency) => (0, Some(frequency)), - }; - let (pid, cpu) = match scope { - PerfEventScope::CallingProcess { cpu } => (0, cpu.map_or(-1, |cpu| cpu as i32)), - PerfEventScope::OneProcess { pid, cpu } => (pid, cpu.map_or(-1, |cpu| cpu as i32)), - PerfEventScope::AllProcessesOneCpu { cpu } => (-1, cpu as i32), - }; let fd = perf_event_open( - perf_type, config, - pid, - cpu, - sample_period, - sample_frequency, - false, + scope, + sample_policy, + WakeupPolicy::Events(1), inherit, 0, ) diff --git a/aya/src/sys/perf_event.rs b/aya/src/sys/perf_event.rs index afedefa9..3514ef4e 100644 --- a/aya/src/sys/perf_event.rs +++ b/aya/src/sys/perf_event.rs @@ -7,53 +7,97 @@ use std::{ use aya_obj::generated::{ PERF_FLAG_FD_CLOEXEC, perf_event_attr, perf_event_sample_format::PERF_SAMPLE_RAW, - perf_sw_ids::PERF_COUNT_SW_BPF_OUTPUT, - perf_type_id::{PERF_TYPE_SOFTWARE, PERF_TYPE_TRACEPOINT}, + perf_type_id::{ + PERF_TYPE_BREAKPOINT, PERF_TYPE_HARDWARE, PERF_TYPE_HW_CACHE, PERF_TYPE_RAW, + PERF_TYPE_SOFTWARE, PERF_TYPE_TRACEPOINT, + }, }; use libc::pid_t; use super::{PerfEventIoctlRequest, Syscall, syscall}; +use crate::programs::perf_event::{ + PerfEventConfig, PerfEventScope, SamplePolicy, SoftwareEvent, WakeupPolicy, perf_type_id_to_u32, +}; -#[expect(clippy::too_many_arguments)] pub(crate) fn perf_event_open( - perf_type: u32, - config: u64, - pid: pid_t, - cpu: c_int, - sample_period: u64, - sample_frequency: Option, - wakeup: bool, + config: PerfEventConfig, + scope: PerfEventScope, + sample_policy: SamplePolicy, + wakeup_policy: WakeupPolicy, inherit: bool, flags: u32, ) -> io::Result { let mut attr = unsafe { mem::zeroed::() }; + let (perf_type, config) = match config { + PerfEventConfig::Pmu { pmu_type, config } => (pmu_type, config), + PerfEventConfig::Hardware(hw_event) => ( + perf_type_id_to_u32(PERF_TYPE_HARDWARE), + u64::from(hw_event.into_primitive()), + ), + PerfEventConfig::Software(sw_event) => ( + perf_type_id_to_u32(PERF_TYPE_SOFTWARE), + u64::from(sw_event.into_primitive()), + ), + PerfEventConfig::TracePoint { event_id } => { + (perf_type_id_to_u32(PERF_TYPE_TRACEPOINT), event_id) + } + PerfEventConfig::HwCache { + event, + operation, + result, + } => ( + perf_type_id_to_u32(PERF_TYPE_HW_CACHE), + u64::from(event.into_primitive()) + | (u64::from(operation.into_primitive()) << 8) + | (u64::from(result.into_primitive()) << 16), + ), + PerfEventConfig::Raw { event_id } => (perf_type_id_to_u32(PERF_TYPE_RAW), event_id), + PerfEventConfig::Breakpoint => (perf_type_id_to_u32(PERF_TYPE_BREAKPOINT), 0), + }; + attr.config = config; attr.size = mem::size_of::() as u32; attr.type_ = perf_type; attr.sample_type = PERF_SAMPLE_RAW as u64; attr.set_inherit(if inherit { 1 } else { 0 }); - attr.__bindgen_anon_2.wakeup_events = u32::from(wakeup); - if let Some(frequency) = sample_frequency { - attr.set_freq(1); - attr.__bindgen_anon_1.sample_freq = frequency; - } else { - attr.__bindgen_anon_1.sample_period = sample_period; + match sample_policy { + SamplePolicy::Period(period) => { + attr.__bindgen_anon_1.sample_period = period; + } + SamplePolicy::Frequency(frequency) => { + attr.set_freq(1); + attr.__bindgen_anon_1.sample_freq = frequency; + } } + match wakeup_policy { + WakeupPolicy::Events(events) => { + attr.__bindgen_anon_2.wakeup_events = events; + } + WakeupPolicy::Watermark(watermark) => { + attr.set_watermark(1); + attr.__bindgen_anon_2.wakeup_watermark = watermark; + } + } + + let (pid, cpu) = match scope { + PerfEventScope::CallingProcess { cpu } => (0, cpu.map_or(-1, |cpu| cpu as i32)), + PerfEventScope::OneProcess { pid, cpu } => (pid, cpu.map_or(-1, |cpu| cpu as i32)), + PerfEventScope::AllProcessesOneCpu { cpu } => (-1, cpu as i32), + }; + perf_event_sys(attr, pid, cpu, flags) } pub(crate) fn perf_event_open_bpf(cpu: c_int) -> io::Result { + let cpu = cpu as u32; perf_event_open( - PERF_TYPE_SOFTWARE as u32, - PERF_COUNT_SW_BPF_OUTPUT as u64, - -1, - cpu, - 1, - None, - true, + PerfEventConfig::Software(SoftwareEvent::BpfOutput), + PerfEventScope::AllProcessesOneCpu { cpu }, + SamplePolicy::Period(1), + WakeupPolicy::Events(1), false, PERF_FLAG_FD_CLOEXEC, ) diff --git a/xtask/public-api/aya.txt b/xtask/public-api/aya.txt index f398a0bc..0bd2328f 100644 --- a/xtask/public-api/aya.txt +++ b/xtask/public-api/aya.txt @@ -5517,6 +5517,7 @@ impl core::clone::Clone for aya::programs::perf_event::PerfEventScope pub fn aya::programs::perf_event::PerfEventScope::clone(&self) -> aya::programs::perf_event::PerfEventScope impl core::fmt::Debug for aya::programs::perf_event::PerfEventScope pub fn aya::programs::perf_event::PerfEventScope::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Copy for aya::programs::perf_event::PerfEventScope impl core::marker::Freeze for aya::programs::perf_event::PerfEventScope impl core::marker::Send for aya::programs::perf_event::PerfEventScope impl core::marker::Sync for aya::programs::perf_event::PerfEventScope @@ -5552,6 +5553,7 @@ impl core::clone::Clone for aya::programs::perf_event::SamplePolicy pub fn aya::programs::perf_event::SamplePolicy::clone(&self) -> aya::programs::perf_event::SamplePolicy impl core::fmt::Debug for aya::programs::perf_event::SamplePolicy pub fn aya::programs::perf_event::SamplePolicy::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Copy for aya::programs::perf_event::SamplePolicy impl core::marker::Freeze for aya::programs::perf_event::SamplePolicy impl core::marker::Send for aya::programs::perf_event::SamplePolicy impl core::marker::Sync for aya::programs::perf_event::SamplePolicy @@ -5629,7 +5631,7 @@ pub fn aya::programs::perf_event::SoftwareEvent::from(t: T) -> T pub struct aya::programs::perf_event::PerfEvent impl aya::programs::perf_event::PerfEvent pub const aya::programs::perf_event::PerfEvent::PROGRAM_TYPE: aya::programs::ProgramType -pub fn aya::programs::perf_event::PerfEvent::attach(&mut self, perf_type: aya::programs::perf_event::PerfEventConfig, scope: aya::programs::perf_event::PerfEventScope, sample_policy: aya::programs::perf_event::SamplePolicy, inherit: bool) -> core::result::Result +pub fn aya::programs::perf_event::PerfEvent::attach(&mut self, config: aya::programs::perf_event::PerfEventConfig, scope: aya::programs::perf_event::PerfEventScope, sample_policy: aya::programs::perf_event::SamplePolicy, inherit: bool) -> core::result::Result pub fn aya::programs::perf_event::PerfEvent::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError> impl aya::programs::perf_event::PerfEvent pub fn aya::programs::perf_event::PerfEvent::detach(&mut self, link_id: aya::programs::perf_event::PerfEventLinkId) -> core::result::Result<(), aya::programs::ProgramError> @@ -7940,46 +7942,6 @@ impl core::clone::CloneToUninit for aya::programs::LsmAttachType where T: cor pub unsafe fn aya::programs::LsmAttachType::clone_to_uninit(&self, dest: *mut u8) impl core::convert::From for aya::programs::LsmAttachType pub fn aya::programs::LsmAttachType::from(t: T) -> T -pub enum aya::programs::PerfEventScope -pub aya::programs::PerfEventScope::AllProcessesOneCpu -pub aya::programs::PerfEventScope::AllProcessesOneCpu::cpu: u32 -pub aya::programs::PerfEventScope::CallingProcess -pub aya::programs::PerfEventScope::CallingProcess::cpu: core::option::Option -pub aya::programs::PerfEventScope::OneProcess -pub aya::programs::PerfEventScope::OneProcess::cpu: core::option::Option -pub aya::programs::PerfEventScope::OneProcess::pid: i32 -impl core::clone::Clone for aya::programs::perf_event::PerfEventScope -pub fn aya::programs::perf_event::PerfEventScope::clone(&self) -> aya::programs::perf_event::PerfEventScope -impl core::fmt::Debug for aya::programs::perf_event::PerfEventScope -pub fn aya::programs::perf_event::PerfEventScope::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::Freeze for aya::programs::perf_event::PerfEventScope -impl core::marker::Send for aya::programs::perf_event::PerfEventScope -impl core::marker::Sync for aya::programs::perf_event::PerfEventScope -impl core::marker::Unpin for aya::programs::perf_event::PerfEventScope -impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::perf_event::PerfEventScope -impl core::panic::unwind_safe::UnwindSafe for aya::programs::perf_event::PerfEventScope -impl core::convert::Into for aya::programs::perf_event::PerfEventScope where U: core::convert::From -pub fn aya::programs::perf_event::PerfEventScope::into(self) -> U -impl core::convert::TryFrom for aya::programs::perf_event::PerfEventScope where U: core::convert::Into -pub type aya::programs::perf_event::PerfEventScope::Error = core::convert::Infallible -pub fn aya::programs::perf_event::PerfEventScope::try_from(value: U) -> core::result::Result>::Error> -impl core::convert::TryInto for aya::programs::perf_event::PerfEventScope where U: core::convert::TryFrom -pub type aya::programs::perf_event::PerfEventScope::Error = >::Error -pub fn aya::programs::perf_event::PerfEventScope::try_into(self) -> core::result::Result>::Error> -impl alloc::borrow::ToOwned for aya::programs::perf_event::PerfEventScope where T: core::clone::Clone -pub type aya::programs::perf_event::PerfEventScope::Owned = T -pub fn aya::programs::perf_event::PerfEventScope::clone_into(&self, target: &mut T) -pub fn aya::programs::perf_event::PerfEventScope::to_owned(&self) -> T -impl core::any::Any for aya::programs::perf_event::PerfEventScope where T: 'static + ?core::marker::Sized -pub fn aya::programs::perf_event::PerfEventScope::type_id(&self) -> core::any::TypeId -impl core::borrow::Borrow for aya::programs::perf_event::PerfEventScope where T: ?core::marker::Sized -pub fn aya::programs::perf_event::PerfEventScope::borrow(&self) -> &T -impl core::borrow::BorrowMut for aya::programs::perf_event::PerfEventScope where T: ?core::marker::Sized -pub fn aya::programs::perf_event::PerfEventScope::borrow_mut(&mut self) -> &mut T -impl core::clone::CloneToUninit for aya::programs::perf_event::PerfEventScope where T: core::clone::Clone -pub unsafe fn aya::programs::perf_event::PerfEventScope::clone_to_uninit(&self, dest: *mut u8) -impl core::convert::From for aya::programs::perf_event::PerfEventScope -pub fn aya::programs::perf_event::PerfEventScope::from(t: T) -> T pub enum aya::programs::ProbeKind pub aya::programs::ProbeKind::KProbe pub aya::programs::ProbeKind::KRetProbe @@ -8390,41 +8352,6 @@ impl core::clone::CloneToUninit for aya::programs::ProgramType where T: core: pub unsafe fn aya::programs::ProgramType::clone_to_uninit(&self, dest: *mut u8) impl core::convert::From for aya::programs::ProgramType pub fn aya::programs::ProgramType::from(t: T) -> T -pub enum aya::programs::SamplePolicy -pub aya::programs::SamplePolicy::Frequency(u64) -pub aya::programs::SamplePolicy::Period(u64) -impl core::clone::Clone for aya::programs::perf_event::SamplePolicy -pub fn aya::programs::perf_event::SamplePolicy::clone(&self) -> aya::programs::perf_event::SamplePolicy -impl core::fmt::Debug for aya::programs::perf_event::SamplePolicy -pub fn aya::programs::perf_event::SamplePolicy::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::Freeze for aya::programs::perf_event::SamplePolicy -impl core::marker::Send for aya::programs::perf_event::SamplePolicy -impl core::marker::Sync for aya::programs::perf_event::SamplePolicy -impl core::marker::Unpin for aya::programs::perf_event::SamplePolicy -impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::perf_event::SamplePolicy -impl core::panic::unwind_safe::UnwindSafe for aya::programs::perf_event::SamplePolicy -impl core::convert::Into for aya::programs::perf_event::SamplePolicy where U: core::convert::From -pub fn aya::programs::perf_event::SamplePolicy::into(self) -> U -impl core::convert::TryFrom for aya::programs::perf_event::SamplePolicy where U: core::convert::Into -pub type aya::programs::perf_event::SamplePolicy::Error = core::convert::Infallible -pub fn aya::programs::perf_event::SamplePolicy::try_from(value: U) -> core::result::Result>::Error> -impl core::convert::TryInto for aya::programs::perf_event::SamplePolicy where U: core::convert::TryFrom -pub type aya::programs::perf_event::SamplePolicy::Error = >::Error -pub fn aya::programs::perf_event::SamplePolicy::try_into(self) -> core::result::Result>::Error> -impl alloc::borrow::ToOwned for aya::programs::perf_event::SamplePolicy where T: core::clone::Clone -pub type aya::programs::perf_event::SamplePolicy::Owned = T -pub fn aya::programs::perf_event::SamplePolicy::clone_into(&self, target: &mut T) -pub fn aya::programs::perf_event::SamplePolicy::to_owned(&self) -> T -impl core::any::Any for aya::programs::perf_event::SamplePolicy where T: 'static + ?core::marker::Sized -pub fn aya::programs::perf_event::SamplePolicy::type_id(&self) -> core::any::TypeId -impl core::borrow::Borrow for aya::programs::perf_event::SamplePolicy where T: ?core::marker::Sized -pub fn aya::programs::perf_event::SamplePolicy::borrow(&self) -> &T -impl core::borrow::BorrowMut for aya::programs::perf_event::SamplePolicy where T: ?core::marker::Sized -pub fn aya::programs::perf_event::SamplePolicy::borrow_mut(&mut self) -> &mut T -impl core::clone::CloneToUninit for aya::programs::perf_event::SamplePolicy where T: core::clone::Clone -pub unsafe fn aya::programs::perf_event::SamplePolicy::clone_to_uninit(&self, dest: *mut u8) -impl core::convert::From for aya::programs::perf_event::SamplePolicy -pub fn aya::programs::perf_event::SamplePolicy::from(t: T) -> T pub enum aya::programs::SkSkbKind pub aya::programs::SkSkbKind::StreamParser pub aya::programs::SkSkbKind::StreamVerdict @@ -9576,7 +9503,7 @@ pub fn aya::programs::lsm_cgroup::LsmCgroup::from(t: T) -> T pub struct aya::programs::PerfEvent impl aya::programs::perf_event::PerfEvent pub const aya::programs::perf_event::PerfEvent::PROGRAM_TYPE: aya::programs::ProgramType -pub fn aya::programs::perf_event::PerfEvent::attach(&mut self, perf_type: aya::programs::perf_event::PerfEventConfig, scope: aya::programs::perf_event::PerfEventScope, sample_policy: aya::programs::perf_event::SamplePolicy, inherit: bool) -> core::result::Result +pub fn aya::programs::perf_event::PerfEvent::attach(&mut self, config: aya::programs::perf_event::PerfEventConfig, scope: aya::programs::perf_event::PerfEventScope, sample_policy: aya::programs::perf_event::SamplePolicy, inherit: bool) -> core::result::Result pub fn aya::programs::perf_event::PerfEvent::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError> impl aya::programs::perf_event::PerfEvent pub fn aya::programs::perf_event::PerfEvent::detach(&mut self, link_id: aya::programs::perf_event::PerfEventLinkId) -> core::result::Result<(), aya::programs::ProgramError>