mprober_lib/cpu/
cpu_stat.rs

1use std::{io::ErrorKind, thread::sleep, time::Duration};
2
3use crate::{
4    cpu::CPUTime,
5    scanner_rust::{
6        generic_array::typenum::{U1024, U72},
7        ScannerAscii, ScannerError,
8    },
9};
10
11#[derive(Default, Debug, Clone)]
12pub struct CPUStat {
13    pub user:       u64,
14    pub nice:       u64,
15    pub system:     u64,
16    pub idle:       u64,
17    pub iowait:     u64,
18    pub irq:        u64,
19    pub softirq:    u64,
20    pub steal:      u64,
21    pub guest:      u64,
22    pub guest_nice: u64,
23}
24
25impl CPUStat {
26    /// Add all idle time and non-idle time respectively.
27    ///
28    /// ```rust
29    /// use mprober_lib::cpu;
30    ///
31    /// let average_cpu_stat = cpu::get_average_cpu_stat().unwrap();
32    /// let cpu_time = average_cpu_stat.compute_cpu_time();
33    ///
34    /// println!("{cpu_time:#?}");
35    /// ```
36    #[inline]
37    pub fn compute_cpu_time(&self) -> CPUTime {
38        let idle = self.idle + self.iowait;
39
40        let non_idle = self.user + self.nice + self.system + self.irq + self.softirq + self.steal;
41
42        CPUTime {
43            non_idle,
44            idle,
45        }
46    }
47
48    /// Compute CPU utilization in percentage between two `CPUStat` instances at different time. If it returns `1.0`, means `100%`.
49    ///
50    /// ```rust
51    /// use std::{thread::sleep, time::Duration};
52    ///
53    /// use mprober_lib::cpu;
54    ///
55    /// let pre_average_cpu_stat = cpu::get_average_cpu_stat().unwrap();
56    ///
57    /// sleep(Duration::from_millis(100));
58    ///
59    /// let average_cpu_stat = cpu::get_average_cpu_stat().unwrap();
60    ///
61    /// let cpu_percentage = pre_average_cpu_stat
62    ///     .compute_cpu_utilization_in_percentage(&average_cpu_stat);
63    ///
64    /// println!("{:.2}%", cpu_percentage * 100.0);
65    /// ```
66    #[inline]
67    pub fn compute_cpu_utilization_in_percentage(&self, cpu_stat_after_this: &CPUStat) -> f64 {
68        let pre_cpu_time = self.compute_cpu_time();
69        let cpu_time = cpu_stat_after_this.compute_cpu_time();
70
71        let d_total = cpu_time.get_total_time() - pre_cpu_time.get_total_time();
72        let d_non_idle = cpu_time.non_idle - pre_cpu_time.non_idle;
73
74        d_non_idle as f64 / d_total as f64
75    }
76}
77
78/// Get average CPU stats by reading the `/proc/stat` file.
79///
80/// ```rust
81/// use mprober_lib::cpu;
82///
83/// let average_cpu_stat = cpu::get_average_cpu_stat().unwrap();
84///
85/// println!("{average_cpu_stat:#?}");
86/// ```
87pub fn get_average_cpu_stat() -> Result<CPUStat, ScannerError> {
88    let mut sc: ScannerAscii<_, U72> = ScannerAscii::scan_path2("/proc/stat")?;
89
90    let label = sc.next_raw()?.ok_or(ErrorKind::UnexpectedEof)?;
91
92    if label == b"cpu" {
93        let user = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
94        let nice = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
95        let system = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
96        let idle = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
97        let iowait = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
98        let irq = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
99        let softirq = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
100        let steal = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
101        let guest = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
102        let guest_nice = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
103
104        Ok(CPUStat {
105            user,
106            nice,
107            system,
108            idle,
109            iowait,
110            irq,
111            softirq,
112            steal,
113            guest,
114            guest_nice,
115        })
116    } else {
117        Err(ErrorKind::InvalidData.into())
118    }
119}
120
121/// Get all CPUs' stats with or without the average by reading the `/proc/stat` file.
122///
123/// ```rust
124/// use mprober_lib::cpu;
125///
126/// let all_cpus_stat = cpu::get_all_cpus_stat(false).unwrap();
127///
128/// println!("{all_cpus_stat:#?}");
129/// ```
130pub fn get_all_cpus_stat(with_average: bool) -> Result<Vec<CPUStat>, ScannerError> {
131    let mut sc: ScannerAscii<_, U1024> = ScannerAscii::scan_path2("/proc/stat")?;
132
133    let mut cpus_stat = Vec::with_capacity(1);
134
135    if with_average {
136        let label = sc.next_raw()?.ok_or(ErrorKind::UnexpectedEof)?;
137
138        if label == b"cpu" {
139            let user = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
140            let nice = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
141            let system = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
142            let idle = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
143            let iowait = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
144            let irq = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
145            let softirq = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
146            let steal = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
147            let guest = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
148            let guest_nice = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
149
150            let cpu_stat = CPUStat {
151                user,
152                nice,
153                system,
154                idle,
155                iowait,
156                irq,
157                softirq,
158                steal,
159                guest,
160                guest_nice,
161            };
162
163            cpus_stat.push(cpu_stat);
164        } else {
165            return Err(ErrorKind::InvalidData.into());
166        }
167    } else {
168        sc.drop_next_line()?.ok_or(ErrorKind::UnexpectedEof)?;
169    }
170
171    loop {
172        let label = sc.next_raw()?.ok_or(ErrorKind::UnexpectedEof)?;
173
174        if label.starts_with(b"cpu") {
175            let user = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
176            let nice = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
177            let system = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
178            let idle = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
179            let iowait = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
180            let irq = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
181            let softirq = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
182            let steal = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
183            let guest = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
184            let guest_nice = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
185
186            let cpu_stat = CPUStat {
187                user,
188                nice,
189                system,
190                idle,
191                iowait,
192                irq,
193                softirq,
194                steal,
195                guest,
196                guest_nice,
197            };
198
199            cpus_stat.push(cpu_stat);
200        } else {
201            break;
202        }
203    }
204
205    Ok(cpus_stat)
206}
207
208/// Calculate average CPU utilization in percentage within a specific time interval. It will cause the current thread to sleep. If the number it returns is `1.0`, means `100%`.
209///
210/// ```rust
211/// use std::time::Duration;
212///
213/// use mprober_lib::cpu;
214///
215/// let cpu_percentage = cpu::get_average_cpu_utilization_in_percentage(
216///     Duration::from_millis(100),
217/// )
218/// .unwrap();
219///
220/// println!("{:.2}%", cpu_percentage * 100.0);
221/// ```
222#[inline]
223pub fn get_average_cpu_utilization_in_percentage(interval: Duration) -> Result<f64, ScannerError> {
224    let pre_cpu_stat = get_average_cpu_stat()?;
225
226    sleep(interval);
227
228    let cpu_stat = get_average_cpu_stat()?;
229
230    Ok(pre_cpu_stat.compute_cpu_utilization_in_percentage(&cpu_stat))
231}
232
233/// Calculate all CPU utilization in percentage with or without the average within a specific time interval. It will cause the current thread to sleep. If the number it returns is `1.0`, means `100%`.
234///
235/// ```rust
236/// use std::time::Duration;
237///
238/// use mprober_lib::cpu;
239///
240/// let all_cpu_percentage_without_average: Vec<String> =
241///     cpu::get_all_cpu_utilization_in_percentage(
242///         false,
243///         Duration::from_millis(100),
244///     )
245///     .unwrap()
246///     .into_iter()
247///     .map(|cpu_percentage| format!("{:.2}%", cpu_percentage * 100.0))
248///     .collect();
249///
250/// println!("{:#?}", all_cpu_percentage_without_average);
251/// ```
252#[inline]
253pub fn get_all_cpu_utilization_in_percentage(
254    with_average: bool,
255    interval: Duration,
256) -> Result<Vec<f64>, ScannerError> {
257    let pre_cpus_stat = get_all_cpus_stat(with_average)?;
258
259    sleep(interval);
260
261    let cpus_stat = get_all_cpus_stat(with_average)?;
262
263    let result = pre_cpus_stat
264        .into_iter()
265        .zip(cpus_stat)
266        .map(|(pre_cpus_stat, cpus_stat)| {
267            pre_cpus_stat.compute_cpu_utilization_in_percentage(&cpus_stat)
268        })
269        .collect();
270
271    Ok(result)
272}