perf_event/events/
dynamic.rs

1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3use std::{fmt, io};
4
5use perf_event_open_sys::bindings::perf_event_attr;
6
7use crate::events::x86::Msr;
8use crate::events::Event;
9
10used_in_docs!(Msr);
11
12/// An event exposed as a dynamic PMU via the sysfs filesystem.
13///
14/// This type has no operations beyond implementing [`Event`]. Use
15/// [`DynamicBuilder`] to build one.
16#[derive(Copy, Clone, Debug)]
17pub struct Dynamic {
18    ty: u32,
19    config: u64,
20    config1: u64,
21    config2: u64,
22}
23
24impl Dynamic {
25    /// Construct a new dynamic builder for the provided perf PMU.
26    ///
27    /// See [`DynamicBuilder::new`].
28    pub fn builder(pmu: impl AsRef<Path>) -> io::Result<DynamicBuilder> {
29        DynamicBuilder::new(pmu)
30    }
31}
32
33impl Event for Dynamic {
34    fn update_attrs(self, attr: &mut perf_event_attr) {
35        attr.type_ = self.ty;
36        attr.config = self.config;
37        attr.config1 = self.config1;
38        attr.config2 = self.config2;
39    }
40}
41
42#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
43enum FieldDest {
44    Config,
45    Config1,
46    Config2,
47}
48
49#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
50enum FieldBits {
51    /// Field is a single bit
52    Bit(u32),
53
54    /// Field covers a range of bits.
55    ///
56    /// Note that `hi` is inclusive.
57    Range { lo: u32, hi: u32 },
58}
59
60impl FieldBits {
61    pub fn mask(self) -> u64 {
62        match self {
63            Self::Bit(bit) => 1u64 << bit,
64            Self::Range { lo, hi } => (u64::MAX >> (63 - hi)) & (u64::MAX << lo),
65        }
66    }
67
68    pub fn shift(self) -> u32 {
69        match self {
70            Self::Bit(bit) => bit,
71            Self::Range { lo, .. } => lo,
72        }
73    }
74
75    pub fn validate(&self, value: u64) -> bool {
76        let mask = self.mask() >> self.shift();
77        value & !mask == 0
78    }
79}
80
81#[derive(Copy, Clone, Debug)]
82struct Field {
83    dest: FieldDest,
84    bits: FieldBits,
85    value: Option<u64>,
86}
87
88/// Builder for a [`Dynamic`] event.
89///
90/// The linux kernel exposes dynamic perfomance monitoring units (PMUs) using a
91/// special filesystem under `/sys/bus/event_source/devices`. This builder reads
92/// the config files for a specified PMU and allows you to set their values by
93/// using the kernel-specified names instead of having to directly set the right
94/// bits in the config fields.
95///
96/// If you find yourself having to use `Dynamic` and `DynamicBuilder` for an
97/// event please consider submitting a PR to add a native [`Event`] impl for it
98/// to this crate.
99///
100/// # Fields and Parameters
101/// Generally, kernel PMUs expose a few pieces of info:
102/// - A format, which defines fields and indicates which bits they correspond to
103///   within [`perf_event_attr`].
104/// - A set of events, which contain values to assign to the the fields defined
105///   in the format.
106///
107/// Generally, all you need to do is to pick a PMU and an event within that PMU.
108/// Here's how one would configure the [`Msr`] PMU using [`Dynamic`]:
109///
110/// ```
111/// # fn main() -> std::io::Result<()> {
112/// # use std::path::Path;
113/// #
114/// # match () {
115/// #   _ if !cfg!(any(target_arch = "x86_64", target_arch = "i686")) => return Ok(()),
116/// #   _ if !Path::new("/sys/bus/event_source/devices/msr").exists() => return Ok(()),
117/// #   _ => ()
118/// # }
119/// #
120/// use perf_event::events::Dynamic;
121///
122/// let event = Dynamic::builder("msr")?.event("tsc")?.build()?;
123/// # Ok(())
124/// # }
125/// ```
126///
127/// You can use `perf list` to get a list of which kernel PMU events are
128/// supported on the current machine. These will generally be listed in the
129/// format `<pmu>/<event>/`. Here is a sample of some of the events on my
130/// machine:
131///
132/// ```text
133/// msr/aperf/                                         [Kernel PMU event]
134/// msr/cpu_thermal_margin/                            [Kernel PMU event]
135/// msr/mperf/                                         [Kernel PMU event]
136/// msr/pperf/                                         [Kernel PMU event]
137/// msr/smi/                                           [Kernel PMU event]
138/// msr/tsc/                                           [Kernel PMU event]
139/// power/energy-cores/                                [Kernel PMU event]
140/// power/energy-gpu/                                  [Kernel PMU event]
141/// power/energy-pkg/                                  [Kernel PMU event]
142/// power/energy-psys/                                 [Kernel PMU event]
143/// power/energy-ram/                                  [Kernel PMU event]
144/// ```
145///
146/// In most cases, the event file has values for all the fields contained in the
147/// format. However, for some events, the kernel leaves the field as an event
148/// parameter which must be set by the user. In that case, you must set the
149/// value for the field using the [`field`] method.
150///
151/// ```
152/// # fn main() -> std::io::Result<()> {
153/// # let pmu = std::path::Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/data/dyn-pmu"));
154/// use perf_event::events::Dynamic;
155///
156/// let event = Dynamic::builder(&pmu)?
157///     .event("evt1")?
158///     .field("param", 32)?
159///     .build()?;
160/// # Ok(())
161/// # }
162/// ```
163///
164/// In cases where the kernel does not provide the desired event you can still
165/// set the fields directly. Whether this works properly will ultimately depend
166/// on the PMU itself. You will need to set all the fields or else [`build`]
167/// will return an error.
168///
169/// ```
170/// # fn main() -> std::io::Result<()> {
171/// # let pmu = std::path::Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/data/dyn-pmu"));
172/// use perf_event::events::Dynamic;
173///
174/// let event = Dynamic::builder(&pmu)?
175///     .field("event", 77)?
176///     .field("param", 32)?
177///     .field("flag", 1)?
178///     .build()?;
179/// # Ok(())
180/// # }
181/// ```
182///
183/// [`build`]: Self::build
184/// [`field`]: Self::field
185#[derive(Clone)]
186pub struct DynamicBuilder {
187    ty: u32,
188    pmu: PathBuf,
189    event: Option<PathBuf>,
190    fields: HashMap<String, Field>,
191}
192
193impl DynamicBuilder {
194    /// Construct a new dynamic builder for the provided perf PMU.
195    ///
196    /// `pmu` can be either
197    /// - an absolute path to the PMU directory, or,
198    /// - the name of a pmu under `/sys/bus/event_source/devices`.
199    ///
200    /// This method will read the dynamic format configuration out of the PMU
201    /// directory tree and configure the builder to use it.
202    ///
203    /// # Errors
204    /// This method reads the contents of the format directory. Any IO errors
205    /// from this will be returned directly. Any errors from parsing will
206    /// have kind [`io::ErrorKind::Other`] with the inner error being a
207    /// [`DynamicBuilderError`].
208    pub fn new(pmu: impl AsRef<Path>) -> io::Result<Self> {
209        Self::_new(pmu.as_ref())
210    }
211
212    fn _new(pmu: &Path) -> io::Result<Self> {
213        let mut path = Path::new("/sys/bus/event_source/devices").to_path_buf();
214        path.push(pmu);
215
216        path.push("type");
217        let ty: u32 = match std::fs::read_to_string(&path)?.trim().parse() {
218            Ok(ty) => ty,
219            Err(e) => return Err(Error::parse(path, e))?,
220        };
221
222        path.pop();
223        path.push("format");
224
225        let mut fields = HashMap::new();
226
227        for entry in std::fs::read_dir(&path)? {
228            let entry = entry?;
229            let contents = std::fs::read_to_string(entry.path())?;
230
231            let (name, rest) = contents
232                .split_once(':')
233                .ok_or_else(|| Error::missing_colon(entry.path()))?;
234
235            let dest = match name {
236                "config" => FieldDest::Config,
237                "config1" => FieldDest::Config1,
238                "config2" => FieldDest::Config2,
239                _ => return Err(Error::unknown_target(entry.path(), name.to_owned()))?,
240            };
241
242            let bits = if let Some((first, rest)) = rest.split_once('-') {
243                let lo: u32 = first.parse().map_err(|e| Error::parse(entry.path(), e))?;
244                let hi: u32 = rest
245                    .trim_end()
246                    .parse()
247                    .map_err(|e| Error::parse(entry.path(), e))?;
248
249                FieldBits::Range { lo, hi }
250            } else {
251                let index = rest
252                    .trim_end()
253                    .parse()
254                    .map_err(|e| Error::parse(entry.path(), e))?;
255
256                FieldBits::Bit(index)
257            };
258
259            let name = entry.file_name();
260            let name = name
261                .to_str()
262                .ok_or_else(|| Error::invalid_field_name(entry.path()))?;
263
264            fields.insert(
265                name.to_owned(),
266                Field {
267                    dest,
268                    bits,
269                    value: None,
270                },
271            );
272        }
273
274        path.pop();
275
276        Ok(Self {
277            ty,
278            pmu: path,
279            event: None,
280            fields,
281        })
282    }
283
284    /// Initialize the builder for the specified event.
285    ///
286    /// `event` can be either
287    /// - an absolute path to the event config file, or,
288    /// - the name of an event file under
289    ///   `/sys/bus/event_source/devices/<pmu>/events`.
290    ///
291    /// # Errors
292    /// This method reads the contents of the event file. Any IO errors from
293    /// this will be returned directly. Any errors from parsing will have kind
294    /// [`io::ErrorKind::Other`] with the inner error being a
295    /// [`DynamicBuilderError`].
296    pub fn event(&mut self, event: impl AsRef<Path>) -> io::Result<&mut Self> {
297        self._event(event.as_ref())
298    }
299
300    fn _event(&mut self, event: &Path) -> io::Result<&mut Self> {
301        // The event file format is described here:
302        // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-event_source-devices-events
303
304        let mut path = self.pmu.join("events");
305        path.push(event);
306
307        let text = std::fs::read_to_string(&path)?;
308
309        for term in text.split(',') {
310            let term = term.trim_end();
311
312            let (term, value) = match term.split_once('=') {
313                Some((term, "?")) => (term, None),
314                Some((term, value)) => match parse_hex(value) {
315                    Ok(value) => (term, Some(value)),
316                    Err(e) => return Err(Error::parse(path, e))?,
317                },
318                None => (term, Some(1u64)),
319            };
320
321            let field = match self.fields.get_mut(term) {
322                Some(field) => field,
323                None => return Err(Error::unknown_field(path, term.to_owned()))?,
324            };
325
326            field.value = value;
327        }
328
329        self.event = Some(path);
330        Ok(self)
331    }
332
333    /// Set the value of a field.
334    ///
335    /// This overwrites the previous value of the field, if there was one.
336    ///
337    /// # Errors
338    /// An error will be returned if
339    /// - `field` did not exist in the format description for the PMU, or,
340    /// - `value` was larger than the availabel bits for `field`.
341    pub fn field(&mut self, field: &str, value: u64) -> Result<&mut Self, DynamicBuilderError> {
342        let field = self
343            .fields
344            .get_mut(field)
345            .ok_or_else(|| Error::new(ErrorData::UnknownField(field.to_owned())))?;
346
347        if !field.bits.validate(value) {
348            return Err(Error::new(ErrorData::ValueTooLarge));
349        }
350
351        field.value = Some(value);
352        Ok(self)
353    }
354
355    /// Build the [`Dynamic`] event type using the fields in this builder.
356    ///
357    /// This will return an error if any event parameters have not been set to a
358    /// value.
359    pub fn build(&self) -> Result<Dynamic, MissingParameterError> {
360        let mut dynamic = Dynamic {
361            ty: self.ty,
362            config: 0,
363            config1: 0,
364            config2: 0,
365        };
366
367        for (name, field) in self.fields.iter() {
368            let target = match field.dest {
369                FieldDest::Config => &mut dynamic.config,
370                FieldDest::Config1 => &mut dynamic.config1,
371                FieldDest::Config2 => &mut dynamic.config2,
372            };
373
374            let mask = field.bits.mask();
375            let value = match field.value {
376                Some(value) => (value << field.bits.shift()) & mask,
377                None => return Err(MissingParameterError::new(name.to_owned())),
378            };
379
380            *target &= !mask;
381            *target |= value;
382        }
383
384        Ok(dynamic)
385    }
386
387    /// Iterate over all unset parameter fields.
388    ///
389    /// By default, no fields are parameter fields. They only become parameter
390    /// fields when the event file contains `<field>=?`.
391    pub fn params(&self) -> impl Iterator<Item = &str> {
392        self.fields()
393            .filter(|(_, value)| value.is_none())
394            .map(|(key, _)| key)
395    }
396
397    /// Iterate over all fields in this builder.
398    pub fn fields(&self) -> impl Iterator<Item = (&str, Option<u64>)> {
399        self.fields.iter().map(|(key, field)| (&**key, field.value))
400    }
401
402    fn property(&self, suffix: &str) -> io::Result<(PathBuf, Option<String>)> {
403        let event = match &self.event {
404            Some(event) => event,
405            None => return Err(Error::new(ErrorData::MissingEvent))?,
406        };
407
408        let path = event.with_extension(suffix);
409        let content = match std::fs::read_to_string(&path) {
410            Ok(content) => Some(content),
411            Err(e) if e.kind() == io::ErrorKind::NotFound => None,
412            Err(e) => return Err(e),
413        };
414
415        Ok((path, content))
416    }
417
418    /// Read the scale factor of the event.
419    ///
420    /// This is a value to be multiplied by the event count emitted by the
421    /// kernel in order to convert the count to the unit as returned by
422    /// [`unit`](Self::unit). Not all events have a scale.
423    ///
424    /// If [`event`](Self::event) has not been specified then this method will
425    /// always return an error.
426    pub fn scale(&self) -> io::Result<Option<f64>> {
427        let (path, content) = match self.property("scale")? {
428            (_, None) => return Ok(None),
429            (path, Some(content)) => (path, content),
430        };
431
432        let scale: f64 = match content.trim().parse() {
433            Ok(scale) => scale,
434            Err(e) => return Err(Error::parse_float(path, e))?,
435        };
436
437        Ok(Some(scale))
438    }
439
440    /// Read the unit of the event.
441    ///
442    /// This is a string describing the english unit that the event represents
443    /// (once multiplied by [`scale`](Self::scale)). Not all events have a unit.
444    ///
445    /// If [`event`](Self::event) has not been specified then this method will
446    /// always return an error.
447    pub fn unit(&self) -> io::Result<Option<String>> {
448        Ok(match self.property("unit")? {
449            (_, Some(mut content)) => {
450                let trimmed = content.trim_end();
451                content.truncate(trimmed.len());
452                Some(content)
453            }
454            (_, None) => None,
455        })
456    }
457}
458
459fn parse_hex(text: &str) -> Result<u64, std::num::ParseIntError> {
460    let text = text.strip_prefix("0x").unwrap_or(text);
461    u64::from_str_radix(text, 16)
462}
463
464impl fmt::Debug for DynamicBuilder {
465    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
466        let mut dbg = f.debug_struct("DynamicBuilder");
467        dbg.field("type", &self.ty);
468        dbg.field("pmu", &self.pmu);
469
470        if let Some(event) = &self.event {
471            dbg.field("event", &event);
472        }
473
474        dbg.field("fields", &DebugFields(self));
475        dbg.finish()
476    }
477}
478
479struct DebugValue(Option<u64>);
480
481impl fmt::Debug for DebugValue {
482    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
483        match self.0 {
484            Some(value) => value.fmt(f),
485            None => f.write_str("?"),
486        }
487    }
488}
489
490struct DebugFields<'a>(&'a DynamicBuilder);
491
492impl fmt::Debug for DebugFields<'_> {
493    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
494        f.debug_map()
495            .entries(
496                self.0
497                    .fields()
498                    .map(|(name, value)| (name, DebugValue(value))),
499            )
500            .finish()
501    }
502}
503
504#[derive(Clone, Debug)]
505enum ErrorData {
506    /// The destination field for the config was not one of the ones we support.
507    UnknownTarget(String),
508
509    /// The field name was not one of the ones in the PMU format description.
510    UnknownField(String),
511
512    /// The value was larger than the maximum value supported by the field.
513    ValueTooLarge,
514
515    /// We were unable to parse one of the integers within the config file.
516    InvalidInteger(std::num::ParseIntError),
517
518    // Same thing, but a float instead
519    InvalidFloat(std::num::ParseFloatError),
520
521    /// The field name was not valid UTF-8
522    NonUtf8FieldName,
523
524    /// When reading a format file we were unable to find the colon separating
525    /// the field target from its bit specification.
526    MissingColon,
527
528    /// We tried to read an event property but no event has been specified for
529    /// the builder.
530    MissingEvent,
531
532    /// A required parameter was specified in the format but it was not provided
533    /// by the user.
534    MissingParam(String),
535}
536
537type Error = DynamicBuilderError;
538
539/// Error for when the PMU config files are invalid.
540#[derive(Debug)]
541pub struct DynamicBuilderError {
542    data: ErrorData,
543    path: Option<PathBuf>,
544}
545
546impl DynamicBuilderError {
547    pub(self) fn new(data: ErrorData) -> Self {
548        Self { data, path: None }
549    }
550
551    fn missing_colon(path: PathBuf) -> Self {
552        Self {
553            data: ErrorData::MissingColon,
554            path: Some(path),
555        }
556    }
557
558    fn parse(path: PathBuf, e: std::num::ParseIntError) -> Self {
559        Self {
560            data: ErrorData::InvalidInteger(e),
561            path: Some(path),
562        }
563    }
564
565    fn parse_float(path: PathBuf, e: std::num::ParseFloatError) -> Self {
566        Self {
567            data: ErrorData::InvalidFloat(e),
568            path: Some(path),
569        }
570    }
571
572    fn invalid_field_name(path: PathBuf) -> Self {
573        Self {
574            data: ErrorData::NonUtf8FieldName,
575            path: Some(path),
576        }
577    }
578
579    fn unknown_target(path: PathBuf, target: String) -> Self {
580        Self {
581            data: ErrorData::UnknownTarget(target),
582            path: Some(path),
583        }
584    }
585
586    fn unknown_field(path: PathBuf, field: String) -> Self {
587        Self {
588            data: ErrorData::UnknownField(field),
589            path: Some(path),
590        }
591    }
592}
593
594impl fmt::Display for ErrorData {
595    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
596        match self {
597            Self::UnknownTarget(field) => write!(f, "unknown target field `{field}`"),
598            Self::UnknownField(field) => write!(f, "unknown field `{field}`"),
599            Self::ValueTooLarge => write!(f, "value was too large for the field"),
600            Self::InvalidInteger(e) => e.fmt(f),
601            Self::InvalidFloat(e) => e.fmt(f),
602            Self::NonUtf8FieldName => write!(f, "field name contained invalid UTF-8"),
603            Self::MissingColon => write!(f, "expected a ':', found EOF instead"),
604            Self::MissingEvent => write!(f, "need a configured event to read an event property"),
605            Self::MissingParam(param) => write!(f, "missing required parameter `{param}`"),
606        }
607    }
608}
609
610impl fmt::Display for DynamicBuilderError {
611    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
612        match &self.path {
613            Some(path) => write!(f, "invalid PMU config file `{}`", path.display()),
614            None => self.data.fmt(f),
615        }
616    }
617}
618
619impl std::error::Error for ErrorData {
620    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
621        match self {
622            Self::InvalidInteger(e) => Some(e),
623            _ => None,
624        }
625    }
626}
627
628impl std::error::Error for DynamicBuilderError {
629    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
630        match &self.path {
631            Some(_) => Some(&self.data),
632            None => self.data.source(),
633        }
634    }
635}
636
637/// A required PMU parameter did not have a value.
638///
639/// Some dynamic events have event parameters. These need to be set to a value
640/// before a dynamic event can be built. If they are not, then
641/// [`DynamicBuilder::build`] will emit this error.
642#[derive(Clone, Debug)]
643pub struct MissingParameterError {
644    param: String,
645}
646
647impl MissingParameterError {
648    fn new(param: String) -> Self {
649        Self { param }
650    }
651
652    /// The name of the parameter that was missing.
653    pub fn param(&self) -> &str {
654        &self.param
655    }
656}
657
658impl fmt::Display for MissingParameterError {
659    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
660        write!(f, "missing required parameter `{}`", self.param)
661    }
662}
663
664impl std::error::Error for MissingParameterError {}
665
666impl From<DynamicBuilderError> for io::Error {
667    fn from(value: DynamicBuilderError) -> Self {
668        io::Error::new(io::ErrorKind::Other, value)
669    }
670}
671
672impl From<MissingParameterError> for DynamicBuilderError {
673    fn from(value: MissingParameterError) -> Self {
674        Self::new(ErrorData::MissingParam(value.param))
675    }
676}
677
678impl From<MissingParameterError> for io::Error {
679    fn from(value: MissingParameterError) -> Self {
680        io::Error::new(io::ErrorKind::Other, value)
681    }
682}
683
684#[cfg(test)]
685mod tests {
686    use std::iter::FromIterator;
687
688    use super::*;
689
690    fn pmu_enabled(pmu: &str) -> bool {
691        let path = Path::new("/sys/bus/event_source/devices").join(pmu);
692        path.exists()
693    }
694
695    fn test_pmu_dir() -> &'static Path {
696        Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/data"))
697    }
698
699    #[test]
700    fn parse_hex_sanity() {
701        assert_eq!(parse_hex("0xFFFFFFFF"), Ok(0xFFFFFFFF));
702    }
703
704    #[test]
705    #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
706    fn dynamic_msr_event() {
707        if !pmu_enabled("msr") {
708            return;
709        }
710
711        let _event = Dynamic::builder("msr")
712            .unwrap()
713            .event("tsc")
714            .unwrap()
715            .build()
716            .unwrap();
717    }
718
719    #[test]
720    fn dynamic_pmu_evt1() {
721        let pmu = test_pmu_dir().join("dyn-pmu");
722        let mut builder = Dynamic::builder(pmu).unwrap();
723        builder.event("evt1").unwrap();
724
725        let fields: HashMap<_, _> = HashMap::from_iter(builder.fields());
726        assert_eq!(fields["param"], None);
727        assert_eq!(fields["flag"], Some(1));
728        assert_eq!(fields["event"], Some(0xFFFFFFFF));
729
730        assert_eq!(builder.scale().unwrap(), Some(0.5));
731        assert_eq!(builder.unit().unwrap().as_deref(), Some("Frogs"));
732
733        builder
734            .build()
735            .expect_err("param not set, build should have errored");
736        builder.field("param", 0x770).unwrap();
737        let event = builder.build().unwrap();
738
739        assert_eq!(event.ty, 66666);
740        assert_eq!(event.config, 0x770FFFFFFFF);
741        assert_eq!(event.config1, 0x1);
742        assert_eq!(event.config2, 0);
743    }
744
745    #[test]
746    fn dynamic_pmu_evt2() {
747        let pmu = test_pmu_dir().join("dyn-pmu");
748        let mut builder = Dynamic::builder(pmu).unwrap();
749        builder.event("evt2").unwrap();
750
751        let fields: HashMap<_, _> = HashMap::from_iter(builder.fields());
752        assert_eq!(fields["param"], Some(0x45));
753        assert_eq!(fields["flag"], Some(0x00));
754        assert_eq!(fields["event"], Some(0xABCDEF));
755
756        assert_eq!(builder.scale().unwrap(), None);
757        assert_eq!(builder.unit().unwrap().as_deref(), None);
758
759        let event = builder.build().unwrap();
760        assert_eq!(event.ty, 66666);
761        assert_eq!(event.config, 0x4500ABCDEF);
762        assert_eq!(event.config1, 0);
763        assert_eq!(event.config2, 0);
764    }
765
766    #[test]
767    fn dynamic_pmu_empty() {
768        let pmu = test_pmu_dir().join("dyn-pmu");
769        let builder = Dynamic::builder(pmu).unwrap();
770
771        let fields: HashMap<_, _> = HashMap::from_iter(builder.fields());
772        assert_eq!(fields["param"], None);
773        assert_eq!(fields["flag"], None);
774        assert_eq!(fields["event"], None);
775    }
776}