Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_sys/src/cpu.rs

4.0 KiB, 1 run

created by r1870400018:10578, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! CPU time accounting from `/proc/stat`.
2//!
3//! The kernel reports cumulative tick counters (jiffies) per mode
4//! per CPU. A single instantaneous read tells you how much time
5//! the machine has spent in each mode since boot; two reads a few
6//! seconds apart let you compute the busy percentage over that
7//! interval via [`CpuTimes::busy_fraction`].
8
9use crate::parse::{
10 parse_num,
11 read_to_string,
12 tokens,
13};
14
15use oxedyne_fe2o3_core::prelude::*;
16
17/// Absolute tick counts for the aggregate `cpu` line of
18/// `/proc/stat`. All fields are cumulative since boot.
19#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
20pub struct CpuTimes {
21 /// User-mode time (excluding nice).
22 pub user: u64,
23 /// Niced user-mode time.
24 pub nice: u64,
25 /// Kernel-mode time.
26 pub system: u64,
27 /// Idle time.
28 pub idle: u64,
29 /// Waiting on I/O.
30 pub iowait: u64,
31 /// Servicing hardware interrupts.
32 pub irq: u64,
33 /// Servicing soft interrupts.
34 pub softirq: u64,
35 /// Involuntary wait (stolen by other tenants).
36 pub steal: u64,
37 /// Virtual-guest time not already counted in `user`.
38 pub guest: u64,
39 /// Niced virtual-guest time.
40 pub guest_nice: u64,
41}
42
43impl CpuTimes {
44 /// Read `/proc/stat` and return the aggregate `cpu` line.
45 pub fn sample() -> Outcome<Self> {
46 let content = res!(read_to_string("/proc/stat"));
47 Self::from_stat(&content)
48 }
49
50 /// Parse the aggregate `cpu` line out of a `/proc/stat` body.
51 pub fn from_stat(content: &str) -> Outcome<Self> {
52 for line in content.lines() {
53 if line.starts_with("cpu ") || line.starts_with("cpu\t") {
54 return Self::from_line(line);
55 }
56 }
57 Err(err!(
58 "/proc/stat did not contain an aggregate 'cpu ' line.";
59 Input, Missing, Decode))
60 }
61
62 /// Parse one `cpu...` line. Accepts the aggregate and
63 /// per-cpu forms; the caller selects which one to pass in.
64 pub fn from_line(line: &str) -> Outcome<Self> {
65 let toks = tokens(line);
66 if toks.len() < 5 {
67 return Err(err!(
68 "cpu line has only {} tokens: {:?}",
69 toks.len(), line;
70 Input, Size, Decode));
71 }
72 let get = |i: usize| -> Outcome<u64> {
73 if i >= toks.len() {
74 Ok(0)
75 } else {
76 parse_num::<u64>(toks[i], "cpu")
77 }
78 };
79 Ok(Self {
80 user: res!(get(1)),
81 nice: res!(get(2)),
82 system: res!(get(3)),
83 idle: res!(get(4)),
84 iowait: res!(get(5)),
85 irq: res!(get(6)),
86 softirq: res!(get(7)),
87 steal: res!(get(8)),
88 guest: res!(get(9)),
89 guest_nice: res!(get(10)),
90 })
91 }
92
93 /// Total ticks since boot, across every mode.
94 pub fn total(&self) -> u64 {
95 self.user
96 + self.nice
97 + self.system
98 + self.idle
99 + self.iowait
100 + self.irq
101 + self.softirq
102 + self.steal
103 }
104
105 /// Idle ticks since boot. Counts `idle` and `iowait`; anything
106 /// else is considered busy.
107 pub fn idle_total(&self) -> u64 {
108 self.idle + self.iowait
109 }
110
111 /// Fraction (0..=1) of ticks spent busy over the interval
112 /// between `prev` and `self`. Returns 0 if the interval is
113 /// zero or the clock appears to have moved backwards.
114 pub fn busy_fraction(&self, prev: &Self) -> f64 {
115 let total_now = self.total();
116 let total_prev = prev.total();
117 if total_now <= total_prev {
118 return 0.0;
119 }
120 let idle_now = self.idle_total();
121 let idle_prev = prev.idle_total();
122 let total_delta = (total_now - total_prev) as f64;
123 let idle_delta = idle_now.saturating_sub(idle_prev) as f64;
124 let busy = total_delta - idle_delta;
125 if busy <= 0.0 {
126 0.0
127 } else {
128 busy / total_delta
129 }
130 }
131}