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#[derive(Copy, Clone, Debug)]
17pub struct Dynamic {
18 ty: u32,
19 config: u64,
20 config1: u64,
21 config2: u64,
22}
23
24impl Dynamic {
25 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 Bit(u32),
53
54 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#[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 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 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 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 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 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 pub fn params(&self) -> impl Iterator<Item = &str> {
392 self.fields()
393 .filter(|(_, value)| value.is_none())
394 .map(|(key, _)| key)
395 }
396
397 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 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 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 UnknownTarget(String),
508
509 UnknownField(String),
511
512 ValueTooLarge,
514
515 InvalidInteger(std::num::ParseIntError),
517
518 InvalidFloat(std::num::ParseFloatError),
520
521 NonUtf8FieldName,
523
524 MissingColon,
527
528 MissingEvent,
531
532 MissingParam(String),
535}
536
537type Error = DynamicBuilderError;
538
539#[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#[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 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}