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)]
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)]
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 {
160 Some(proc) => format!("\nProcessor #{proc}\n"),
161 None => format!("\nProcessor #unknown\n"),
162 };
163 s += "\tArchitecture: x86_64\n";
164 s += &print_opt_val("Vendor ID", &self.vendor_id);
165 s += &print_opt_val("CPU Family", &self.cpu_family);
166 s += &print_opt_val("CPU Model ID", &self.model);
167 s += &print_opt_val("CPU Model Name", &self.model_name);
168 s += &print_opt_val("Stepping", &self.stepping);
169 s += &print_opt_val("Microcode", &self.microcode);
170 s += &print_opt_val("Current frequency", &self.cpu_mhz);
171 s += &print_opt_val("L3 Cache Size", &self.cache_size);
172 s += &print_opt_val("Physical ID of CPU Core", &self.physical_id);
173 s += &print_opt_val("Siblings", &self.siblings);
174 s += &print_opt_val("Core ID", &self.core_id);
175 s += &print_opt_val("CPU cores", &self.cpu_cores);
176 s += &print_opt_val("APIC ID", &self.apicid);
177 s += &print_opt_val("Initial APIC ID", &self.initial_apicid);
178 s += &print_opt_val("FPU", &self.fpu);
179 s += &print_opt_val("FPU Exception", &self.fpu_exception);
180 s += &print_opt_val("CPUID Level", &self.cpuid_level);
181 s += &print_opt_val("WP", &self.wp);
182 s += &print_opt_val("Bogo MIPS", &self.bogomips);
183 s += &print_opt_val("Clflush Size", &self.clflush_size);
184 s += &print_opt_val("Cache alignment", &self.cache_alignment);
185 s += &print_opt_val("Address sizes", &self.address_sizes);
186 s += &print_opt_val("Power management", &self.power_management);
187
188 s
189 }
190}
191
192#[cfg(target_arch = "aarch64")]
193impl ToPlainText for CPU {
194 fn convert(&self) -> String {
195 let mut s = match self.processor {
196 Some(proc) => format!("Processor #{proc}\n"),
197 None => format!("Processor #unknown\n"),
198 };
199 s += &print_opt_val("CPU Implementer", &self.cpu_implementer);
200 s += &print_opt_val("CPU Architecture", &self.cpu_architecture);
201 s += &print_opt_val("CPU Variant", &self.cpu_variant);
202 s += &print_opt_val("CPU Part", &self.cpu_part);
203 s += &print_opt_val("CPU Revision", &self.cpu_revision);
204
205 s
206 }
207}
208
209fn read_info() -> Result<Vec<CPU>> {
210 let blocks = read_to_string("/proc/cpuinfo")?;
211 let blocks = blocks
212 .split("\n\n") .collect::<Vec<_>>();
214 let mut processors = Vec::with_capacity(blocks.len());
215
216 for block in blocks {
217 if block.trim().is_empty() {
218 continue;
219 }
220 let mut cpu = CPU::default();
221 for line in block.lines() {
222 parse_cpuinfo(&mut cpu, line);
223 }
224 processors.push(cpu);
225 }
226 Ok(processors)
227}
228
229fn get_parts(s: &str) -> impl Iterator<Item = &str> {
230 s.splitn(2, ':').map(|item| item.trim())
231}
232
233#[cfg(target_arch = "x86_64")]
234fn parse_cpuinfo(cpu: &mut CPU, parts: &str) {
235 let mut parts = get_parts(parts);
236 match (parts.next(), parts.next()) {
237 (Some(key), Some(val)) => match key {
238 "processor" => cpu.processor = val.parse().ok(),
239 "vendor_id" => cpu.vendor_id = Some(val.to_string()),
240 "cpu family" => cpu.cpu_family = val.parse().ok(),
241 "model" => cpu.model = val.parse().ok(),
242 "model name" => cpu.model_name = Some(val.to_string()),
243 "stepping" => cpu.stepping = val.parse().ok(),
244 "microcode" => cpu.microcode = Some(val.to_string()),
245 "cpu MHz" => cpu.cpu_mhz = val.parse().ok(),
246 "cache size" => cpu.cache_size = Size::try_from(val).ok(),
247 "physical id" => cpu.physical_id = val.parse().ok(),
248 "siblings" => cpu.siblings = val.parse().ok(),
249 "core id" => cpu.core_id = val.parse().ok(),
250 "cpu cores" => cpu.cpu_cores = val.parse().ok(),
251 "apicid" => cpu.apicid = val.parse().ok(),
252 "initial apicid" => cpu.initial_apicid = val.parse().ok(),
253 "fpu" => cpu.fpu = Some(get_bool(val)),
254 "fpu_exception" => cpu.fpu_exception = Some(get_bool(val)),
255 "cpuid level" => cpu.cpuid_level = val.parse().ok(),
256 "wp" => cpu.wp = Some(get_bool(val)),
257 "flags" | "Features" => {
258 cpu.flags = Some(val.split_whitespace().map(String::from).collect())
259 }
260 "bugs" => cpu.bugs = Some(val.split_whitespace().map(String::from).collect()),
261 "bogomips" | "BogoMIPS" => cpu.bogomips = val.parse().ok(),
262 "clflush size" => cpu.clflush_size = val.parse().ok(),
263 "cache_alignment" => cpu.cache_alignment = val.parse().ok(),
264 "address sizes" => cpu.address_sizes = Some(val.to_string()),
265 "power management" => cpu.power_management = Some(val.to_string()),
266 _ => {} },
268 _ => {}
269 }
270}
271
272#[cfg(target_arch = "aarch64")]
273fn parse_cpuinfo(cpu: &mut CPU, parts: &str) {
274 let mut parts = get_parts(parts);
275 match (parts.next(), parts.next()) {
276 (Some(key), Some(val)) => match key {
277 "CPU implementer" => cpu.cpu_implementer = Some(val.to_string()),
279 "CPU architecture" => cpu.cpu_architecture = val.parse().ok(),
280 "CPU variant" => cpu.cpu_variant = Some(val.to_string()),
281 "CPU part" => cpu.cpu_part = Some(val.to_string()),
282 "CPU revision" => cpu.cpu_revision = val.parse().ok(),
283 _ => {} },
285 _ => {}
286 }
287}
288
289fn get_bool(s: &str) -> bool {
290 match s {
291 "yes" | "ok" => true,
292 _ => false,
293 }
294}