perf-event-open 0.6.3

Full-featured support for the perf_event_open syscall.
Documentation
use std::fs::File;
use std::os::fd::AsRawFd;

use crate::ffi::bindings as b;

/// Monitor all processes (if [`Proc`] is not set) or all CPUs (if [`Cpu`] is
/// not set).
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct All;

/// Which CPU to monitor.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Cpu(pub u32);

impl Cpu {
    /// Monitor all CPUs.
    ///
    /// This is an alias for [`All`].
    pub const ALL: All = All;
}

/// Which process (thread) to monitor.
///
/// Construct with pid or tid.
///
/// `Proc(0)` indicates the current thread.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Proc(pub u32);

impl Proc {
    /// Monitor all processes.
    ///
    /// This is an alias for [`All`].
    pub const ALL: All = All;

    /// Monitor the current thread.
    pub const CURRENT: Proc = Proc(0);
}

/// Which cgroup to monitor.
///
/// For instance, if the cgroup to monitor is called test, then a file descriptor
/// opened on `/dev/cgroup/test` should be passed (assuming cgroupfs is mounted
/// on `/dev/cgroup`).
///
/// cgroup monitoring is available only for system-wide events
/// and may therefore require extra permissions.
#[derive(Copy, Clone, Debug)]
pub struct Cgroup<'a>(pub &'a File);

/// Event target, the process (or cgroup) and CPU to monitor.
///
/// To create an event target, combine these types in a tuple: [`Proc`] (or
/// [`Cgroup`]), [`Cpu`] and [`All`].
///
/// For example, we want to monitor process with pid 12345 on all CPUs:
/// `(Proc(12345), Cpu::ALL)`. The order of types in the tuples is not sensitive
/// because we impl `Into<Target>` for these swapped tuples, e.g. `(Cpu::ALL,
/// Proc(12345))` has the same semantics as the example above.
///
/// This design limits what we can monitor at compile time. For example,
/// the kernel does not support monitoring any process on all CPUs,
/// or a cgroup on all CPUs.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Target {
    pub(crate) pid: i32,
    pub(crate) cpu: i32,
    pub(crate) flags: u64,
}

macro_rules! into_target {
    ($ty: ty, $destruct: tt, $pid: expr, $cpu: expr, $flags: expr) => {
        impl From<$ty> for Target {
            fn from($destruct: $ty) -> Self {
                Target {
                    pid: $pid as _,
                    cpu: $cpu as _,
                    flags: $flags as _,
                }
            }
        }
    };
}

into_target!((Proc, Cpu), (Proc(pid), Cpu(cpu)), pid, cpu, 0);
into_target!((Cpu, Proc), (Cpu(cpu), Proc(pid)), pid, cpu, 0);

into_target!((Proc, All), (Proc(pid), _), pid, -1, 0);
into_target!((All, Proc), (_, Proc(pid)), pid, -1, 0);

into_target!((Cpu, All), (Cpu(cpu), _), -1, cpu, 0);
into_target!((All, Cpu), (_, Cpu(cpu)), -1, cpu, 0);

into_target!(
    (Cgroup<'_>, Cpu),
    (Cgroup(file), Cpu(cpu)),
    file.as_raw_fd(),
    cpu,
    b::PERF_FLAG_PID_CGROUP
);
into_target!(
    (Cpu, Cgroup<'_>),
    (Cpu(cpu), Cgroup(file)),
    file.as_raw_fd(),
    cpu,
    b::PERF_FLAG_PID_CGROUP
);

// For why `(CgroupFd, Any)` is invalid:
// https://github.com/torvalds/linux/blob/v6.13/kernel/events/core.c#L12791