Add support for PerfEvent programs.

perf-event
Markus Stange 3 years ago committed by Alessandro Decina
parent 269be03a89
commit c39dff6025

@ -19,9 +19,9 @@ use crate::{
Object, ParseError, ProgramSection,
},
programs::{
CgroupSkb, CgroupSkbAttachType, KProbe, LircMode2, ProbeKind, Program, ProgramData,
ProgramError, SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint,
UProbe, Xdp,
CgroupSkb, CgroupSkbAttachType, KProbe, LircMode2, PerfEvent, ProbeKind, Program,
ProgramData, ProgramError, SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter,
TracePoint, UProbe, Xdp,
},
sys::bpf_map_update_elem_ptr,
util::{possible_cpus, POSSIBLE_CPUS},
@ -202,6 +202,7 @@ impl Bpf {
expected_attach_type: Some(CgroupSkbAttachType::Egress),
}),
ProgramSection::LircMode2 { .. } => Program::LircMode2(LircMode2 { data }),
ProgramSection::PerfEvent { .. } => Program::PerfEvent(PerfEvent { data }),
};
(name, program)

@ -15,7 +15,7 @@ use crate::{
perf_event_header, perf_event_mmap_page,
perf_event_type::{PERF_RECORD_LOST, PERF_RECORD_SAMPLE},
},
sys::{perf_event_ioctl, perf_event_open},
sys::{perf_event_ioctl, perf_event_open_bpf},
PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE,
};
@ -87,7 +87,7 @@ impl PerfBuffer {
return Err(PerfBufferError::InvalidPageCount { page_count });
}
let fd = perf_event_open(cpu_id as i32)
let fd = perf_event_open_bpf(cpu_id as i32)
.map_err(|(_, io_error)| PerfBufferError::OpenError { io_error })?
as RawFd;
let size = page_size * page_count;

