mprober_lib/process/
process_time_stat.rs

1use std::{io::ErrorKind, path::Path};
2
3use crate::{
4    process::ProcessStat,
5    scanner_rust::{generic_array::typenum::U96, Scanner, ScannerError},
6};
7
8#[derive(Default, Debug, Clone)]
9pub struct ProcessTimeStat {
10    pub utime: u32,
11    pub stime: u32,
12}
13
14impl ProcessTimeStat {
15    /// Compute CPU utilization in percentage between two `ProcessTimeStat` instances at different time. If it returns `1.0`, means `100%`.
16    ///
17    /// ```rust
18    /// use std::{thread::sleep, time::Duration};
19    ///
20    /// use mprober_lib::{cpu, process};
21    ///
22    /// let pre_average_cpu_stat = cpu::get_average_cpu_stat().unwrap();
23    /// let pre_process_time_stat = process::get_process_time_stat(1).unwrap();
24    ///
25    /// sleep(Duration::from_millis(100));
26    ///
27    /// let average_cpu_stat = cpu::get_average_cpu_stat().unwrap();
28    /// let process_time_stat = process::get_process_time_stat(1).unwrap();
29    ///
30    /// let total_cpu_time_f64 = {
31    ///     let pre_average_cpu_time = pre_average_cpu_stat.compute_cpu_time();
32    ///     let average_cpu_time = average_cpu_stat.compute_cpu_time();
33    ///
34    ///     (average_cpu_time.get_total_time()
35    ///         - pre_average_cpu_time.get_total_time()) as f64
36    /// };
37    ///
38    /// let cpu_percentage = pre_process_time_stat
39    ///     .compute_cpu_utilization_in_percentage(
40    ///         &process_time_stat,
41    ///         total_cpu_time_f64,
42    ///     );
43    ///
44    /// println!("{:.2}%", cpu_percentage * 100.0);
45    /// ```
46    #[inline]
47    pub fn compute_cpu_utilization_in_percentage(
48        &self,
49        process_time_stat_after_this: &ProcessTimeStat,
50        total_cpu_time: f64,
51    ) -> f64 {
52        let d_utime = process_time_stat_after_this.utime - self.utime;
53        let d_stime = process_time_stat_after_this.stime - self.stime;
54        let d_time_f64 = (d_utime + d_stime) as f64;
55
56        if total_cpu_time < 1.0 {
57            0.0
58        } else if d_time_f64 >= total_cpu_time {
59            1.0
60        } else {
61            d_time_f64 / total_cpu_time
62        }
63    }
64}
65
66impl From<ProcessStat> for ProcessTimeStat {
67    #[inline]
68    fn from(process_stat: ProcessStat) -> Self {
69        ProcessTimeStat {
70            utime: process_stat.utime, stime: process_stat.stime
71        }
72    }
73}
74
75/// Get the time stat of a specific process found by ID by reading the `/proc/PID/stat` file.
76///
77/// ```rust
78/// use mprober_lib::process;
79///
80/// let process_time_stat = process::get_process_time_stat(1).unwrap();
81///
82/// println!("{process_time_stat:#?}");
83/// ```
84pub fn get_process_time_stat(pid: u32) -> Result<ProcessTimeStat, ScannerError> {
85    let stat_path = Path::new("/proc").join(pid.to_string()).join("stat");
86
87    let mut sc: Scanner<_, U96> = Scanner::scan_path2(stat_path)?;
88
89    sc.drop_next()?.ok_or(ErrorKind::UnexpectedEof)?;
90
91    loop {
92        let comm = sc.next_raw()?.ok_or(ErrorKind::UnexpectedEof)?;
93
94        if comm.ends_with(b")") {
95            break;
96        }
97    }
98
99    for _ in 0..11 {
100        sc.drop_next()?.ok_or(ErrorKind::UnexpectedEof)?;
101    }
102
103    let utime = sc.next_u32()?.ok_or(ErrorKind::UnexpectedEof)?;
104    let stime = sc.next_u32()?.ok_or(ErrorKind::UnexpectedEof)?;
105
106    let time_stat = ProcessTimeStat {
107        utime,
108        stime,
109    };
110
111    Ok(time_stat)
112}