perf_event_data/
config.rs

1use std::fmt;
2
3use bitflags::bitflags;
4use perf_event_open_sys::bindings::{self, perf_event_attr, PERF_SAMPLE_BRANCH_HW_INDEX};
5
6use crate::endian::Endian;
7use crate::{ReadFormat, SampleFlags};
8
9bitflags! {
10    /// The set of flags used by the kernel is a lot smaller than the full
11    /// available 64 bits. We can shrink the size of the `ParseConfig` object
12    /// by cramming them all together into one bitfield.
13    ///
14    /// This will obviously stop working at some point so there is a canary
15    /// test below that will fail once there is only 8 bits of buffer left. At
16    /// that point we will need to split this bitfield apart (likely moving
17    /// sample_type to its own field).
18    #[derive(Copy, Clone, Debug, Default)]
19    struct ConfigFlags : u64 {
20        const READ_FORMAT = ((1u64 << ConfigFlags::READ_FORMAT_WIDTH) - 1);
21        const SAMPLE_TYPE = (u64::MAX << ConfigFlags::READ_FORMAT_WIDTH) & (ConfigFlags::SAMPLE_ID_ALL.bits() - 1);
22
23        const SAMPLE_ID_ALL   = 1 << 46;
24        const BRANCH_HW_INDEX = 1 << 47;
25        const MISC = u64::MAX << ConfigFlags::MISC_OFFSET;
26    }
27}
28
29#[allow(dead_code)]
30impl ConfigFlags {
31    const MISC_WIDTH: u32 = u16::BITS;
32    // Note: we keep one additional bit around within read_format so that we can
33    //       mark that we have bits that are not supported by the version of
34    //       perf_event_open_sys2 that this crate was compiled against.
35    const READ_FORMAT_WIDTH: u32 = (bindings::PERF_FORMAT_MAX - 1).count_ones() + 1;
36    const SAMPLE_TYPE_WIDTH: u32 = (bindings::PERF_SAMPLE_MAX - 1).count_ones();
37
38    const READ_FORMAT_OFFSET: u32 = 0;
39    const SAMPLE_TYPE_OFFSET: u32 = Self::READ_FORMAT_WIDTH;
40    const SAMPLE_ID_ALL_OFFSET: u32 = Self::BRANCH_HW_INDEX_OFFSET - 1;
41    const BRANCH_HW_INDEX_OFFSET: u32 = Self::MISC_OFFSET - 1;
42    const MISC_OFFSET: u32 = u64::BITS - Self::MISC_WIDTH;
43}
44
45impl ConfigFlags {
46    fn new(
47        read_format: ReadFormat,
48        sample_type: SampleFlags,
49        sample_id_all: bool,
50        branch_hw_index: bool,
51        misc: u16,
52    ) -> Self {
53        let mut bits = 0u64;
54        bits |= (sample_id_all as u64) << Self::SAMPLE_ID_ALL_OFFSET;
55        bits |= (branch_hw_index as u64) << Self::BRANCH_HW_INDEX_OFFSET;
56        bits |= (misc as u64) << Self::MISC_OFFSET;
57
58        let mut flags = Self::from_bits_retain(bits);
59        flags.set_read_format(read_format);
60        flags.set_sample_type(sample_type);
61        flags.set_misc(misc);
62
63        flags
64    }
65
66    fn read_format(&self) -> ReadFormat {
67        ReadFormat::from_bits_retain((*self & Self::READ_FORMAT).bits() >> Self::READ_FORMAT_OFFSET)
68    }
69
70    fn sample_type(&self) -> SampleFlags {
71        SampleFlags::from_bits_retain(
72            (*self & Self::SAMPLE_TYPE).bits() >> Self::SAMPLE_TYPE_OFFSET,
73        )
74    }
75
76    fn sample_id_all(&self) -> bool {
77        self.contains(Self::SAMPLE_ID_ALL)
78    }
79
80    fn branch_hw_index(&self) -> bool {
81        self.contains(Self::BRANCH_HW_INDEX)
82    }
83
84    fn misc(&self) -> u16 {
85        ((*self & Self::MISC).bits() >> Self::MISC_OFFSET) as _
86    }
87
88    fn set_misc(&mut self, misc: u16) {
89        *self &= !Self::MISC;
90        *self |= Self::from_bits_retain((misc as u64) << Self::MISC_OFFSET);
91    }
92
93    fn set_sample_type(&mut self, sample_type: SampleFlags) {
94        *self &= !Self::SAMPLE_TYPE;
95        *self |= Self::from_bits_retain(sample_type.bits() << Self::SAMPLE_TYPE_OFFSET)
96            & Self::SAMPLE_TYPE;
97    }
98
99    fn set_read_format(&mut self, read_format: ReadFormat) {
100        *self &= !Self::READ_FORMAT;
101        *self |= Self::from_bits_retain(read_format.bits() << Self::READ_FORMAT_OFFSET)
102            & Self::READ_FORMAT;
103        *self |= Self::from_bits_retain(
104            ((read_format.bits() >> Self::READ_FORMAT_WIDTH != 0) as u64)
105                << (Self::READ_FORMAT_OFFSET + Self::READ_FORMAT_WIDTH - 1),
106        );
107    }
108}
109
110#[derive(Copy, Clone, Debug, Default)]
111#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
112pub(crate) struct RawParseConfig {
113    config_flags: ConfigFlags,
114    sample_regs_user: u64,
115    sample_regs_intr: u64,
116}
117
118/// All the configuration data needed to parse any perf record.
119#[derive(Clone, Default)]
120pub struct ParseConfig<E> {
121    config: RawParseConfig,
122    endian: E,
123}
124
125impl<E> ParseConfig<E> {
126    /// Use this `ParseConfig` with a different `Endian`.
127    pub fn with_endian<E2: Endian>(self, endian: E2) -> ParseConfig<E2> {
128        ParseConfig {
129            endian,
130            config: self.config,
131        }
132    }
133
134    #[allow(dead_code)]
135    /// Used for testing, please open an issue if you need this.
136    pub(crate) fn with_sample_type(mut self, sample_type: SampleFlags) -> Self {
137        self.config.config_flags.set_sample_type(sample_type);
138        self
139    }
140
141    #[allow(dead_code)]
142    /// Used for testing, please open an issue if you need this.
143    pub(crate) fn with_read_format(mut self, read_format: ReadFormat) -> Self {
144        self.config.config_flags.set_read_format(read_format);
145        self
146    }
147
148    pub(crate) fn with_misc(mut self, misc: u16) -> Self {
149        self.config.config_flags.set_misc(misc);
150        self
151    }
152}
153
154impl<E> ParseConfig<E> {
155    /// Flags controlling what fields are returned by the kernel when reading
156    /// from a counter.
157    pub fn read_format(&self) -> ReadFormat {
158        self.config.config_flags.read_format()
159    }
160
161    /// Flags indicating which fields are captured by the kernel when
162    /// collecting a sample.
163    pub fn sample_type(&self) -> SampleFlags {
164        self.config.config_flags.sample_type()
165    }
166
167    /// Bitmask indicating which user-space registers are saved when the kernel
168    /// takes a sample.
169    pub fn regs_user(&self) -> u64 {
170        self.config.sample_regs_user
171    }
172
173    /// Bitmask indicating which registers are saved when the kernel takes a
174    /// sample.
175    ///
176    /// Unlike [`regs_user`](Self::regs_user), the source registers may be in
177    /// either kernel-space or user-space depending on where the perf sampling
178    /// interrupt occurred.
179    pub fn regs_intr(&self) -> u64 {
180        self.config.sample_regs_intr
181    }
182
183    pub(crate) fn sample_id_all(&self) -> bool {
184        self.config.config_flags.sample_id_all()
185    }
186
187    pub(crate) fn branch_hw_index(&self) -> bool {
188        self.config.config_flags.branch_hw_index()
189    }
190
191    pub(crate) fn misc(&self) -> u16 {
192        self.config.config_flags.misc()
193    }
194
195    /// The [`Endian`] for this `ParseConfig`.
196    pub fn endian(&self) -> &E {
197        &self.endian
198    }
199}
200
201impl From<perf_event_attr> for RawParseConfig {
202    fn from(attrs: perf_event_attr) -> Self {
203        Self {
204            config_flags: ConfigFlags::new(
205                ReadFormat::from_bits_retain(attrs.read_format),
206                SampleFlags::from_bits_retain(attrs.sample_type),
207                attrs.sample_id_all() != 0,
208                (attrs.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX as u64) != 0,
209                0,
210            ),
211            sample_regs_user: attrs.sample_regs_user,
212            sample_regs_intr: attrs.sample_regs_intr,
213        }
214    }
215}
216
217impl<E> From<perf_event_attr> for ParseConfig<E>
218where
219    E: Default,
220{
221    fn from(value: perf_event_attr) -> Self {
222        Self {
223            endian: E::default(),
224            config: RawParseConfig::from(value),
225        }
226    }
227}
228
229impl<E: fmt::Debug> fmt::Debug for ParseConfig<E> {
230    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231        f.debug_struct("ParseConfig")
232            .field("read_format", &self.read_format())
233            .field("sample_type", &self.sample_type())
234            .field("sample_id_all", &self.sample_id_all())
235            .field("branch_hw_index", &self.branch_hw_index())
236            .field("misc", &format_args!("0x{:X}", self.misc()))
237            .field("regs_user", &format_args!("0x{:X}", self.regs_user()))
238            .field("regs_intr", &format_args!("0x{:X}", self.regs_intr()))
239            .finish()
240    }
241}
242
243#[cfg(feature = "arbitrary")]
244mod fuzzing {
245    use super::*;
246
247    use arbitrary::{Arbitrary, Result, Unstructured};
248
249    impl<'a, E: Endian + Default> arbitrary::Arbitrary<'a> for ParseConfig<E> {
250        fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
251            Ok(Self {
252                endian: E::default(),
253                config: RawParseConfig::arbitrary(u)?,
254            })
255        }
256    }
257
258    impl<'a> Arbitrary<'a> for ConfigFlags {
259        fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
260            Ok(Self::from_bits_retain(Arbitrary::arbitrary(u)?))
261        }
262    }
263}
264
265#[test]
266fn assert_sufficient_spare_sample_type_bits() {
267    assert!(ConfigFlags::SAMPLE_TYPE.bits().count_ones() >= ConfigFlags::SAMPLE_TYPE_WIDTH + 8)
268}