use std::ffi::c_int;
use std::ffi::c_ulong;
use std::fs::File;
use std::os::fd::AsRawFd;
use std::os::fd::FromRawFd;
use libc::pid_t;
use sys::bindings::perf_event_attr;
use crate::check_errno_syscall;
use crate::events;
use crate::events::Event;
use crate::flags::ReadFormat;
use crate::sys;
use crate::Clock;
use crate::Counter;
use crate::Group;
use crate::SampleBranchFlag;
use crate::SampleSkid;
pub struct Builder<'a> {
attrs: perf_event_attr,
who: EventPid<'a>,
cpu: Option<usize>,
group: Option<&'a mut Group>,
}
impl<'a> Default for Builder<'a> {
fn default() -> Builder<'a> {
let mut attrs = perf_event_attr {
size: std::mem::size_of::<perf_event_attr>() as u32,
..perf_event_attr::default()
};
attrs.set_disabled(1);
attrs.set_exclude_kernel(1); attrs.set_exclude_hv(1);
attrs.read_format |= sys::bindings::PERF_FORMAT_TOTAL_TIME_ENABLED as u64
| sys::bindings::PERF_FORMAT_TOTAL_TIME_RUNNING as u64;
let kind = Event::Hardware(events::Hardware::INSTRUCTIONS);
kind.update_attrs(&mut attrs);
Builder {
attrs,
who: EventPid::ThisProcess,
cpu: None,
group: None,
}
}
}
impl<'a> Builder<'a> {
pub fn new() -> Builder<'a> {
Builder::default()
}
pub fn kind<K: Into<Event>>(mut self, kind: K) -> Builder<'a> {
let kind = kind.into();
kind.update_attrs(&mut self.attrs);
self
}
pub fn build(mut self) -> std::io::Result<Counter> {
let cpu = match self.cpu {
Some(cpu) => cpu as c_int,
None => -1,
};
let (pid, flags) = self.who.as_args();
let group_fd = match self.group {
Some(ref mut g) => {
g.max_members += 1;
g.file.as_raw_fd() as c_int
}
None => -1,
};
let file = unsafe {
File::from_raw_fd(check_errno_syscall(|| {
sys::perf_event_open(&mut self.attrs, pid, cpu, group_fd, flags as c_ulong)
})?)
};
let mut id = 0_u64;
check_errno_syscall(|| unsafe { sys::ioctls::ID(file.as_raw_fd(), &mut id) })?;
Ok(Counter::new(file, id))
}
}
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) -> Builder<'a> {
self.who = EventPid::ThisProcess;
self
}
pub fn observe_pid(mut self, pid: pid_t) -> Builder<'a> {
self.who = EventPid::Other(pid);
self
}
pub fn any_pid(mut self) -> Builder<'a> {
self.who = EventPid::Any;
self
}
pub fn observe_cgroup(mut self, cgroup: &'a File) -> Builder<'a> {
self.who = EventPid::CGroup(cgroup);
self
}
pub fn one_cpu(mut self, cpu: usize) -> Builder<'a> {
self.cpu = Some(cpu);
self
}
pub fn any_cpu(mut self) -> Builder<'a> {
self.cpu = None;
self
}
pub fn group(mut self, group: &'a mut Group) -> Builder<'a> {
self.group = Some(group);
self.attrs.set_disabled(0);
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
}
}
#[derive(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),
}
}
}