mprober_lib/cpu/
cpu_info.rs1use std::{
2 collections::BTreeSet,
3 hash::{Hash, Hasher},
4 io::ErrorKind,
5 str::from_utf8_unchecked,
6};
7
8use crate::scanner_rust::{generic_array::typenum::U1024, ScannerAscii, ScannerError};
9
10#[allow(clippy::upper_case_acronyms)]
11#[derive(Default, Debug, Clone)]
12pub struct CPU {
13 pub physical_id: usize,
14 pub model_name: String,
15 pub cpus_mhz: Vec<f64>,
16 pub siblings: usize,
17 pub cpu_cores: usize,
18}
19
20impl Hash for CPU {
21 #[inline]
22 fn hash<H: Hasher>(&self, state: &mut H) {
23 self.physical_id.hash(state)
24 }
25}
26
27impl PartialEq for CPU {
28 #[inline]
29 fn eq(&self, other: &CPU) -> bool {
30 self.physical_id.eq(&other.physical_id)
31 }
32}
33
34pub fn get_cpus() -> Result<Vec<CPU>, ScannerError> {
44 const USEFUL_ITEMS: [&[u8]; 5] =
45 [b"model name", b"cpu MHz", b"physical id", b"siblings", b"cpu cores"];
46 const MODEL_NAME_INDEX: usize = 0;
47 const CPU_MHZ_INDEX: usize = 1;
48 const PHYSICAL_ID_INDEX: usize = 2;
49 const SIBLINGS_INDEX: usize = 3;
50 const CPU_CORES: usize = 4;
51
52 let mut sc: ScannerAscii<_, U1024> = ScannerAscii::scan_path2("/proc/cpuinfo")?;
53
54 let mut cpus = Vec::with_capacity(1);
55 let mut physical_ids: BTreeSet<usize> = BTreeSet::new();
56
57 let mut physical_id = 0;
58 let mut model_name = String::new();
59 let mut cpus_mhz = Vec::with_capacity(1);
60 let mut siblings = 0;
61 let mut cpu_cores = 0;
62
63 'outer: loop {
64 'item: for (i, &item) in USEFUL_ITEMS.iter().enumerate() {
65 let item_len = item.len();
66
67 loop {
68 match sc.next_line_raw()? {
69 Some(line) => {
70 if line.starts_with(item) {
71 let colon_index = line[item_len..]
72 .iter()
73 .copied()
74 .position(|b| b == b':')
75 .ok_or(ErrorKind::InvalidData)?;
76
77 let value = unsafe {
78 from_utf8_unchecked(&line[(item_len + colon_index + 1)..])
79 }
80 .trim();
81
82 match i {
83 MODEL_NAME_INDEX => {
84 if model_name.is_empty() {
85 model_name.push_str(value);
86 }
87 },
88 CPU_MHZ_INDEX => {
89 cpus_mhz.push(value.parse()?);
90 },
91 PHYSICAL_ID_INDEX => {
92 physical_id = value.parse()?;
93
94 if physical_ids.contains(&physical_id) {
95 break 'item;
96 }
97 },
98 SIBLINGS_INDEX => {
99 siblings = value.parse()?;
100 },
101 CPU_CORES => {
102 cpu_cores = value.parse()?;
103
104 break 'item;
105 },
106 _ => unreachable!(),
107 }
108
109 break;
110 }
111 },
112 None => {
113 if i == MODEL_NAME_INDEX {
114 break 'outer;
115 } else {
116 return Err(ErrorKind::UnexpectedEof.into());
117 }
118 },
119 }
120 }
121 }
122
123 if siblings == cpus_mhz.len() {
124 let cpu = CPU {
125 physical_id,
126 model_name,
127 cpus_mhz,
128 siblings,
129 cpu_cores,
130 };
131
132 cpus.push(cpu);
133 physical_ids.insert(physical_id);
134
135 physical_id = 0;
136 model_name = String::new();
137 cpus_mhz = Vec::with_capacity(1);
138 siblings = 0;
139 cpu_cores = 0;
140 }
141
142 loop {
143 let line_length = sc.drop_next_line()?;
144
145 match line_length {
146 Some(line_length) => {
147 if line_length == 0 {
148 break;
149 }
150 },
151 None => {
152 break 'outer;
153 },
154 }
155 }
156 }
157
158 Ok(cpus)
159}