papi_bindings/
lib.rs

1//! This package provides bindings to the PAPI performance counters
2//! library.
3
4#[allow(non_camel_case_types)]
5#[allow(non_snake_case)]
6#[allow(dead_code)]
7#[allow(non_upper_case_globals)]
8#[allow(deref_nullptr)]
9mod bindings;
10pub mod counter;
11pub mod events_set;
12
13use std::os::raw::{c_int, c_ulong};
14use std::sync::atomic::{AtomicU64, Ordering};
15
16use crate::bindings::*;
17
18const fn papi_version_number(maj: u32, min: u32, rev: u32, inc: u32) -> u32 {
19    (maj << 24) | (min << 16) | (rev << 8) | inc
20}
21
22const PAPI_VERSION: u32 = papi_version_number(6, 0, 0, 1);
23const PAPI_VER_CURRENT: c_int = (PAPI_VERSION & 0xffff0000) as c_int;
24
25#[link(name = "papi")]
26extern "C" {}
27
28#[derive(Debug)]
29#[allow(dead_code)]
30pub struct PapiError {
31    code: i32,
32}
33
34pub(crate) fn check_error(code: i32) -> Result<(), PapiError> {
35    if code == (PAPI_OK as i32) {
36        Ok(())
37    } else {
38        Err(PapiError { code })
39    }
40}
41
42static THREAD_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
43
44thread_local! {
45    static THREAD_INDEX: u64 = THREAD_ID_COUNTER.fetch_add(1, Ordering::Relaxed);
46}
47
48extern "C" fn get_thread_id() -> c_ulong {
49    THREAD_INDEX.with(|id| *id as c_ulong)
50}
51
52pub fn initialize(multithread: bool) -> Result<(), PapiError> {
53    unsafe {
54        let version = PAPI_library_init(PAPI_VER_CURRENT);
55        if version != PAPI_VER_CURRENT {
56            return Err(PapiError { code: version });
57        }
58
59        if multithread {
60            check_error(PAPI_thread_init(Some(get_thread_id)))?;
61        }
62    }
63
64    Ok(())
65}
66
67pub fn is_initialized() -> bool {
68    unsafe { check_error(PAPI_is_initialized()).is_ok() }
69}
70
71// The only reasonable action for counters_in_use is to
72// retry. Otherwise, you might as well just fail yourself.
73#[derive(PartialEq, Eq)]
74pub enum Action {
75    Retry,
76}
77
78#[cfg(test)]
79mod tests {
80    use crate::counter::Counter;
81    use crate::events_set::EventsSet;
82
83    #[test]
84    fn test_fib() {
85        crate::initialize(true).unwrap();
86
87        let counters = vec![
88            Counter::from_name("ix86arch::INSTRUCTION_RETIRED").unwrap(),
89            Counter::from_name("ix86arch::MISPREDICTED_BRANCH_RETIRED").unwrap(),
90        ];
91
92        let mut event_set = EventsSet::new(&counters).unwrap();
93        let mut second_set = event_set.try_clone().unwrap();
94
95        for fv in 1..45 {
96            event_set.start().unwrap();
97            second_set.start().unwrap();
98            let x = fib(fv);
99            second_set.stop().unwrap();
100            let counters = event_set.stop().unwrap();
101            println!(
102                "Computed fib({}) = {} in {} instructions [mispredicted: {}].",
103                fv, x, counters[0], counters[1]
104            );
105        }
106    }
107
108    fn fib(n: isize) -> isize {
109        if n < 2 {
110            1
111        } else {
112            fib(n - 1) + fib(n - 2)
113        }
114    }
115}