1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//! Information about available performance counters.

use super::cpuid;
use phf;

use core::fmt::{Write, Result, Error};
use core::str;

pub mod intel;

const MODEL_LEN: usize = 30;

#[derive(Default)]
struct ModelWriter {
    buffer: [u8; MODEL_LEN],
    index: usize,
}

impl ModelWriter {
    fn as_str(&self) -> &str {
        str::from_utf8(&self.buffer[..self.index]).unwrap()
    }
}

impl Write for ModelWriter {
    fn write_str(&mut self, s: &str) -> Result {
        // TODO: There exists probably a more efficient way of doing this:
        for c in s.chars() {
            if self.index >= self.buffer.len() {
                return Err(Error);
            }
            self.buffer[self.index] = c as u8;
            self.index += 1;
        }
        Ok(())
    }
}

// Format must be a string literal
macro_rules! get_counters {
    ($format:expr) => ({
        let cpuid = cpuid::CpuId::new();

        cpuid.get_vendor_info().map_or(None, |vf| {
            cpuid.get_feature_info().map_or(None, |fi| {
                let vendor = vf.as_string();
                let (family, extended_model, model) = (fi.family_id(), fi.extended_model_id(), fi.model_id());

                let mut writer: ModelWriter = Default::default();
                // Should work as long as it fits in MODEL_LEN bytes:
                write!(writer, $format, vendor, family, extended_model, model).unwrap();
                let key = writer.as_str();

                intel::counters::COUNTER_MAP.get(key)
            })
        })
    });
}

/// Return all core performance counters for the running micro-architecture.
pub fn core_counters() -> Option<&'static phf::Map<&'static str, intel::description::IntelPerformanceCounterDescription>> {
    get_counters!("{}-{}-{:X}{:X}-core")
}

/// Return all uncore performance counters for the running micro-architecture.
pub fn uncore_counters() -> Option<&'static phf::Map<&'static str, intel::description::IntelPerformanceCounterDescription>> {
    get_counters!("{}-{}-{:X}{:X}-uncore")
}

#[test]
fn counter_test() {
    // Note: This will silently fail in case the counter is not available.
    core_counters().map(|cc| {
        cc.get("INST_RETIRED.ANY").map(|p| {
            assert!(p.event_name == "INST_RETIRED.ANY");
        });
    });
}