@ -84,6 +84,7 @@ pub enum ProgramSection {
CgroupSkbIngress { name: String },
CgroupSkbEgress { name: String },
LircMode2 { name: String },
PerfEvent { name: String },
}
impl ProgramSection {
@ -104,6 +105,7 @@ impl ProgramSection {
ProgramSection::CgroupSkbIngress { name } => name,
ProgramSection::CgroupSkbEgress { name } => name,
ProgramSection::LircMode2 { name } => name,
ProgramSection::PerfEvent { name } => name,
}
}
}
@ -144,6 +146,7 @@ impl FromStr for ProgramSection {
"cgroup_skb/ingress" => CgroupSkbIngress { name },
"cgroup_skb/egress" => CgroupSkbEgress { name },
"lirc_mode2" => LircMode2 { name },
"perf_event" => PerfEvent { name },
_ => {
return Err(ParseError::InvalidProgramSection {
section: section.to_owned(),
@ -556,6 +559,7 @@ fn is_program_section(name: &str) -> bool {
"kprobe",
"kretprobe",
"lirc_mode2",
"perf_event",
"sk_msg",
"sk_skb/stream_parser",
"sk_skb/stream_verdict",

@ -40,6 +40,7 @@ mod cgroup_skb;
mod kprobe;
mod lirc_mode2;
mod perf_attach;
mod perf_event;
mod probe;
mod sk_msg;
mod sk_skb;
@ -66,6 +67,7 @@ pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType};
pub use kprobe::{KProbe, KProbeError};
pub use lirc_mode2::LircMode2;
use perf_attach::*;
pub use perf_event::{PerfEvent, PerfEventScope, SamplePolicy};
pub use probe::ProbeKind;
pub use sk_msg::SkMsg;
pub use sk_skb::{SkSkb, SkSkbKind};
@ -181,6 +183,7 @@ pub enum Program {
SchedClassifier(SchedClassifier),
CgroupSkb(CgroupSkb),
LircMode2(LircMode2),
PerfEvent(PerfEvent),
}
impl Program {
@ -213,6 +216,7 @@ impl Program {
Program::SchedClassifier(_) => BPF_PROG_TYPE_SCHED_CLS,
Program::CgroupSkb(_) => BPF_PROG_TYPE_CGROUP_SKB,
Program::LircMode2(_) => BPF_PROG_TYPE_LIRC_MODE2,
Program::PerfEvent(_) => BPF_PROG_TYPE_PERF_EVENT,
}
}
@ -234,6 +238,7 @@ impl Program {
Program::SchedClassifier(p) => &p.data,
Program::CgroupSkb(p) => &p.data,
Program::LircMode2(p) => &p.data,
Program::PerfEvent(p) => &p.data,
}
}
@ -250,6 +255,7 @@ impl Program {
Program::SchedClassifier(p) => &mut p.data,
Program::CgroupSkb(p) => &mut p.data,
Program::LircMode2(p) => &mut p.data,
Program::PerfEvent(p) => &mut p.data,
}
}
}
@ -535,7 +541,8 @@ impl_program_fd!(
SkSkb,
SchedClassifier,
CgroupSkb,
LircMode2
LircMode2,
PerfEvent
);
macro_rules! impl_try_from_program {
@ -577,7 +584,8 @@ impl_try_from_program!(
SockOps,
SchedClassifier,
CgroupSkb,
LircMode2
LircMode2,
PerfEvent
);
/// Provides information about a loaded program, like name, id and statistics

@ -0,0 +1,115 @@
use crate::{generated::bpf_prog_type::BPF_PROG_TYPE_PERF_EVENT, sys::perf_event_open};
pub use crate::generated::perf_type_id;
use super::{load_program, perf_attach, LinkRef, ProgramData, ProgramError};
#[derive(Debug, Clone)]
pub enum SamplePolicy {
Period(u64),
Frequency(u64),
}
#[derive(Debug, Clone)]
#[allow(clippy::enum_variant_names)]
pub enum PerfEventScope {
CallingProcessAnyCpu,
CallingProcessOneCpu { cpu: u32 },
OneProcessAnyCpu { pid: u32 },
OneProcessOneCpu { cpu: u32, pid: u32 },
AllProcessesOneCpu { cpu: u32 },
}
/// A program that can be attached at a perf event.
///
/// TODO: Explain the different types of perf events and how to get a list.
/// Maybe just link to the man page of `perf list`.
/// (But it's not clear to me how to translate those strings into numbers.)
///
/// # Minimum kernel version
///
/// TODO: minimum kernel version?
///
/// # Examples
///
/// ```no_run
/// # #[derive(Debug, thiserror::Error)]
/// # enum Error {
/// # #[error(transparent)]
/// # IO(#[from] std::io::Error),
/// # #[error(transparent)]
/// # Map(#[from] aya::maps::MapError),
/// # #[error(transparent)]
/// # Program(#[from] aya::programs::ProgramError),
/// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError)
/// # }
/// # let mut bpf = aya::Bpf::load(&[], None)?;
/// use std::convert::TryInto;
/// use aya::programs::{PerfEvent, PerfEventScope, SamplePolicy };
/// use aya::util::online_cpus;
///
/// let prog: &mut PerfEvent = bpf.program_mut("observe_cpu_clock")?.try_into()?;
/// prog.load()?;
///
/// for cpu in online_cpus()? {
/// prog.attach(
/// 1, /* PERF_TYPE_SOFTWARE */
/// 0, /* PERF_COUNT_SW_CPU_CLOCK */
/// PerfEventScope::AllProcessesOneCpu { cpu },
/// SamplePolicy::Period(1000000),
/// )?;
/// }
/// # Ok::<(), Error>(())
/// ```
#[derive(Debug)]
#[doc(alias = "BPF_PROG_TYPE_PERF_EVENT")]
pub struct PerfEvent {
pub(crate) data: ProgramData,
}
impl PerfEvent {
/// Loads the program inside the kernel.
///
/// See also [`Program::load`](crate::programs::Program::load).
pub fn load(&mut self) -> Result<(), ProgramError> {
load_program(BPF_PROG_TYPE_PERF_EVENT, &mut self.data)
}
/// Attaches to a given perf event.
pub fn attach(
&mut self,
perf_type: u32, // perf_type_id
config: u64,
scope: PerfEventScope,
sample_policy: SamplePolicy,
) -> Result<LinkRef, ProgramError> {
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::CallingProcessAnyCpu => (0, -1),
PerfEventScope::CallingProcessOneCpu { cpu } => (0, cpu as i32),
PerfEventScope::OneProcessAnyCpu { pid } => (pid as i32, -1),
PerfEventScope::OneProcessOneCpu { cpu, pid } => (pid as i32, 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,
0,
)
.map_err(|(_code, io_error)| ProgramError::SyscallError {
call: "perf_event_open".to_owned(),
io_error,
})? as i32;
perf_attach(&mut self.data, fd)
}
}

@ -12,25 +12,55 @@ use crate::generated::{
use super::{syscall, SysResult, Syscall};
pub(crate) fn perf_event_open(cpu: c_int) -> SysResult {
#[allow(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<u64>,
wakeup: bool,
flags: u32,
) -> SysResult {
let mut attr = unsafe { mem::zeroed::<perf_event_attr>() };
attr.config = PERF_COUNT_SW_BPF_OUTPUT as u64;
attr.config = config;
attr.size = mem::size_of::<perf_event_attr>() as u32;
attr.type_ = PERF_TYPE_SOFTWARE as u32;
attr.type_ = perf_type;
attr.sample_type = PERF_SAMPLE_RAW as u64;
attr.__bindgen_anon_1.sample_period = 1;
attr.__bindgen_anon_2.wakeup_events = 1;
// attr.inherits = if pid > 0 { 1 } else { 0 };
attr.__bindgen_anon_2.wakeup_events = if wakeup { 1 } else { 0 };
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;
}
syscall(Syscall::PerfEventOpen {
attr,
pid: -1,
pid,
cpu,
group: -1,
flags: PERF_FLAG_FD_CLOEXEC,
flags,
})
}
pub(crate) fn perf_event_open_bpf(cpu: c_int) -> SysResult {
perf_event_open(
PERF_TYPE_SOFTWARE as u32,
PERF_COUNT_SW_BPF_OUTPUT as u64,
-1,
cpu,
1,
None,
true,
PERF_FLAG_FD_CLOEXEC,
)
}
pub(crate) fn perf_event_open_probe(
ty: u32,
ret_bit: Option<u32>,

@ -323,3 +323,32 @@ impl TracePoint {
})
}
}
pub struct PerfEvent {
item: ItemFn,
name: String,
}
impl PerfEvent {
pub fn from_syn(mut args: Args, item: ItemFn) -> Result<PerfEvent> {
let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
Ok(PerfEvent { item, name })
}
pub fn expand(&self) -> Result<TokenStream> {
let section_name = format!("perf_event/{}", self.name);
let fn_name = &self.item.sig.ident;
let item = &self.item;
Ok(quote! {
#[no_mangle]
#[link_section = #section_name]
fn #fn_name(ctx: *mut ::core::ffi::c_void) -> u32 {
let _ = #fn_name(::aya_bpf::programs::PerfEventContext::new(ctx));
return 0;
#item
}
})
}
}

