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
//! This is only for reading `CpuInstructionNumber` specialization, not a complete package of [perf_event_read](https://www.man7.org/linux/man-pages/man2/perf_event_open.2.html)
//!
//! Example:
//! ```ignore
//! use std::{fs, time::{Duration, Instant}, thread};
//! use cpu_instructions_reader::{InstructionNumber, InstructionNumberReader, InstructionNumberInstant};
//!
//! let reader = InstructionNumberReader::new().unwrap();
//! let record_1 = reader.instant(0).unwrap();
//!
//! thread::sleep(Duration::from_secs(1));
//!
//! let record_2 = reader.instant(0).unwrap();
//! let instructions = record_2 - record_1;
//!
//! println!("{instructions}");
//! ```
#![deny(clippy::all, clippy::pedantic)]
#![warn(clippy::nursery, clippy::cargo)]
#![allow(clippy::missing_panics_doc, clippy::module_name_repetitions)]
#![cfg(any(target_os = "linux", target_os = "android"))]
mod instruction_number;
mod error;
pub mod ffi;
mod instant;

use std::ptr;

use ffi::InstructionNumberReaderRaw;
use libc::{c_int, pid_t};

pub use instruction_number::InstructionNumber;
pub use error::{Error, Result};
pub use instant::InstructionNumberInstant;

#[derive(Debug)]
pub struct InstructionNumberReader {
    raw_ptr: *mut InstructionNumberReaderRaw,
}

impl Drop for InstructionNumberReader {
    fn drop(&mut self) {
        unsafe {
            ffi::disableInstructionNumberReader(self.raw_ptr);
            ffi::destroyInstructionNumberReader(self.raw_ptr);
        }
        self.raw_ptr = ptr::null_mut();
    }
}

impl InstructionNumberReader {
    /// pid: This measures the specified process/thread on any CPU. Set to `None` if measures all processes/threads on any cpu is wanted.
    ///
    /// # Errors
    ///
    /// If there is an error when calling the syscall, it will return an error
    pub fn new(pid: Option<pid_t>) -> Result<Self> {
        let cpus = c_int::try_from(num_cpus::get_physical())?;
        let cpus: Vec<_> = (0..cpus).collect();
        let cpus_ptr = cpus.as_ptr();

        let raw_ptr = unsafe {
            let ptr = ffi::createInstructionNumberReader(cpus_ptr, cpus.len(), pid.unwrap_or(-1));
            ffi::enableInstructionNumberReader(ptr);
            ptr
        };

        if raw_ptr.is_null() {
            return Err(Error::FailedToCreate);
        }

        Ok(Self { raw_ptr })
    }

    /// # Errors
    ///
    /// If there is an error when calling the syscall, it will return an error
    pub fn instant_of_spec(&self, cpu: c_int) -> Result<InstructionNumberInstant> {
        let raw = unsafe { ffi::readInstructionNumberReaderSpec(self.raw_ptr, cpu) };

        if raw == -1 {
            Err(Error::FailedToRead)
        } else {
            let instructions = InstructionNumber::new(raw);
            let instant = InstructionNumberInstant::new(cpu, instructions);
            Ok(instant)
        }
    }

    /// # Errors
    ///
    /// If there is an error when calling the syscall, it will return an error
    pub fn instant_of_all(&self) -> Result<InstructionNumberInstant> {
        let raw = unsafe { ffi::readInstructionNumberReaderAll(self.raw_ptr) };

        if raw == -1 {
            Err(Error::FailedToRead)
        } else {
            let instructions = InstructionNumber::new(raw);
            let instant = InstructionNumberInstant::new(-1, instructions);
            Ok(instant)
        }
    }
}