Skip to main content

kbpf_basic/perf/
util.rs

1use alloc::string::String;
2
3use bitflags::bitflags;
4use int_enum::IntEnum;
5
6use crate::{BpfError, BpfResult as Result, KernelAuxiliaryOps, linux_bpf::*};
7
8bitflags! {
9    #[derive(Debug, Clone, Copy)]
10    pub struct PerfEventOpenFlags: u32 {
11        const PERF_FLAG_FD_NO_GROUP = 1;
12        const PERF_FLAG_FD_OUTPUT = 2;
13        const PERF_FLAG_PID_CGROUP = 4;
14        const PERF_FLAG_FD_CLOEXEC = 8;
15    }
16}
17
18/// The `PerfEventIoc` enum is used to define the ioctl commands for perf events.
19///
20/// See https://elixir.bootlin.com/linux/v6.1/source/include/uapi/linux/perf_event.h#L544
21#[repr(u32)]
22#[derive(Debug, Copy, Clone, PartialEq, Eq, IntEnum)]
23pub enum PerfEventIoc {
24    /// Equivalent to [crate::linux_bpf::AYA_PERF_EVENT_IOC_ENABLE].
25    Enable = 9216,
26    /// Equivalent to [crate::linux_bpf::AYA_PERF_EVENT_IOC_DISABLE].
27    Disable = 9217,
28    /// Equivalent to [crate::linux_bpf::AYA_PERF_EVENT_IOC_SET_BPF].
29    SetBpf = 1074013192,
30}
31
32#[allow(non_camel_case_types, missing_docs)]
33#[repr(u32)]
34#[derive(Debug, Copy, Clone, PartialEq, Eq, IntEnum)]
35pub enum PerfTypeId {
36    PERF_TYPE_HARDWARE = 0,
37    PERF_TYPE_SOFTWARE = 1,
38    PERF_TYPE_TRACEPOINT = 2,
39    PERF_TYPE_HW_CACHE = 3,
40    PERF_TYPE_RAW = 4,
41    PERF_TYPE_BREAKPOINT = 5,
42    // TODO: Make sure these are correct.
43    PERF_TYPE_KPROBE = 6,
44    PERF_TYPE_UPROBE = 7,
45}
46
47#[derive(Debug, Clone)]
48#[allow(unused)]
49/// `perf_event_open` syscall arguments.
50pub struct PerfProbeArgs {
51    /// Configuration for the perf probe.
52    pub config: PerfProbeConfig,
53    /// Name of the perf probe.
54    pub name: String,
55    /// Offset for the perf probe.
56    pub offset: u64,
57    /// Size of the perf probe.
58    pub size: u32,
59    /// Type of the perf probe.
60    pub type_: PerfTypeId,
61    /// PID for the perf probe.
62    pub pid: i32,
63    /// CPU for the perf probe.
64    pub cpu: i32,
65    /// Group file descriptor for the perf probe.
66    pub group_fd: i32,
67    /// Flags for the perf probe.
68    pub flags: PerfEventOpenFlags,
69    /// Sample type for the perf probe.
70    pub sample_type: Option<perf_event_sample_format>,
71}
72
73/// Configuration for the perf probe.
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub enum PerfProbeConfig {
76    /// Perf software IDs.
77    PerfSwIds(perf_sw_ids),
78    /// Perf raw config.
79    Raw(u64),
80}
81
82impl PerfProbeArgs {
83    /// Try to create a `PerfProbeArgs` from a `perf_event_attr` structure.
84    pub fn try_from_perf_attr<F: KernelAuxiliaryOps>(
85        attr: &perf_event_attr,
86        pid: i32,
87        cpu: i32,
88        group_fd: i32,
89        flags: u32,
90    ) -> Result<Self> {
91        let ty = PerfTypeId::try_from(attr.type_).map_err(|_| BpfError::EINVAL)?;
92        let config = match ty {
93            PerfTypeId::PERF_TYPE_KPROBE
94            | PerfTypeId::PERF_TYPE_UPROBE
95            | PerfTypeId::PERF_TYPE_TRACEPOINT => PerfProbeConfig::Raw(attr.config),
96            _ => {
97                let sw_id =
98                    perf_sw_ids::try_from(attr.config as u32).map_err(|_| BpfError::EINVAL)?;
99                PerfProbeConfig::PerfSwIds(sw_id)
100            }
101        };
102
103        let name = if ty == PerfTypeId::PERF_TYPE_KPROBE || ty == PerfTypeId::PERF_TYPE_UPROBE {
104            let name_ptr = unsafe { attr.__bindgen_anon_3.config1 } as *const u8;
105
106            F::string_from_user_cstr(name_ptr)?
107        } else {
108            String::new()
109        };
110        let sample_ty = perf_event_sample_format::try_from(attr.sample_type as u32).ok();
111        let args = PerfProbeArgs {
112            config,
113            name,
114            offset: unsafe { attr.__bindgen_anon_4.config2 },
115            size: attr.size,
116            type_: ty,
117            pid,
118            cpu,
119            group_fd,
120            flags: PerfEventOpenFlags::from_bits_truncate(flags),
121            sample_type: sample_ty,
122        };
123        Ok(args)
124    }
125}
126
127/// The event type in our particular use case will be `PERF_RECORD_SAMPLE` or `PERF_RECORD_LOST`.
128/// `PERF_RECORD_SAMPLE` indicating that there is an actual sample after this header.
129/// And `PERF_RECORD_LOST` indicating that there is a record lost header following the perf event header.
130#[repr(C)]
131#[derive(Debug)]
132pub struct LostSamples {
133    pub header: perf_event_header,
134    pub id: u64,
135    pub count: u64,
136}
137
138impl LostSamples {
139    pub fn as_bytes(&self) -> &[u8] {
140        unsafe { core::slice::from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
141    }
142}
143
144#[repr(C)]
145#[derive(Debug)]
146pub struct SampleHeader {
147    pub header: perf_event_header,
148    pub size: u32,
149}
150
151impl SampleHeader {
152    pub fn as_bytes(&self) -> &[u8] {
153        unsafe { core::slice::from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
154    }
155}
156
157#[repr(C)]
158#[derive(Debug)]
159pub struct PerfSample<'a> {
160    pub s_hdr: SampleHeader,
161    pub value: &'a [u8],
162}
163
164impl PerfSample<'_> {
165    pub fn calculate_size(value_size: usize) -> usize {
166        size_of::<SampleHeader>() + value_size
167    }
168}