use std::fmt;
use std::fs::File;
use std::io::{self, ErrorKind};
use std::os::raw::{c_int, c_ulong};
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::sync::Arc;
use libc::pid_t;
use perf_event_data::parse::ParseConfig;
use perf_event_open_sys::bindings;
use crate::events::{Event, EventData};
use crate::sys::bindings::perf_event_attr;
use crate::{
check_errno_syscall, sys, Clock, Counter, Group, ReadFormat, SampleBranchFlag, SampleFlag,
SampleSkid,
};
#[derive(Clone)]
pub struct Builder<'a> {
attrs: perf_event_attr,
who: EventPid<'a>,
cpu: Option<usize>,
event_data: Option<Arc<dyn EventData>>,
}
impl UnwindSafe for Builder<'_> {}
impl RefUnwindSafe for Builder<'_> {}
#[derive(Clone, Debug)]
enum EventPid<'a> {
ThisProcess,
Other(pid_t),
CGroup(&'a File),
Any,
}
impl<'a> EventPid<'a> {
fn as_args(&self) -> (pid_t, u32) {
match self {
EventPid::Any => (-1, 0),
EventPid::ThisProcess => (0, 0),
EventPid::Other(pid) => (*pid, 0),
EventPid::CGroup(file) => (file.as_raw_fd(), sys::bindings::PERF_FLAG_PID_CGROUP),
}
}
}
impl<'a> Builder<'a> {
pub fn new<E: Event>(event: E) -> Self {
let mut attrs = perf_event_attr::default();
let data = event.update_attrs_with_data(&mut attrs);
attrs.size = std::mem::size_of::<perf_event_attr>() as u32;
let mut builder = Self {
attrs,
who: EventPid::ThisProcess,
cpu: None,
event_data: data,
};
builder.enabled(false);
builder.exclude_kernel(true);
builder.exclude_hv(true);
builder.read_format(ReadFormat::TOTAL_TIME_ENABLED | ReadFormat::TOTAL_TIME_RUNNING);
builder
}
pub fn event<E: Event>(&mut self, event: E) -> &mut Self {
self.attrs.type_ = bindings::PERF_TYPE_HARDWARE;
self.attrs.config = 0;
self.attrs.config1 = 0;
self.attrs.config2 = 0;
self.attrs.config3 = 0;
self.event_data = event.update_attrs_with_data(&mut self.attrs);
self
}
pub fn build(&self) -> std::io::Result<Counter> {
Counter::new_internal(self.build_impl(None)?, ParseConfig::from(self.attrs))
}
pub fn build_with_group(&self, mut group: impl AsMut<Counter>) -> io::Result<Counter> {
let group: &mut Counter = group.as_mut();
let file = self.build_impl(Some(group.as_raw_fd()))?;
group.member_count = group
.member_count
.checked_add(1)
.expect("cannot add more than u32::MAX elements to a group");
Counter::new_internal(file, ParseConfig::from(self.attrs))
}
pub fn build_group(&self) -> io::Result<Group> {
let read_format = ReadFormat::from_bits_retain(self.attrs.read_format);
if !read_format.contains(ReadFormat::GROUP) {
return Err(io::Error::new(
ErrorKind::Other,
"groups must be created with the GROUP flag enabled",
));
}
Ok(Group(self.build()?))
}
pub(crate) fn build_impl(&self, group_fd: Option<RawFd>) -> io::Result<File> {
assert!(self.attrs.size <= std::mem::size_of::<perf_event_attr>() as u32);
let cpu = match self.cpu {
Some(cpu) => cpu as c_int,
None => -1,
};
let (pid, flags) = self.who.as_args();
let group_fd = group_fd.unwrap_or(-1);
let flags = flags | sys::bindings::PERF_FLAG_FD_CLOEXEC;
let mut attrs = self.attrs;
let result = check_errno_syscall(|| unsafe {
sys::perf_event_open(&mut attrs, pid, cpu, group_fd, flags as c_ulong)
});
match result {
Ok(fd) => unsafe { Ok(File::from_raw_fd(fd)) },
Err(e) if e.raw_os_error() == Some(libc::E2BIG) => Err(std::io::Error::new(
ErrorKind::Unsupported,
UnsupportedOptionsError::new(attrs.size),
)),
Err(e) => Err(e),
}
}
}
impl<'a> Builder<'a> {
pub fn attrs(&self) -> &perf_event_attr {
&self.attrs
}
pub fn attrs_mut(&mut self) -> &mut perf_event_attr {
&mut self.attrs
}
pub fn observe_self(&mut self) -> &mut Self {
self.who = EventPid::ThisProcess;
self
}
pub fn observe_pid(&mut self, pid: pid_t) -> &mut Self {
self.who = EventPid::Other(pid);
self
}
pub fn any_pid(&mut self) -> &mut Self {
self.who = EventPid::Any;
self
}
pub fn observe_cgroup(&mut self, cgroup: &'a File) -> &mut Self {
self.who = EventPid::CGroup(cgroup);
self
}
pub fn one_cpu(&mut self, cpu: usize) -> &mut Self {
self.cpu = Some(cpu);
self
}
pub fn any_cpu(&mut self) -> &mut Self {
self.cpu = None;
self
}
pub fn sample(&mut self, sample: SampleFlag) -> &mut Self {
self.attrs.sample_type |= sample.bits();
self
}
pub fn read_format(&mut self, mut read_format: ReadFormat) -> &mut Self {
if read_format.contains(ReadFormat::GROUP) {
read_format |= ReadFormat::ID;
}
self.attrs.read_format = read_format.bits();
self
}
}
impl<'a> Builder<'a> {
pub fn enabled(&mut self, enabled: bool) -> &mut Self {
self.attrs.set_disabled((!enabled).into());
self
}
pub fn inherit(&mut self, inherit: bool) -> &mut Self {
self.attrs.set_inherit(inherit.into());
self
}
pub fn pinned(&mut self, pinned: bool) -> &mut Self {
self.attrs.set_pinned(pinned.into());
self
}
pub fn exclusive(&mut self, exclusive: bool) -> &mut Self {
self.attrs.set_exclusive(exclusive.into());
self
}
pub fn exclude_user(&mut self, exclude_user: bool) -> &mut Self {
self.attrs.set_exclude_user(exclude_user.into());
self
}
pub fn exclude_kernel(&mut self, exclude_kernel: bool) -> &mut Self {
self.attrs.set_exclude_kernel(exclude_kernel.into());
self
}
pub fn include_kernel(&mut self) -> &mut Self {
self.exclude_kernel(false)
}
pub fn exclude_hv(&mut self, exclude_hv: bool) -> &mut Self {
self.attrs.set_exclude_hv(exclude_hv.into());
self
}
pub fn include_hv(&mut self) -> &mut Self {
self.exclude_hv(false)
}
pub fn exclude_idle(&mut self, exclude_idle: bool) -> &mut Self {
self.attrs.set_exclude_idle(exclude_idle.into());
self
}
pub fn mmap(&mut self, mmap: bool) -> &mut Self {
self.attrs.set_mmap(mmap.into());
self
}
pub fn comm(&mut self, comm: bool) -> &mut Self {
self.attrs.set_comm(comm.into());
self
}
pub fn sample_period(&mut self, period: u64) -> &mut Self {
self.attrs.set_freq(0);
self.attrs.sample_period = period;
self
}
pub fn sample_frequency(&mut self, frequency: u64) -> &mut Self {
self.attrs.set_freq(1);
self.attrs.sample_freq = frequency;
self
}
pub fn inherit_stat(&mut self, inherit_stat: bool) -> &mut Self {
self.attrs.set_inherit_stat(inherit_stat.into());
self
}
pub fn enable_on_exec(&mut self, enable_on_exec: bool) -> &mut Self {
self.attrs.set_enable_on_exec(enable_on_exec.into());
self
}
pub fn task(&mut self, task: bool) -> &mut Self {
self.attrs.set_task(task.into());
self
}
pub fn wakeup_watermark(&mut self, watermark: usize) -> &mut Self {
self.attrs.set_watermark(1);
self.attrs.wakeup_watermark = watermark as _;
self
}
pub fn wakeup_events(&mut self, events: usize) -> &mut Self {
self.attrs.set_watermark(0);
self.attrs.wakeup_events = events as _;
self
}
pub fn precise_ip(&mut self, skid: SampleSkid) -> &mut Self {
self.attrs.set_precise_ip(skid as _);
self
}
pub fn mmap_data(&mut self, mmap_data: bool) -> &mut Self {
self.attrs.set_mmap_data(mmap_data.into());
self
}
pub fn sample_id_all(&mut self, sample_id_all: bool) -> &mut Self {
self.attrs.set_sample_id_all(sample_id_all.into());
self
}
pub fn exclude_host(&mut self, exclude_host: bool) -> &mut Self {
self.attrs.set_exclude_host(exclude_host.into());
self
}
pub fn exclude_guest(&mut self, exclude_guest: bool) -> &mut Self {
self.attrs.set_exclude_guest(exclude_guest.into());
self
}
pub fn exclude_callchain_kernel(&mut self, exclude_kernel: bool) -> &mut Self {
self.attrs
.set_exclude_callchain_kernel(exclude_kernel.into());
self
}
pub fn exclude_callchain_user(&mut self, exclude_user: bool) -> &mut Self {
self.attrs.set_exclude_callchain_user(exclude_user.into());
self
}
pub fn mmap2(&mut self, mmap2: bool) -> &mut Self {
self.attrs.set_mmap2(mmap2.into());
self
}
pub fn comm_exec(&mut self, comm_exec: bool) -> &mut Self {
self.attrs.set_comm_exec(comm_exec.into());
self
}
pub fn clockid(&mut self, clockid: impl Into<Option<Clock>>) -> &mut Self {
let clockid = clockid.into();
self.attrs.set_use_clockid(clockid.is_some().into());
self.attrs.clockid = clockid.map(Clock::into_raw).unwrap_or(0);
self
}
pub fn context_switch(&mut self, context_switch: bool) -> &mut Self {
self.attrs.set_context_switch(context_switch.into());
self
}
pub fn namespaces(&mut self, namespaces: bool) -> &mut Self {
self.attrs.set_namespaces(namespaces.into());
self
}
pub fn ksymbol(&mut self, ksymbol: bool) -> &mut Self {
self.attrs.set_ksymbol(ksymbol.into());
self
}
pub fn bpf_event(&mut self, bpf_event: bool) -> &mut Self {
self.attrs.set_bpf_event(bpf_event.into());
self
}
pub fn aux_output(&mut self, aux_output: bool) -> &mut Self {
self.attrs.set_aux_output(aux_output.into());
self
}
pub fn cgroup(&mut self, cgroup: bool) -> &mut Self {
self.attrs.set_cgroup(cgroup.into());
self
}
pub fn text_poke(&mut self, text_poke: bool) -> &mut Self {
self.attrs.set_text_poke(text_poke.into());
self
}
pub fn build_id(&mut self, build_id: bool) -> &mut Self {
self.attrs.set_build_id(build_id.into());
self
}
pub fn inherit_thread(&mut self, inherit_thread: bool) -> &mut Self {
self.attrs.set_inherit_thread(inherit_thread.into());
self
}
pub fn remove_on_exec(&mut self, remove_on_exec: bool) -> &mut Self {
self.attrs.set_remove_on_exec(remove_on_exec.into());
self
}
pub fn sigtrap(&mut self, sigtrap: bool) -> &mut Self {
self.attrs.set_sigtrap(sigtrap.into());
self
}
pub fn sig_data(&mut self, sig_data: u64) -> &mut Self {
self.attrs.sig_data = sig_data;
self
}
pub fn branch_sample_type(&mut self, flags: SampleBranchFlag) -> &mut Self {
self.attrs.branch_sample_type = flags.bits();
self
}
pub fn sample_regs_user(&mut self, regs: u64) -> &mut Self {
self.attrs.sample_regs_user = regs;
self
}
pub fn sample_regs_intr(&mut self, regs: u64) -> &mut Self {
self.attrs.sample_regs_user = regs;
self
}
pub fn sample_stack_user(&mut self, stack: u32) -> &mut Self {
self.attrs.sample_stack_user = stack;
self
}
pub fn sample_max_stack(&mut self, max_stack: u16) -> &mut Self {
self.attrs.sample_max_stack = max_stack;
self
}
pub fn aux_watermark(&mut self, watermark: u32) -> &mut Self {
self.attrs.aux_watermark = watermark;
self
}
pub fn aux_sample_size(&mut self, sample_size: u32) -> &mut Self {
self.attrs.aux_sample_size = sample_size;
self
}
}
impl fmt::Debug for Builder<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Builder")
.field("attrs", &self.attrs)
.field("who", &self.who)
.field("cpu", &self.cpu)
.field(
"event_data",
&self.event_data.as_ref().map(|_| "<dyn EventData>"),
)
.finish()
}
}
#[derive(Debug)]
pub struct UnsupportedOptionsError {
expected_size: u32,
}
impl UnsupportedOptionsError {
pub(crate) fn new(expected_size: u32) -> Self {
Self { expected_size }
}
pub fn expected_size(&self) -> usize {
self.expected_size as _
}
}
impl fmt::Display for UnsupportedOptionsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("perf_event_attr contained options not valid for the current kernel")
}
}
impl std::error::Error for UnsupportedOptionsError {}