@ -1,6 +1,8 @@
mod expand;
use expand::{Args, Map, Probe, ProbeKind, SchedClassifier, SkMsg, SockOps, TracePoint, Xdp};
use expand::{
Args, Map, PerfEvent, Probe, ProbeKind, SchedClassifier, SkMsg, SockOps, TracePoint, Xdp,
};
use proc_macro::TokenStream;
use syn::{parse_macro_input, ItemFn, ItemStatic};
@ -112,3 +114,14 @@ pub fn tracepoint(attrs: TokenStream, item: TokenStream) -> TokenStream {
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
#[proc_macro_attribute]
pub fn perf_event(attrs: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attrs as Args);
let item = parse_macro_input!(item as ItemFn);
PerfEvent::from_syn(args, item)
.and_then(|u| u.expand())
.unwrap_or_else(|err| err.to_compile_error())
.into()
}

@ -1,3 +1,4 @@
pub mod perf_event;
pub mod probe;
pub mod sk_msg;
pub mod sk_skb;
@ -5,6 +6,7 @@ pub mod sock_ops;
pub mod tracepoint;
pub mod xdp;
pub use perf_event::PerfEventContext;
pub use probe::ProbeContext;
pub use sk_msg::SkMsgContext;
pub use sk_skb::SkSkbContext;

@ -0,0 +1,18 @@
use crate::BpfContext;
use core::ffi::c_void;
pub struct PerfEventContext {
ctx: *mut c_void,
}
impl PerfEventContext {
pub fn new(ctx: *mut c_void) -> PerfEventContext {
PerfEventContext { ctx }
}
}
impl BpfContext for PerfEventContext {
fn as_ptr(&self) -> *mut c_void {
self.ctx
}
}
Loading…
Cancel
Save