perf_event_open/count/
stat.rs

1use crate::ffi::{bindings as b, deref_offset};
2use crate::sample::record::debug;
3
4/// Event statistics.
5///
6/// This type can be formatted with the `{:-?}` formatter for compact
7/// debugging displays.
8#[derive(Clone)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct Stat {
11    /// Event count.
12    pub count: u64,
13
14    /// Event ID.
15    pub id: Option<u64>,
16
17    /// The enabled time of the counter.
18    ///
19    /// This can be used to calculate estimated totals if the PMU is overcommitted and multiplexing is happening,
20    /// the raw count can be scaled by `raw / (time_running / time_enabled)` to estimate the totals.
21    pub time_enabled: Option<u64>,
22
23    /// The running time of the counter.
24    ///
25    /// Usually used together with [`time_enabled`][Self::time_enabled].
26    pub time_running: Option<u64>,
27
28    /// The number of lost records.
29    ///
30    /// If the sampler ring-buffer has no more space to hold the new records
31    /// or the ring-buffer output is paused, the records are considered lost.
32    ///
33    /// Since `linux-6.0`: <https://github.com/torvalds/linux/commit/119a784c81270eb88e573174ed2209225d646656>
34    pub lost_records: Option<u64>,
35
36    /// Sibling event statistics.
37    pub siblings: Vec<SiblingStat>,
38}
39
40impl Stat {
41    // https://github.com/torvalds/linux/blob/v6.13/include/uapi/linux/perf_event.h#L344
42    // struct read_format {
43    //     {
44    //         u64 value;
45    //         { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
46    //         { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
47    //         { u64 id;           } && PERF_FORMAT_ID
48    //         { u64 lost;         } && PERF_FORMAT_LOST
49    //     } && !PERF_FORMAT_GROUP
50    //     {
51    //         u64 nr;
52    //         { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
53    //         { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
54    //         {
55    //             u64 value;
56    //             { u64 id;   } && PERF_FORMAT_ID
57    //             { u64 lost; } && PERF_FORMAT_LOST
58    //         } cntr[nr];
59    //     } && PERF_FORMAT_GROUP
60    // };
61    pub(crate) unsafe fn from_ptr_offset(ptr: &mut *const u8, read_format: u64) -> Self {
62        macro_rules! when {
63            ($flag:ident, $ty:ty) => {
64                (read_format & (b::$flag as u64) > 0).then(|| deref_offset::<$ty>(ptr))
65            };
66        }
67
68        if read_format & b::PERF_FORMAT_GROUP as u64 == 0 {
69            let count = deref_offset(ptr);
70            let time_enabled = when!(PERF_FORMAT_TOTAL_TIME_ENABLED, u64);
71            let time_running = when!(PERF_FORMAT_TOTAL_TIME_RUNNING, u64);
72            let id = when!(PERF_FORMAT_ID, u64);
73            #[cfg(feature = "linux-6.0")]
74            let lost_records = when!(PERF_FORMAT_LOST, u64);
75            #[cfg(not(feature = "linux-6.0"))]
76            let lost_records = None;
77
78            Self {
79                count,
80                id,
81                time_enabled,
82                time_running,
83                lost_records,
84                siblings: vec![],
85            }
86        } else {
87            let nr: u64 = deref_offset(ptr);
88            let time_enabled = when!(PERF_FORMAT_TOTAL_TIME_ENABLED, u64);
89            let time_running = when!(PERF_FORMAT_TOTAL_TIME_RUNNING, u64);
90
91            let count = deref_offset(ptr);
92            let id = when!(PERF_FORMAT_ID, u64);
93            #[cfg(feature = "linux-6.0")]
94            let lost_records = when!(PERF_FORMAT_LOST, u64);
95            #[cfg(not(feature = "linux-6.0"))]
96            let lost_records = None;
97
98            let siblings = (1..nr)
99                .map(|_| {
100                    let count = deref_offset(ptr);
101                    let id = when!(PERF_FORMAT_ID, u64);
102                    #[cfg(feature = "linux-6.0")]
103                    let lost_records = when!(PERF_FORMAT_LOST, u64);
104                    #[cfg(not(feature = "linux-6.0"))]
105                    let lost_records = None;
106
107                    SiblingStat {
108                        count,
109                        id,
110                        lost_records,
111                    }
112                })
113                .collect();
114
115            Self {
116                count,
117                id,
118                time_enabled,
119                time_running,
120                lost_records,
121                siblings,
122            }
123        }
124    }
125
126    pub(crate) unsafe fn from_ptr(mut ptr: *const u8, read_format: u64) -> Self {
127        Self::from_ptr_offset(&mut ptr, read_format)
128    }
129
130    pub(crate) fn read_buf_size(group_size: usize, read_format: u64) -> usize {
131        let mut size = size_of::<u64>();
132
133        macro_rules! when {
134            ($flag:ident, $size:expr) => {
135                if read_format & b::$flag as u64 > 0 {
136                    size += $size;
137                }
138            };
139        }
140
141        when!(PERF_FORMAT_TOTAL_TIME_ENABLED, size_of::<u64>());
142        when!(PERF_FORMAT_TOTAL_TIME_RUNNING, size_of::<u64>());
143        when!(PERF_FORMAT_GROUP, group_size * size_of::<u64>());
144        when!(PERF_FORMAT_ID, group_size * size_of::<u64>());
145        #[cfg(feature = "linux-6.0")]
146        when!(PERF_FORMAT_LOST, group_size * size_of::<u64>());
147
148        size
149    }
150}
151
152debug!(Stat {
153    {count},
154    {id?},
155    {time_enabled?},
156    {time_running?},
157    {lost_records?},
158    {siblings},
159});
160
161/// Sibling event statistics.
162#[derive(Clone)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
164pub struct SiblingStat {
165    /// Event count.
166    pub count: u64,
167
168    /// Event ID.
169    pub id: Option<u64>,
170
171    /// The number of lost records.
172    ///
173    /// If the sampler ring-buffer has no more space to hold the new records
174    /// or the ring-buffer output is paused, the records are considered lost.
175    ///
176    /// Since `linux-6.0`: <https://github.com/torvalds/linux/commit/119a784c81270eb88e573174ed2209225d646656>
177    pub lost_records: Option<u64>,
178}
179
180debug!(SiblingStat {
181    {count},
182    {id?},
183    {lost_records?},
184});