1use anyhow::Result;
36use serde::Serialize;
37use std::fs::read_to_string;
38
39use crate::traits::{ToJson, ToPlainText, print_opt_val};
40use crate::utils::Size;
41
42#[derive(Debug, Serialize, Clone)]
44pub struct Processors {
45 pub entries: Vec<CPU>,
47}
48
49impl Processors {
50 pub fn new() -> Result<Self> {
51 Ok(Self {
52 entries: read_info()?,
53 })
54 }
55}
56
57impl ToJson for Processors {}
58impl ToPlainText for Processors {
59 fn to_plain(&self) -> String {
60 let mut s = format!("Information about processors");
61 for proc in &self.entries {
62 s += &proc.to_plain();
63 }
64 s
65 }
66}
67
68#[derive(Debug, Serialize, Default, Clone)]
70pub struct CPU {
71 pub processor: Option<usize>,
73
74 pub vendor_id: Option<String>,
79
80 pub cpu_family: Option<u32>,
82
83 pub model: Option<u32>,
85
86 pub model_name: Option<String>,
88
89 pub stepping: Option<u32>,
91
92 pub microcode: Option<String>,
94
95 pub cpu_mhz: Option<f32>,
97
98 pub cache_size: Option<Size>,
100
101 pub physical_id: Option<u32>,
103
104 pub siblings: Option<u32>,
106
107 pub core_id: Option<u32>,
109
110 pub cpu_cores: Option<u32>,
112
113 pub apicid: Option<u32>,
115
116 pub initial_apicid: Option<u32>,
118
119 pub fpu: Option<bool>,
121
122 pub fpu_exception: Option<bool>,
123 pub cpuid_level: Option<u32>,
124 pub wp: Option<bool>,
125 pub flags: Option<Vec<String>>,
126 pub bugs: Option<Vec<String>>,
127 pub bogomips: Option<f64>,
128 pub clflush_size: Option<u32>,
129 pub cache_alignment: Option<u32>,
130 pub address_sizes: Option<String>,
131 pub power_management: Option<String>,
132
133 pub cpu_implementer: Option<String>,
137 pub cpu_architecture: Option<u8>,
138 pub cpu_variant: Option<String>,
139 pub cpu_part: Option<String>,
140 pub cpu_revision: Option<u32>,
141
142 pub cpu: Option<String>,
146 pub clock: Option<f32>,
147 pub revision: Option<String>,
148 pub timebase: Option<usize>,
149 pub platform: Option<String>,
150 pub machine: Option<String>,
151 pub model_ppc: Option<String>,
152}
153
154impl ToJson for CPU {}
155
156#[cfg(target_arch = "x86_64")]
157impl ToPlainText for CPU {
158 fn to_plain(&self) -> String {
159 let mut s = match self.processor { Some(proc) => format!("\nProcessor #{proc}\n"),
160 None => format!("\nProcessor #unknown\n"),
161 };
162 s += "\tArchitecture: x86_64\n";
163 s += &print_opt_val("Vendor ID", &self.vendor_id);
164 s += &print_opt_val("CPU Family", &self.cpu_family);
165 s += &print_opt_val("CPU Model ID", &self.model);
166 s += &print_opt_val("CPU Model Name", &self.model_name);
167 s += &print_opt_val("Stepping", &self.stepping);
168 s += &print_opt_val("Microcode", &self.microcode);
169 s += &print_opt_val("Current frequency", &self.cpu_mhz);
170 s += &print_opt_val("L3 Cache Size", &self.cache_size);
171 s += &print_opt_val("Physical ID of CPU Core", &self.physical_id);
172 s += &print_opt_val("Siblings", &self.siblings);
173 s += &print_opt_val("Core ID", &self.core_id);
174 s += &print_opt_val("CPU cores", &self.cpu_cores);
175 s += &print_opt_val("APIC ID", &self.apicid);
176 s += &print_opt_val("Initial APIC ID", &self.initial_apicid);
177 s += &print_opt_val("FPU", &self.fpu);
178 s += &print_opt_val("FPU Exception", &self.fpu_exception);
179 s += &print_opt_val("CPUID Level", &self.cpuid_level);
180 s += &print_opt_val("WP", &self.wp);
181 s += &print_opt_val("Bogo MIPS", &self.bogomips);
182 s += &print_opt_val("Clflush Size", &self.clflush_size);
183 s += &print_opt_val("Cache alignment", &self.cache_alignment);
184 s += &print_opt_val("Address sizes", &self.address_sizes);
185 s += &print_opt_val("Power management", &self.power_management);
186
187 s
188 }
189}
190
191#[cfg(target_arch = "aarch64")]
192impl ToPlainText for CPU {
193 fn convert(&self) -> String {
194 let mut s = match self.processor {
195 Some(proc) => format!("Processor #{proc}\n"),
196 None => format!("Processor #unknown\n"),
197 };
198 s += &print_opt_val("CPU Implementer", &self.cpu_implementer);
199 s += &print_opt_val("CPU Architecture", &self.cpu_architecture);
200 s += &print_opt_val("CPU Variant", &self.cpu_variant);
201 s += &print_opt_val("CPU Part", &self.cpu_part);
202 s += &print_opt_val("CPU Revision", &self.cpu_revision);
203
204 s
205 }
206}
207
208fn read_info() -> Result<Vec<CPU>> {
209 let blocks = read_to_string("/proc/cpuinfo")?;
210 let blocks = blocks
211 .split("\n\n") .collect::<Vec<_>>();
213 let mut processors = Vec::with_capacity(blocks.len());
214
215 for block in blocks {
216 if block.trim().is_empty() {
217 continue;
218 }
219 let mut cpu = CPU::default();
220 for line in block.lines() {
221 parse_cpuinfo(&mut cpu, line);
222 }
223 processors.push(cpu);
224 }
225 Ok(processors)
226}
227
228fn get_parts(s: &str) -> impl Iterator<Item = &str> {
229 s.splitn(2, ':').map(|item| item.trim())
230}
231
232#[cfg(target_arch = "x86_64")]
233fn parse_cpuinfo(cpu: &mut CPU, parts: &str) {
234 let mut parts = get_parts(parts);
235 match (parts.next(), parts.next()) {
236 (Some(key), Some(val)) => match key {
237 "processor" => cpu.processor = val.parse().ok(),
238 "vendor_id" => cpu.vendor_id = Some(val.to_string()),
239 "cpu family" => cpu.cpu_family = val.parse().ok(),
240 "model" => cpu.model = val.parse().ok(),
241 "model name" => cpu.model_name = Some(val.to_string()),
242 "stepping" => cpu.stepping = val.parse().ok(),
243 "microcode" => cpu.microcode = Some(val.to_string()),
244 "cpu MHz" => cpu.cpu_mhz = val.parse().ok(),
245 "cache size" => cpu.cache_size = Size::try_from(val).ok(),
246 "physical id" => cpu.physical_id = val.parse().ok(),
247 "siblings" => cpu.siblings = val.parse().ok(),
248 "core id" => cpu.core_id = val.parse().ok(),
249 "cpu cores" => cpu.cpu_cores = val.parse().ok(),
250 "apicid" => cpu.apicid = val.parse().ok(),
251 "initial apicid" => cpu.initial_apicid = val.parse().ok(),
252 "fpu" => cpu.fpu = Some(get_bool(val)),
253 "fpu_exception" => cpu.fpu_exception = Some(get_bool(val)),
254 "cpuid level" => cpu.cpuid_level = val.parse().ok(),
255 "wp" => cpu.wp = Some(get_bool(val)),
256 "flags" | "Features" => {
257 cpu.flags = Some(val.split_whitespace().map(String::from).collect())
258 }
259 "bugs" => cpu.bugs = Some(val.split_whitespace().map(String::from).collect()),
260 "bogomips" | "BogoMIPS" => cpu.bogomips = val.parse().ok(),
261 "clflush size" => cpu.clflush_size = val.parse().ok(),
262 "cache_alignment" => cpu.cache_alignment = val.parse().ok(),
263 "address sizes" => cpu.address_sizes = Some(val.to_string()),
264 "power management" => cpu.power_management = Some(val.to_string()),
265 _ => {} },
267 _ => {}
268 }
269}
270
271#[cfg(target_arch = "aarch64")]
272fn parse_cpuinfo(cpu: &mut CPU, parts: &str) {
273 let mut parts = get_parts(parts);
274 match (parts.next(), parts.next()) {
275 (Some(key), Some(val)) => match key {
276 "CPU implementer" => cpu.cpu_implementer = Some(val.to_string()),
278 "CPU architecture" => cpu.cpu_architecture = val.parse().ok(),
279 "CPU variant" => cpu.cpu_variant = Some(val.to_string()),
280 "CPU part" => cpu.cpu_part = Some(val.to_string()),
281 "CPU revision" => cpu.cpu_revision = val.parse().ok(),
282 _ => {} },
284 _ => {}
285 }
286}
287
288fn get_bool(s: &str) -> bool {
289 match s {
290 "yes" | "ok" => true,
291 _ => false,
292 }
293}