xref: /DragonOS/kernel/src/perf/util.rs (revision 7b0ef10895108a0de5ff5ef3d2f93f40cf2e33a5)
1 use crate::include::bindings::linux_bpf::{
2     perf_event_attr, perf_event_header, perf_event_sample_format, perf_sw_ids, perf_type_id,
3 };
4 use crate::syscall::user_access::check_and_clone_cstr;
5 use alloc::string::String;
6 use num_traits::FromPrimitive;
7 use system_error::SystemError;
8 
9 bitflags! {
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, FromPrimitive)]
23 pub enum PerfEventIoc {
24     /// Equivalent to [crate::include::bindings::linux_bpf::AYA_PERF_EVENT_IOC_ENABLE].
25     Enable = 9216,
26     /// Equivalent to [crate::include::bindings::linux_bpf::AYA_PERF_EVENT_IOC_DISABLE].
27     Disable = 9217,
28     /// Equivalent to [crate::include::bindings::linux_bpf::AYA_PERF_EVENT_IOC_SET_BPF].
29     SetBpf = 1074013192,
30 }
31 
32 #[derive(Debug, Clone)]
33 #[allow(unused)]
34 /// `perf_event_open` syscall arguments.
35 pub struct PerfProbeArgs {
36     pub config: perf_sw_ids,
37     pub name: String,
38     pub offset: u64,
39     pub size: u32,
40     pub type_: perf_type_id,
41     pub pid: i32,
42     pub cpu: i32,
43     pub group_fd: i32,
44     pub flags: PerfEventOpenFlags,
45     pub sample_type: Option<perf_event_sample_format>,
46 }
47 
48 impl PerfProbeArgs {
49     pub fn try_from(
50         attr: &perf_event_attr,
51         pid: i32,
52         cpu: i32,
53         group_fd: i32,
54         flags: u32,
55     ) -> Result<Self, SystemError> {
56         let ty = perf_type_id::from_u32(attr.type_).ok_or(SystemError::EINVAL)?;
57         let config = perf_sw_ids::from_u32(attr.config as u32).ok_or(SystemError::EINVAL)?;
58         let name = if ty == perf_type_id::PERF_TYPE_MAX {
59             let name_ptr = unsafe { attr.__bindgen_anon_3.config1 } as *const u8;
60             let name = check_and_clone_cstr(name_ptr, None)?;
61             name.into_string().map_err(|_| SystemError::EINVAL)?
62         } else {
63             String::new()
64         };
65         let sample_ty = perf_event_sample_format::from_u32(attr.sample_type as u32);
66         let args = PerfProbeArgs {
67             config,
68             name,
69             offset: unsafe { attr.__bindgen_anon_4.config2 },
70             size: attr.size,
71             type_: ty,
72             pid,
73             cpu,
74             group_fd,
75             flags: PerfEventOpenFlags::from_bits_truncate(flags),
76             sample_type: sample_ty,
77         };
78         Ok(args)
79     }
80 }
81 
82 /// The event type in our particular use case will be `PERF_RECORD_SAMPLE` or `PERF_RECORD_LOST`.
83 /// `PERF_RECORD_SAMPLE` indicating that there is an actual sample after this header.
84 /// And `PERF_RECORD_LOST` indicating that there is a record lost header following the perf event header.
85 #[repr(C)]
86 #[derive(Debug)]
87 pub struct LostSamples {
88     pub header: perf_event_header,
89     pub id: u64,
90     pub count: u64,
91 }
92 
93 impl LostSamples {
94     pub fn as_bytes(&self) -> &[u8] {
95         unsafe { core::slice::from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
96     }
97 }
98 
99 #[repr(C)]
100 #[derive(Debug)]
101 pub struct SampleHeader {
102     pub header: perf_event_header,
103     pub size: u32,
104 }
105 
106 impl SampleHeader {
107     pub fn as_bytes(&self) -> &[u8] {
108         unsafe { core::slice::from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
109     }
110 }
111 
112 #[repr(C)]
113 #[derive(Debug)]
114 pub struct PerfSample<'a> {
115     pub s_hdr: SampleHeader,
116     pub value: &'a [u8],
117 }
118 
119 impl<'a> PerfSample<'a> {
120     pub fn calculate_size(value_size: usize) -> usize {
121         size_of::<SampleHeader>() + value_size
122     }
123 }
124