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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! Only for reading `CpuCycles` specialization, not a complete package of [perf_event_read](https://www.man7.org/linux/man-pages/man2/perf_event_open.2.html)
//!
//! ⚠ Permission requirements: Make sure the program has root permissions
//!
//! Example:
//! ```ignore
//! use std::{fs, time::{Duration, Instant}};
//! use cpu_cycles_reader::{Cycles, CyclesReader};
//!
//! let reader = CyclesReader::new(&[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
//! reader.enable();
//!
//! let now = Instant::now();
//! let cycles_former = reader.read().unwrap();
//! let cycles_former = cycles_former.get(&7).unwrap(); // get cycles
//!
//! // The cpu has performed some operations, here we record cpu7
//!
//! let dur = Instant::now() - now;
//! let cycles_later = reader.read().unwrap();
//! let cycles_later = cycles_later.get(&7).unwrap(); // get cycles
//!
//! let cycles = *cycles_later - *cycles_former; // Calculate difference
//! // NOTE: There is no need to calculate the difference as a value within 1 second, there is such logic inside Cycles::as_usage() or Cycles::as_diff()
//!
//! let path = format!("/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq", 7);
//! let cur_freq = fs::read_to_string(&path).unwrap();
//! let cur_freq = cur_freq.parse().unwrap();
//! let freq_cycles = Cycles::from_khz(cur_freq);
//!
//! let usage = cycles.as_usage(dur, freq_cycles).unwrap();
//! println!("{:.2}", usage);
//! ```

#![deny(clippy::all, clippy::pedantic)]
#![warn(clippy::nursery, clippy::cargo)]
mod cycles;
pub mod ffi;

use std::{collections::HashMap, ptr, slice};

use ffi::CyclesReaderRaw;
use libc::c_int;

pub use cycles::Cycles;

pub struct CyclesReader {
    raw_ptr: *mut CyclesReaderRaw,
    cpus: Vec<c_int>,
}

impl Drop for CyclesReader {
    fn drop(&mut self) {
        unsafe { ffi::destroyCyclesReader(self.raw_ptr) } // ffi里面已经free,不需要rust调用free
        self.raw_ptr = ptr::null_mut();
    }
}

impl CyclesReader {
    /// Create `CyclesReader`
    ///
    /// # Errors
    /// If there is an error when calling the syscall, it will return an error
    /// ```ignore
    /// use cpu_cycles_reader::CyclesReader;
    ///
    /// let reader = CyclesReader::new(&[0, 1, 2, 3]).unwrap();
    /// ```
    pub fn new(cpus: &[c_int]) -> Result<Self, &'static str> {
        let cpus = cpus.to_vec();
        let cpus_ptr = cpus.as_ptr();

        let raw_ptr = unsafe { ffi::createCyclesReader(cpus_ptr, cpus.len()) };
        if raw_ptr.is_null() {
            return Err("Failed to create CyclesReader");
        }
        Ok(Self { raw_ptr, cpus })
    }

    /// Enable Cycles monitoring
    /// ```ignore
    /// use cpu_cycles_reader::CyclesReader;
    ///
    /// let reader = CyclesReader::new(&[0, 1, 2, 3]).unwrap();
    /// reader.enable();
    /// ```
    pub fn enable(&self) {
        unsafe {
            ffi::enableCyclesReader(self.raw_ptr);
        }
    }

    /// Disable Cycles monitoring
    /// ```ignore
    /// use cpu_cycles_reader::CyclesReader;
    ///
    /// let reader = CyclesReader::new(&[0, 1, 2, 3]).unwrap();
    /// reader.disable();
    /// ```
    pub fn disable(&self) {
        unsafe {
            ffi::disableCyclesReader(self.raw_ptr);
        }
    }

    /// Read the number of Cycles from start to present
    ///
    /// According to the CPU number, it is collected as a [`std::collections::HashMap`], which is convenient for on-demand reading
    ///
    /// # Errors
    /// If there is an error when calling the syscall, it will return an error
    pub fn read(&self) -> Result<HashMap<c_int, Cycles>, &'static str> {
        let raw = unsafe { ffi::readCyclesReader(self.raw_ptr) };

        if raw.is_null() {
            return Err("CyclesReader failed to read");
        }

        let slice = unsafe { slice::from_raw_parts(raw, (*self.raw_ptr).size) };
        let map = self
            .cpus
            .iter()
            .zip(slice)
            .map(|(c, d)| (*c, Cycles::from(*d)))
            .collect(); // copied here

        // Free the array of ffi malloc
        unsafe { libc::free(raw.cast::<libc::c_void>()) }

        Ok(map)
    }
}

unsafe impl Send for CyclesReader {}