perf_event_open/sample/record/
mod.rs

1use std::borrow::Borrow;
2use std::fmt;
3use std::fmt::{Debug, Formatter};
4
5use auxiliary::{Aux, AuxOutputHwId};
6use bpf::BpfEvent;
7use cgroup::Cgroup;
8use comm::Comm;
9use ctx::CtxSwitch;
10use itrace::ItraceStart;
11use ksymbol::Ksymbol;
12use lost::{LostRecords, LostSamples};
13use mmap::Mmap;
14use ns::Namespaces;
15use read::Read;
16use sample::Sample;
17use task::{Exit, Fork};
18use text_poke::TextPoke;
19use throttle::{Throttle, Unthrottle};
20
21use super::rb::CowChunk;
22use crate::ffi::{bindings as b, deref_offset, Attr};
23
24pub mod auxiliary;
25pub mod bpf;
26pub mod cgroup;
27pub mod comm;
28pub mod ctx;
29pub mod itrace;
30pub mod ksymbol;
31pub mod lost;
32pub mod mmap;
33pub mod ns;
34pub mod read;
35pub mod sample;
36pub mod task;
37pub mod text_poke;
38pub mod throttle;
39
40// https://github.com/torvalds/linux/blob/v6.13/include/uapi/linux/perf_event.h#L847
41/// Record types.
42///
43/// This type and all record types can be formatted with the `{:-?}`
44/// formatter for compact debugging displays.
45#[derive(Clone)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47pub enum Record {
48    // PERF_RECORD_SAMPLE
49    Sample(Box<Sample>),
50
51    // PERF_RECORD_MMAP | PERF_RECORD_MMAP2
52    Mmap(Box<Mmap>),
53    // PERF_RECORD_READ
54    Read(Box<Read>),
55    // PERF_RECORD_CGROUP
56    /// Since `linux-5.7`: <https://github.com/torvalds/linux/commit/96aaab686505c449e24d76e76507290dcc30e008>
57    Cgroup(Box<Cgroup>),
58    // PERF_RECORD_KSYMBOL
59    /// Since `linux-5.1`: <https://github.com/torvalds/linux/commit/76193a94522f1d4edf2447a536f3f796ce56343b>
60    Ksymbol(Box<Ksymbol>),
61    // PERF_RECORD_TEXT_POKE
62    /// Since `linux-5.9`: <https://github.com/torvalds/linux/commit/e17d43b93e544f5016c0251d2074c15568d5d963>
63    TextPoke(Box<TextPoke>),
64    // PERF_RECORD_BPF_EVENT
65    /// Since `linux-5.1`: <https://github.com/torvalds/linux/commit/6ee52e2a3fe4ea35520720736e6791df1fb67106>
66    BpfEvent(Box<BpfEvent>),
67    // PERF_RECORD_SWITCH | PERF_RECORD_SWITCH_CPU_WIDE
68    /// Since `linux-4.3`: <https://github.com/torvalds/linux/commit/45ac1403f564f411c6a383a2448688ba8dd705a4>
69    CtxSwitch(Box<CtxSwitch>),
70    // PERF_RECORD_NAMESPACES
71    /// Since `linux-4.12`: <https://github.com/torvalds/linux/commit/e422267322cd319e2695a535e47c5b1feeac45eb>
72    Namespaces(Box<Namespaces>),
73    // PERF_RECORD_ITRACE_START
74    /// Since `linux-4.1`: <https://github.com/torvalds/linux/commit/ec0d7729bbaed4b9d2d3fada693278e13a3d1368>
75    ItraceStart(Box<ItraceStart>),
76
77    // PERF_RECORD_AUX
78    /// Since `linux-4.1`: <https://github.com/torvalds/linux/commit/68db7e98c3a6ebe7284b6cf14906ed7c55f3f7f0>
79    Aux(Box<Aux>),
80    // PERF_RECORD_AUX_OUTPUT_HW_ID
81    /// Since `linux-5.16`: <https://github.com/torvalds/linux/commit/8b8ff8cc3b8155c18162e8b1f70e1230db176862>
82    AuxOutputHwId(Box<AuxOutputHwId>),
83
84    // PERF_RECORD_COMM
85    Comm(Box<Comm>),
86    // PERF_RECORD_EXIT
87    Exit(Box<Exit>),
88    // PERF_RECORD_FORK
89    Fork(Box<Fork>),
90
91    // PERF_RECORD_THROTTLE
92    Throttle(Box<Throttle>),
93    // PERF_RECORD_UNTHROTTLE
94    Unthrottle(Box<Unthrottle>),
95
96    // PERF_RECORD_LOST
97    LostRecords(Box<LostRecords>),
98    // PERF_RECORD_LOST_SAMPLES
99    /// Since `linux-4.2`: <https://github.com/torvalds/linux/commit/f38b0dbb491a6987e198aa6b428db8692a6480f8>
100    LostSamples(Box<LostSamples>),
101
102    /// Unknown record type.
103    ///
104    /// This is for compatibility, not ABI.
105    Unknown(Vec<u8>),
106}
107
108impl Debug for Record {
109    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
110        macro_rules! debug {
111            ($($varient:ident,)+) => {
112                match self {
113                    $(Self::$varient(it) => {
114                        if f.alternate() {
115                            return write!(f, "{:#?}", it)
116                        }
117                        if f.sign_minus(){
118                            return write!(f, "{:-?}", it)
119                        }
120                        write!(f, "{:?}", it)
121                    })+
122                }
123            };
124        }
125
126        debug![
127            Sample,
128            Mmap,
129            Read,
130            Cgroup,
131            Ksymbol,
132            TextPoke,
133            BpfEvent,
134            CtxSwitch,
135            Namespaces,
136            ItraceStart,
137            Aux,
138            AuxOutputHwId,
139            Comm,
140            Exit,
141            Fork,
142            Throttle,
143            Unthrottle,
144            LostRecords,
145            LostSamples,
146            Unknown,
147        ]
148    }
149}
150
151/// Task info.
152#[derive(Clone, Debug)]
153#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
154pub struct Task {
155    /// Process ID.
156    pub pid: u32,
157    /// Thread ID.
158    pub tid: u32,
159}
160
161/// Privilege levels.
162#[derive(Clone, Debug)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
164pub enum Priv {
165    // PERF_RECORD_MISC_USER
166    /// User space.
167    User,
168    // PERF_RECORD_MISC_KERNEL
169    /// Kernel space.
170    Kernel,
171    // PERF_RECORD_MISC_HYPERVISOR
172    /// Hypervisor.
173    Hv,
174    // PERF_RECORD_MISC_GUEST_USER
175    /// Guest user space.
176    GuestUser,
177    // PERF_RECORD_MISC_GUEST_KERNEL
178    /// Guest kernel space.
179    GuestKernel,
180    // PERF_RECORD_MISC_CPUMODE_UNKNOWN
181    /// Unknown.
182    Unknown,
183}
184
185impl Priv {
186    pub(crate) fn from_misc(misc: u16) -> Self {
187        // 3 bits
188        match misc as u32 & b::PERF_RECORD_MISC_CPUMODE_MASK {
189            b::PERF_RECORD_MISC_USER => Self::User,
190            b::PERF_RECORD_MISC_KERNEL => Self::Kernel,
191            b::PERF_RECORD_MISC_HYPERVISOR => Self::Hv,
192            b::PERF_RECORD_MISC_GUEST_USER => Self::GuestUser,
193            b::PERF_RECORD_MISC_GUEST_KERNEL => Self::GuestKernel,
194            b::PERF_RECORD_MISC_CPUMODE_UNKNOWN => Self::Unknown,
195            _ => Self::Unknown, // For compatibility, not ABI.
196        }
197    }
198}
199
200/// A collection of IDs used to identify records.
201#[derive(Clone)]
202#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
203pub struct RecordId {
204    /// Event ID.
205    pub id: Option<u64>,
206
207    /// Event stream ID.
208    ///
209    /// This ID is used to distinguish the results of different inherited tasks.
210    ///
211    /// See also [`Inherit`][crate::config::Inherit].
212    pub stream_id: Option<u64>,
213
214    /// CPU number.
215    pub cpu: Option<u32>,
216
217    /// Task info.
218    pub task: Option<Task>,
219
220    /// Timestamp.
221    ///
222    /// See also [`Opts::timer`][crate::config::Opts::timer].
223    pub time: Option<u64>,
224}
225
226debug!(RecordId {
227    {id?},
228    {stream_id?},
229    {cpu?},
230    {task?},
231    {time?},
232});
233
234pub(crate) struct SampleType(pub u64);
235
236impl RecordId {
237    pub(crate) unsafe fn from_ptr(mut ptr: *const u8, sample_type: u64) -> Self {
238        // https://github.com/torvalds/linux/blob/v6.13/include/uapi/linux/perf_event.h#L859
239        // struct sample_id {
240        //     { u32 pid, tid;  } && PERF_SAMPLE_TID
241        //     { u64 time;      } && PERF_SAMPLE_TIME
242        //     { u64 id;        } && PERF_SAMPLE_ID
243        //     { u64 stream_id; } && PERF_SAMPLE_STREAM_ID
244        //     { u32 cpu, res;  } && PERF_SAMPLE_CPU
245        //     { u64 id;        } && PERF_SAMPLE_IDENTIFIER
246        // } && perf_event_attr::sample_id_all
247
248        macro_rules! when {
249            ($flag:ident, $ty:ty) => {
250                (sample_type & (b::$flag as u64) > 0).then(|| deref_offset::<$ty>(&mut ptr))
251            };
252            ($flag:ident, $then:expr) => {
253                (sample_type & (b::$flag as u64) > 0).then(|| $then)
254            };
255        }
256
257        let task = when!(PERF_SAMPLE_TID, {
258            let pid = deref_offset(&mut ptr);
259            let tid = deref_offset(&mut ptr);
260            Task { pid, tid }
261        });
262        let time = when!(PERF_SAMPLE_TIME, u64);
263        let id = when!(PERF_SAMPLE_ID, u64);
264        let stream_id = when!(PERF_SAMPLE_STREAM_ID, u64);
265        let cpu = when!(PERF_SAMPLE_CPU, u32);
266
267        // For `PERF_SAMPLE_IDENTIFIER`:
268        // `PERF_SAMPLE_IDENTIFIER` just duplicates the `PERF_SAMPLE_ID` at a fixed offset,
269        // it's useful to distinguish the sample format if multiple events share the same rb.
270        // Our design does not support redirecting samples to another rb (e.g., `PERF_FLAG_FD_OUTPUT`),
271        // and this is not a parser crate, so `PERF_SAMPLE_IDENTIFIER` is not needed.
272        // See:
273        // https://github.com/torvalds/linux/blob/v6.13/kernel/events/core.c#L7342
274        // https://github.com/torvalds/linux/blob/v6.13/tools/perf/Documentation/perf.data-file-format.txt#L466
275        // https://github.com/torvalds/linux/blob/v6.13/kernel/events/core.c#L12808
276
277        Self {
278            id,
279            stream_id,
280            cpu,
281            task,
282            time,
283        }
284    }
285}
286
287macro_rules! from {
288    ($ty:ident) => {
289        impl From<Box<$ty>> for super::Record {
290            fn from(value: Box<$ty>) -> Self {
291                Self::$ty(value)
292            }
293        }
294    };
295}
296use from;
297
298macro_rules! debug {
299    ($ty:ty { $first_field:tt, $($field:tt,)* }) => {
300        impl std::fmt::Debug for $ty {
301            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302                use crate::sample::record::debug;
303
304                // `{:-?}` formatter, ignores `None` fields.
305                if f.sign_minus() {
306                    let has_none = debug!(is_none, self, $first_field) $(|| debug!(is_none, self, $field))+;
307                    write!(f, "{} {{ ", stringify!($ty))?;
308                    if has_none {
309                        debug!({:-?}, self, f, "{}: {:-?}, ", $first_field);
310                        $(debug!({:-?}, self, f, "{}: {:-?}, ", $field);)+
311                        write!(f, "..")?;
312                    } else {
313                        debug!({:-?}, self, f, "{}: {:-?}", $first_field);
314                        $(debug!({:-?}, self, f, ", {}: {:-?}", $field);)+
315                    }
316                    return write!(f, " }}")
317                }
318
319                // `{:#?}` formatter, same as `{:-?}`, but with indentation.
320                if f.alternate() {
321                    let has_none = debug!(is_none, self, $first_field) $(|| debug!(is_none, self, $field))+;
322                    let mut ds = f.debug_struct(stringify!($ty));
323                    debug!({:#?}, self, ds, $first_field);
324                    $(debug!({:#?}, self, ds, $field);)*
325                    return if has_none {
326                        ds.finish_non_exhaustive()
327                    } else {
328                        ds.finish()
329                    }
330                }
331
332                // `{:?}` formatter, same as `#[derive(Debug)]`.
333                let mut ds = f.debug_struct(stringify!($ty));
334                debug!({:?}, self, ds, $first_field);
335                $(debug!({:?}, self, ds, $field);)*
336                ds.finish()
337            }
338        }
339    };
340    // internal switches
341    (is_none, $self:ident, {$field:ident}) => {
342        false
343    };
344    (is_none, $self:ident, {$field:ident?}) => {
345        $self.$field.is_none()
346    };
347    ({:?}, $self:ident, $ds:ident, {$field:ident$(?)?}) => {
348        $ds.field(stringify!($field), &$self.$field);
349    };
350    ({:#?}, $self:ident, $ds:ident, {$field:ident}) => {
351        $ds.field(stringify!($field), &$self.$field);
352    };
353    ({:#?}, $self:ident, $ds:ident, {$field:ident?}) => {
354        if let Some(it) = &$self.$field {
355            $ds.field(stringify!($field), it);
356        }
357    };
358    ({:-?}, $self:ident, $f:ident, $fmt:literal, {$field:ident}) => {
359        write!($f, $fmt, stringify!($field), &$self.$field)?;
360    };
361    ({:-?}, $self:ident, $f:ident, $fmt:literal, {$field:ident?}) => {
362        if let Some(it) = &$self.$field {
363            write!($f, $fmt, stringify!($field), it)?;
364        }
365    };
366}
367pub(crate) use debug;
368
369/// Unsafe record parser.
370///
371/// Unlike [`Parser`], you need to ensure the safety of parsing record bytes.
372#[derive(Clone, Debug)]
373#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
374pub struct UnsafeParser {
375    pub sample_id_all: bool,
376    pub sample_type: u64,
377    pub read_format: u64,
378    pub user_regs: usize,
379    pub intr_regs: usize,
380    pub branch_sample_type: u64,
381}
382
383impl UnsafeParser {
384    pub(crate) fn from_attr(attr: &Attr) -> Self {
385        Self {
386            sample_id_all: attr.sample_id_all() > 0,
387            sample_type: attr.sample_type,
388            user_regs: attr.sample_regs_user.count_ones() as _,
389            intr_regs: attr.sample_regs_intr.count_ones() as _,
390            branch_sample_type: attr.branch_sample_type,
391            read_format: attr.read_format,
392        }
393    }
394
395    /// Parse record bytes into record type.
396    ///
397    /// # Safety
398    ///
399    /// `bytes` must be created by the same sampler as this parser.
400    ///
401    /// See also [`Parser`].
402    pub unsafe fn parse<T>(&self, bytes: T) -> (Priv, Record)
403    where
404        T: Borrow<[u8]>,
405    {
406        let bytes = bytes.borrow();
407        let ptr = &mut bytes.as_ptr();
408
409        // https://github.com/torvalds/linux/blob/v6.13/include/uapi/linux/perf_event.h#L824
410        // struct perf_event_header {
411        //     u32 type;
412        //     u16 misc;
413        //     u16 size;
414        // };
415
416        let ty: u32 = deref_offset(ptr);
417        let misc: u16 = deref_offset(ptr);
418        let record_priv = Priv::from_misc(misc);
419
420        let ptr = ptr.add(size_of::<u16>()); // skip `size`
421        let sample_id_all = self.sample_id_all.then_some(SampleType(self.sample_type));
422
423        fn from<T>(t: T) -> Record
424        where
425            Box<T>: Into<Record>,
426        {
427            Box::new(t).into()
428        }
429
430        let record = match ty {
431            b::PERF_RECORD_SAMPLE => from(Sample::from_ptr(
432                ptr,
433                misc,
434                self.read_format,
435                self.sample_type,
436                self.user_regs,
437                self.intr_regs,
438                self.branch_sample_type,
439            )),
440            b::PERF_RECORD_MMAP => from(Mmap::from_ptr(ptr, misc, false, sample_id_all)),
441            b::PERF_RECORD_MMAP2 => from(Mmap::from_ptr(ptr, misc, true, sample_id_all)),
442            b::PERF_RECORD_READ => from(Read::from_ptr(ptr, self.read_format, sample_id_all)),
443            #[cfg(feature = "linux-5.7")]
444            b::PERF_RECORD_CGROUP => from(Cgroup::from_ptr(ptr, sample_id_all)),
445            #[cfg(feature = "linux-5.1")]
446            b::PERF_RECORD_KSYMBOL => from(Ksymbol::from_ptr(ptr, sample_id_all)),
447            #[cfg(feature = "linux-5.9")]
448            b::PERF_RECORD_TEXT_POKE => from(TextPoke::from_ptr(ptr, sample_id_all)),
449            #[cfg(feature = "linux-5.1")]
450            b::PERF_RECORD_BPF_EVENT => from(BpfEvent::from_ptr(ptr, sample_id_all)),
451            #[cfg(feature = "linux-4.3")]
452            b::PERF_RECORD_SWITCH => from(CtxSwitch::from_ptr(ptr, false, misc, sample_id_all)),
453            #[cfg(feature = "linux-4.3")]
454            b::PERF_RECORD_SWITCH_CPU_WIDE => {
455                from(CtxSwitch::from_ptr(ptr, true, misc, sample_id_all))
456            }
457            #[cfg(feature = "linux-4.12")]
458            b::PERF_RECORD_NAMESPACES => from(Namespaces::from_ptr(ptr, sample_id_all)),
459            #[cfg(feature = "linux-4.1")]
460            b::PERF_RECORD_ITRACE_START => from(ItraceStart::from_ptr(ptr, sample_id_all)),
461            #[cfg(feature = "linux-4.1")]
462            b::PERF_RECORD_AUX => from(Aux::from_ptr(ptr, sample_id_all)),
463            #[cfg(feature = "linux-5.16")]
464            b::PERF_RECORD_AUX_OUTPUT_HW_ID => from(AuxOutputHwId::from_ptr(ptr, sample_id_all)),
465            b::PERF_RECORD_COMM => from(Comm::from_ptr(ptr, misc, sample_id_all)),
466            b::PERF_RECORD_EXIT => from(Exit::from_ptr(ptr, sample_id_all)),
467            b::PERF_RECORD_FORK => from(Fork::from_ptr(ptr, sample_id_all)),
468            b::PERF_RECORD_THROTTLE => from(Throttle::from_ptr(ptr, sample_id_all)),
469            b::PERF_RECORD_UNTHROTTLE => from(Unthrottle::from_ptr(ptr, sample_id_all)),
470            b::PERF_RECORD_LOST => from(LostRecords::from_ptr(ptr, sample_id_all)),
471            #[cfg(feature = "linux-4.2")]
472            b::PERF_RECORD_LOST_SAMPLES => from(LostSamples::from_ptr(ptr, sample_id_all)),
473            _ => Record::Unknown(bytes.to_vec()), // For compatibility, not ABI.
474        };
475
476        (record_priv, record)
477    }
478}
479
480/// Record parser.
481///
482/// This type can only be accessed within the closure scope of COW record iterators,
483/// parse [`CowChunk`] with this parser is always safe since the closure scope ensures
484/// that the `CowChunk` and the underlying unsafe parser are created from the same
485/// sampler.
486#[derive(Debug)]
487pub struct Parser(pub(in crate::sample) UnsafeParser);
488
489impl Parser {
490    /// Parse [`CowChunk`] into record type.
491    pub fn parse(&self, chunk: CowChunk<'_>) -> (Priv, Record) {
492        let bytes = chunk.as_bytes();
493        unsafe { self.0.parse(bytes) }
494    }
495
496    /// Returns the underlying unsafe record parser.
497    pub fn as_unsafe(&self) -> &UnsafeParser {
498        &self.0
499    }
500}