1#![allow(clippy::too_many_arguments)]
4
5use std::collections::{HashMap, HashSet};
6use std::fs::File;
7use std::io::{BufRead, BufReader, Read};
8use std::time::Instant;
9
10use crate::sys::utils::to_u64;
11use crate::{Cpu, CpuRefreshKind};
12
13macro_rules! to_str {
14 ($e:expr) => {
15 unsafe { std::str::from_utf8_unchecked($e) }
16 };
17}
18
19pub(crate) struct CpusWrapper {
20 pub(crate) global_cpu: CpuUsage,
21 pub(crate) cpus: Vec<Cpu>,
22 got_cpu_frequency: bool,
23 last_update: Option<Instant>,
25}
26
27impl CpusWrapper {
28 pub(crate) fn new() -> Self {
29 Self {
30 global_cpu: CpuUsage::default(),
31 cpus: Vec::with_capacity(4),
32 got_cpu_frequency: false,
33 last_update: None,
34 }
35 }
36
37 pub(crate) fn refresh_if_needed(
38 &mut self,
39 only_update_global_cpu: bool,
40 refresh_kind: CpuRefreshKind,
41 ) {
42 self.refresh(only_update_global_cpu, refresh_kind);
43 }
44
45 pub(crate) fn refresh(&mut self, only_update_global_cpu: bool, refresh_kind: CpuRefreshKind) {
46 let need_cpu_usage_update = self
47 .last_update
48 .map(|last_update| last_update.elapsed() >= crate::MINIMUM_CPU_UPDATE_INTERVAL)
49 .unwrap_or(true);
50
51 let first = self.cpus.is_empty();
52 let mut vendors_brands = if first {
53 get_vendor_id_and_brand()
54 } else {
55 HashMap::new()
56 };
57
58 if need_cpu_usage_update {
61 self.last_update = Some(Instant::now());
62 let f = match File::open("/proc/stat") {
63 Ok(f) => f,
64 Err(_e) => {
65 sysinfo_debug!("failed to retrieve CPU information: {:?}", _e);
66 return;
67 }
68 };
69 let buf = BufReader::new(f);
70
71 let mut i: usize = 0;
72 let mut it = buf.split(b'\n');
73
74 if first || refresh_kind.cpu_usage() {
75 if let Some(Ok(line)) = it.next() {
76 if line.len() < 4 || &line[..4] != b"cpu " {
77 return;
78 }
79 let mut parts = line.split(|x| *x == b' ').filter(|s| !s.is_empty()).skip(1);
80 self.global_cpu.set(
81 parts.next().map(to_u64).unwrap_or(0),
82 parts.next().map(to_u64).unwrap_or(0),
83 parts.next().map(to_u64).unwrap_or(0),
84 parts.next().map(to_u64).unwrap_or(0),
85 parts.next().map(to_u64).unwrap_or(0),
86 parts.next().map(to_u64).unwrap_or(0),
87 parts.next().map(to_u64).unwrap_or(0),
88 parts.next().map(to_u64).unwrap_or(0),
89 parts.next().map(to_u64).unwrap_or(0),
90 parts.next().map(to_u64).unwrap_or(0),
91 );
92 }
93 if first || !only_update_global_cpu {
94 while let Some(Ok(line)) = it.next() {
95 if line.len() < 3 || &line[..3] != b"cpu" {
96 break;
97 }
98
99 let mut parts = line.split(|x| *x == b' ').filter(|s| !s.is_empty());
100 if first {
101 let (vendor_id, brand) = match vendors_brands.remove(&i) {
102 Some((vendor_id, brand)) => (vendor_id, brand),
103 None => (String::new(), String::new()),
104 };
105 self.cpus.push(Cpu {
106 inner: CpuInner::new_with_values(
107 to_str!(parts.next().unwrap_or(&[])),
108 parts.next().map(to_u64).unwrap_or(0),
109 parts.next().map(to_u64).unwrap_or(0),
110 parts.next().map(to_u64).unwrap_or(0),
111 parts.next().map(to_u64).unwrap_or(0),
112 parts.next().map(to_u64).unwrap_or(0),
113 parts.next().map(to_u64).unwrap_or(0),
114 parts.next().map(to_u64).unwrap_or(0),
115 parts.next().map(to_u64).unwrap_or(0),
116 parts.next().map(to_u64).unwrap_or(0),
117 parts.next().map(to_u64).unwrap_or(0),
118 0,
119 vendor_id,
120 brand,
121 ),
122 });
123 } else {
124 parts.next(); if let Some(cpu) = self.cpus.get_mut(i) {
126 cpu.inner.set(
127 parts.next().map(to_u64).unwrap_or(0),
128 parts.next().map(to_u64).unwrap_or(0),
129 parts.next().map(to_u64).unwrap_or(0),
130 parts.next().map(to_u64).unwrap_or(0),
131 parts.next().map(to_u64).unwrap_or(0),
132 parts.next().map(to_u64).unwrap_or(0),
133 parts.next().map(to_u64).unwrap_or(0),
134 parts.next().map(to_u64).unwrap_or(0),
135 parts.next().map(to_u64).unwrap_or(0),
136 parts.next().map(to_u64).unwrap_or(0),
137 );
138 } else {
139 sysinfo_debug!("ignoring new CPU added");
142 }
143 }
144
145 i += 1;
146 }
147 }
148 if i < self.cpus.len() {
149 sysinfo_debug!("{} CPU(s) seem to have been removed", self.cpus.len() - i);
150 }
151 }
152 }
153
154 if refresh_kind.frequency() {
155 #[cfg(feature = "multithread")]
156 use rayon::iter::{
157 IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator,
158 };
159
160 #[cfg(feature = "multithread")]
161 fn iter_mut<'a, T>(
163 val: &'a mut T,
164 ) -> <&'a mut T as rayon::iter::IntoParallelIterator>::Iter
165 where
166 &'a mut T: rayon::iter::IntoParallelIterator,
167 {
168 val.par_iter_mut()
169 }
170
171 #[cfg(not(feature = "multithread"))]
172 fn iter_mut(val: &mut [Cpu]) -> std::slice::IterMut<'_, Cpu> {
173 val.iter_mut()
174 }
175
176 iter_mut(&mut self.cpus)
178 .enumerate()
179 .for_each(|(pos, proc_)| proc_.inner.frequency = get_cpu_frequency(pos));
180
181 self.got_cpu_frequency = true;
182 }
183 }
184
185 pub(crate) fn get_global_raw_times(&self) -> (u64, u64) {
186 (self.global_cpu.total_time, self.global_cpu.old_total_time)
187 }
188
189 pub(crate) fn len(&self) -> usize {
190 self.cpus.len()
191 }
192
193 pub(crate) fn is_empty(&self) -> bool {
194 self.cpus.is_empty()
195 }
196}
197
198#[derive(Clone, Copy, Debug, Default)]
200pub(crate) struct CpuValues {
201 user: u64,
202 nice: u64,
203 system: u64,
204 idle: u64,
205 iowait: u64,
206 irq: u64,
207 softirq: u64,
208 steal: u64,
209 guest: u64,
210 guest_nice: u64,
211}
212
213impl CpuValues {
214 pub fn set(
216 &mut self,
217 user: u64,
218 nice: u64,
219 system: u64,
220 idle: u64,
221 iowait: u64,
222 irq: u64,
223 softirq: u64,
224 steal: u64,
225 guest: u64,
226 guest_nice: u64,
227 ) {
228 self.user = user.saturating_sub(guest);
230 self.nice = nice.saturating_sub(guest_nice);
232 self.system = system;
233 self.idle = idle;
234 self.iowait = iowait;
235 self.irq = irq;
236 self.softirq = softirq;
237 self.steal = steal;
238 self.guest = guest;
239 self.guest_nice = guest_nice;
240 }
241
242 #[inline]
243 pub fn work_time(&self) -> u64 {
244 self.user.saturating_add(self.nice)
245 }
246
247 #[inline]
248 pub fn system_time(&self) -> u64 {
249 self.system
250 .saturating_add(self.irq)
251 .saturating_add(self.softirq)
252 }
253
254 #[inline]
255 pub fn idle_time(&self) -> u64 {
256 self.idle.saturating_add(self.iowait)
257 }
258
259 #[inline]
260 pub fn virtual_time(&self) -> u64 {
261 self.guest.saturating_add(self.guest_nice)
262 }
263
264 #[inline]
265 pub fn total_time(&self) -> u64 {
266 self.work_time()
267 .saturating_add(self.system_time())
268 .saturating_add(self.idle_time())
269 .saturating_add(self.virtual_time())
270 .saturating_add(self.steal)
271 }
272}
273
274#[derive(Default)]
275pub(crate) struct CpuUsage {
276 percent: f32,
277 old_values: CpuValues,
278 new_values: CpuValues,
279 total_time: u64,
280 old_total_time: u64,
281}
282
283impl CpuUsage {
284 pub(crate) fn new_with_values(
285 user: u64,
286 nice: u64,
287 system: u64,
288 idle: u64,
289 iowait: u64,
290 irq: u64,
291 softirq: u64,
292 steal: u64,
293 guest: u64,
294 guest_nice: u64,
295 ) -> Self {
296 let mut new_values = CpuValues::default();
297 new_values.set(
298 user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice,
299 );
300 Self {
301 old_values: CpuValues::default(),
302 new_values,
303 percent: 0f32,
304 total_time: 0,
305 old_total_time: 0,
306 }
307 }
308
309 pub(crate) fn set(
310 &mut self,
311 user: u64,
312 nice: u64,
313 system: u64,
314 idle: u64,
315 iowait: u64,
316 irq: u64,
317 softirq: u64,
318 steal: u64,
319 guest: u64,
320 guest_nice: u64,
321 ) {
322 macro_rules! min {
323 ($a:expr, $b:expr, $def:expr) => {
324 if $a > $b {
325 ($a - $b) as f32
326 } else {
327 $def
328 }
329 };
330 }
331
332 self.old_values = self.new_values;
333 self.new_values.set(
334 user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice,
335 );
336
337 self.total_time = self.new_values.total_time();
338 self.old_total_time = self.old_values.total_time();
339
340 let nice_period = self.new_values.nice.saturating_sub(self.old_values.nice);
341 let user_period = self.new_values.user.saturating_sub(self.old_values.user);
342 let steal_period = self.new_values.steal.saturating_sub(self.old_values.steal);
343 let guest_period = self
344 .new_values
345 .virtual_time()
346 .saturating_sub(self.old_values.virtual_time());
347 let system_period = self
348 .new_values
349 .system_time()
350 .saturating_sub(self.old_values.system_time());
351
352 let total = min!(self.total_time, self.old_total_time, 1.);
353 let nice = nice_period as f32 / total;
354 let user = user_period as f32 / total;
355 let system = system_period as f32 / total;
356 let irq = (steal_period + guest_period) as f32 / total;
357
358 self.percent = (nice + user + system + irq) * 100.;
359 if self.percent > 100. {
360 self.percent = 100.; }
362 }
363
364 pub(crate) fn usage(&self) -> f32 {
365 self.percent
366 }
367}
368
369pub(crate) struct CpuInner {
370 usage: CpuUsage,
371 pub(crate) name: String,
372 pub(crate) frequency: u64,
373 pub(crate) vendor_id: String,
374 pub(crate) brand: String,
375}
376
377impl CpuInner {
378 pub(crate) fn new_with_values(
379 name: &str,
380 user: u64,
381 nice: u64,
382 system: u64,
383 idle: u64,
384 iowait: u64,
385 irq: u64,
386 softirq: u64,
387 steal: u64,
388 guest: u64,
389 guest_nice: u64,
390 frequency: u64,
391 vendor_id: String,
392 brand: String,
393 ) -> Self {
394 Self {
395 usage: CpuUsage::new_with_values(
396 user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice,
397 ),
398 name: name.to_owned(),
399 frequency,
400 vendor_id,
401 brand,
402 }
403 }
404
405 pub(crate) fn set(
406 &mut self,
407 user: u64,
408 nice: u64,
409 system: u64,
410 idle: u64,
411 iowait: u64,
412 irq: u64,
413 softirq: u64,
414 steal: u64,
415 guest: u64,
416 guest_nice: u64,
417 ) {
418 self.usage.set(
419 user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice,
420 );
421 }
422
423 pub(crate) fn cpu_usage(&self) -> f32 {
424 self.usage.percent
425 }
426
427 pub(crate) fn name(&self) -> &str {
428 &self.name
429 }
430
431 pub(crate) fn frequency(&self) -> u64 {
433 self.frequency
434 }
435
436 pub(crate) fn vendor_id(&self) -> &str {
437 &self.vendor_id
438 }
439
440 pub(crate) fn brand(&self) -> &str {
441 &self.brand
442 }
443}
444
445pub(crate) fn get_cpu_frequency(cpu_core_index: usize) -> u64 {
446 let mut s = String::new();
447 if File::open(format!(
448 "/sys/devices/system/cpu/cpu{cpu_core_index}/cpufreq/scaling_cur_freq",
449 ))
450 .and_then(|mut f| f.read_to_string(&mut s))
451 .is_ok()
452 {
453 let freq_option = s.trim().split('\n').next();
454 if let Some(freq_string) = freq_option {
455 if let Ok(freq) = freq_string.parse::<u64>() {
456 return freq / 1000;
457 }
458 }
459 }
460 s.clear();
461 if File::open("/proc/cpuinfo")
462 .and_then(|mut f| f.read_to_string(&mut s))
463 .is_err()
464 {
465 return 0;
466 }
467 let find_cpu_mhz = s.split('\n').find(|line| {
468 cpuinfo_is_key(line, b"cpu MHz\t")
469 || cpuinfo_is_key(line, b"CPU MHz\t")
470 || cpuinfo_is_key(line, b"BogoMIPS")
471 || cpuinfo_is_key(line, b"clock\t")
472 || cpuinfo_is_key(line, b"bogomips per cpu")
473 });
474 find_cpu_mhz
475 .and_then(|line| line.split(':').next_back())
476 .and_then(|val| val.replace("MHz", "").trim().parse::<f64>().ok())
477 .map(|speed| speed as u64)
478 .unwrap_or_default()
479}
480
481#[allow(unused_assignments)]
482pub(crate) fn get_physical_core_count() -> Option<usize> {
483 let mut s = String::new();
484 if let Err(_e) = File::open("/proc/cpuinfo").and_then(|mut f| f.read_to_string(&mut s)) {
485 sysinfo_debug!("Cannot read `/proc/cpuinfo` file: {:?}", _e);
486 return None;
487 }
488
489 macro_rules! add_core {
490 ($core_ids_and_physical_ids:ident, $core_id:ident, $physical_id:ident, $cpu:ident) => {{
491 if !$core_id.is_empty() && !$physical_id.is_empty() {
492 $core_ids_and_physical_ids.insert(format!("{} {}", $core_id, $physical_id));
493 } else if !$cpu.is_empty() {
494 $core_ids_and_physical_ids.insert($cpu.to_owned());
498 }
499 $core_id = "";
500 $physical_id = "";
501 $cpu = "";
502 }};
503 }
504
505 let mut core_ids_and_physical_ids: HashSet<String> = HashSet::new();
506 let mut core_id = "";
507 let mut physical_id = "";
508 let mut cpu = "";
509
510 for line in s.lines() {
511 if line.is_empty() {
512 add_core!(core_ids_and_physical_ids, core_id, physical_id, cpu);
513 } else if line.starts_with("processor") {
514 cpu = line
515 .splitn(2, ':')
516 .last()
517 .map(|x| x.trim())
518 .unwrap_or_default();
519 } else if line.starts_with("core id") {
520 core_id = line
521 .splitn(2, ':')
522 .last()
523 .map(|x| x.trim())
524 .unwrap_or_default();
525 } else if line.starts_with("physical id") {
526 physical_id = line
527 .splitn(2, ':')
528 .last()
529 .map(|x| x.trim())
530 .unwrap_or_default();
531 }
532 }
533 add_core!(core_ids_and_physical_ids, core_id, physical_id, cpu);
534
535 Some(core_ids_and_physical_ids.len())
536}
537
538fn get_arm_implementer(implementer: u32) -> Option<&'static str> {
546 Some(match implementer {
547 0x41 => "ARM",
548 0x42 => "Broadcom",
549 0x43 => "Cavium",
550 0x44 => "DEC",
551 0x46 => "FUJITSU",
552 0x48 => "HiSilicon",
553 0x49 => "Infineon",
554 0x4d => "Motorola/Freescale",
555 0x4e => "NVIDIA",
556 0x50 => "APM",
557 0x51 => "Qualcomm",
558 0x53 => "Samsung",
559 0x56 => "Marvell",
560 0x61 => "Apple",
561 0x66 => "Faraday",
562 0x69 => "Intel",
563 0x70 => "Phytium",
564 0xc0 => "Ampere",
565 _ => return None,
566 })
567}
568
569fn get_arm_part(implementer: u32, part: u32) -> Option<&'static str> {
577 Some(match (implementer, part) {
578 (0x41, 0x810) => "ARM810",
580 (0x41, 0x920) => "ARM920",
581 (0x41, 0x922) => "ARM922",
582 (0x41, 0x926) => "ARM926",
583 (0x41, 0x940) => "ARM940",
584 (0x41, 0x946) => "ARM946",
585 (0x41, 0x966) => "ARM966",
586 (0x41, 0xa20) => "ARM1020",
587 (0x41, 0xa22) => "ARM1022",
588 (0x41, 0xa26) => "ARM1026",
589 (0x41, 0xb02) => "ARM11 MPCore",
590 (0x41, 0xb36) => "ARM1136",
591 (0x41, 0xb56) => "ARM1156",
592 (0x41, 0xb76) => "ARM1176",
593 (0x41, 0xc05) => "Cortex-A5",
594 (0x41, 0xc07) => "Cortex-A7",
595 (0x41, 0xc08) => "Cortex-A8",
596 (0x41, 0xc09) => "Cortex-A9",
597 (0x41, 0xc0d) => "Cortex-A17", (0x41, 0xc0f) => "Cortex-A15",
599 (0x41, 0xc0e) => "Cortex-A17",
600 (0x41, 0xc14) => "Cortex-R4",
601 (0x41, 0xc15) => "Cortex-R5",
602 (0x41, 0xc17) => "Cortex-R7",
603 (0x41, 0xc18) => "Cortex-R8",
604 (0x41, 0xc20) => "Cortex-M0",
605 (0x41, 0xc21) => "Cortex-M1",
606 (0x41, 0xc23) => "Cortex-M3",
607 (0x41, 0xc24) => "Cortex-M4",
608 (0x41, 0xc27) => "Cortex-M7",
609 (0x41, 0xc60) => "Cortex-M0+",
610 (0x41, 0xd01) => "Cortex-A32",
611 (0x41, 0xd02) => "Cortex-A34",
612 (0x41, 0xd03) => "Cortex-A53",
613 (0x41, 0xd04) => "Cortex-A35",
614 (0x41, 0xd05) => "Cortex-A55",
615 (0x41, 0xd06) => "Cortex-A65",
616 (0x41, 0xd07) => "Cortex-A57",
617 (0x41, 0xd08) => "Cortex-A72",
618 (0x41, 0xd09) => "Cortex-A73",
619 (0x41, 0xd0a) => "Cortex-A75",
620 (0x41, 0xd0b) => "Cortex-A76",
621 (0x41, 0xd0c) => "Neoverse-N1",
622 (0x41, 0xd0d) => "Cortex-A77",
623 (0x41, 0xd0e) => "Cortex-A76AE",
624 (0x41, 0xd13) => "Cortex-R52",
625 (0x41, 0xd15) => "Cortex-R82",
626 (0x41, 0xd16) => "Cortex-R52+",
627 (0x41, 0xd20) => "Cortex-M23",
628 (0x41, 0xd21) => "Cortex-M33",
629 (0x41, 0xd22) => "Cortex-R55",
630 (0x41, 0xd23) => "Cortex-R85",
631 (0x41, 0xd40) => "Neoverse-V1",
632 (0x41, 0xd41) => "Cortex-A78",
633 (0x41, 0xd42) => "Cortex-A78AE",
634 (0x41, 0xd43) => "Cortex-A65AE",
635 (0x41, 0xd44) => "Cortex-X1",
636 (0x41, 0xd46) => "Cortex-A510",
637 (0x41, 0xd47) => "Cortex-A710",
638 (0x41, 0xd48) => "Cortex-X2",
639 (0x41, 0xd49) => "Neoverse-N2",
640 (0x41, 0xd4a) => "Neoverse-E1",
641 (0x41, 0xd4b) => "Cortex-A78C",
642 (0x41, 0xd4c) => "Cortex-X1C",
643 (0x41, 0xd4d) => "Cortex-A715",
644 (0x41, 0xd4e) => "Cortex-X3",
645 (0x41, 0xd4f) => "Neoverse-V2",
646 (0x41, 0xd80) => "Cortex-A520",
647 (0x41, 0xd81) => "Cortex-A720",
648 (0x41, 0xd82) => "Cortex-X4",
649 (0x41, 0xd84) => "Neoverse-V3",
650 (0x41, 0xd85) => "Cortex-X925",
651 (0x41, 0xd87) => "Cortex-A725",
652 (0x41, 0xd8e) => "Neoverse-N3",
653
654 (0x42, 0x00f) => "Brahma-B15",
656 (0x42, 0x100) => "Brahma-B53",
657 (0x42, 0x516) => "ThunderX2",
658
659 (0x43, 0x0a0) => "ThunderX",
661 (0x43, 0x0a1) => "ThunderX-88XX",
662 (0x43, 0x0a2) => "ThunderX-81XX",
663 (0x43, 0x0a3) => "ThunderX-83XX",
664 (0x43, 0x0af) => "ThunderX2-99xx",
665
666 (0x44, 0xa10) => "SA110",
668 (0x44, 0xa11) => "SA1100",
669
670 (0x46, 0x001) => "A64FX",
672
673 (0x48, 0xd01) => "Kunpeng-920", (0x4e, 0x000) => "Denver",
678 (0x4e, 0x003) => "Denver 2",
679 (0x4e, 0x004) => "Carmel",
680
681 (0x50, 0x000) => "X-Gene",
683
684 (0x51, 0x00f) => "Scorpion",
686 (0x51, 0x02d) => "Scorpion",
687 (0x51, 0x04d) => "Krait",
688 (0x51, 0x06f) => "Krait",
689 (0x51, 0x201) => "Kryo",
690 (0x51, 0x205) => "Kryo",
691 (0x51, 0x211) => "Kryo",
692 (0x51, 0x800) => "Falkor-V1/Kryo",
693 (0x51, 0x801) => "Kryo-V2",
694 (0x51, 0x802) => "Kryo-3XX-Gold",
695 (0x51, 0x803) => "Kryo-3XX-Silver",
696 (0x51, 0x804) => "Kryo-4XX-Gold",
697 (0x51, 0x805) => "Kryo-4XX-Silver",
698 (0x51, 0xc00) => "Falkor",
699 (0x51, 0xc01) => "Saphira",
700
701 (0x53, 0x001) => "exynos-m1",
703
704 (0x56, 0x131) => "Feroceon-88FR131",
706 (0x56, 0x581) => "PJ4/PJ4b",
707 (0x56, 0x584) => "PJ4B-MP",
708
709 (0x61, 0x020) => "Icestorm-A14",
711 (0x61, 0x021) => "Firestorm-A14",
712 (0x61, 0x022) => "Icestorm-M1",
713 (0x61, 0x023) => "Firestorm-M1",
714 (0x61, 0x024) => "Icestorm-M1-Pro",
715 (0x61, 0x025) => "Firestorm-M1-Pro",
716 (0x61, 0x028) => "Icestorm-M1-Max",
717 (0x61, 0x029) => "Firestorm-M1-Max",
718 (0x61, 0x030) => "Blizzard-A15",
719 (0x61, 0x031) => "Avalanche-A15",
720 (0x61, 0x032) => "Blizzard-M2",
721 (0x61, 0x033) => "Avalanche-M2",
722
723 (0x66, 0x526) => "FA526",
725 (0x66, 0x626) => "FA626",
726
727 (0x69, 0x200) => "i80200",
729 (0x69, 0x210) => "PXA250A",
730 (0x69, 0x212) => "PXA210A",
731 (0x69, 0x242) => "i80321-400",
732 (0x69, 0x243) => "i80321-600",
733 (0x69, 0x290) => "PXA250B/PXA26x",
734 (0x69, 0x292) => "PXA210B",
735 (0x69, 0x2c2) => "i80321-400-B0",
736 (0x69, 0x2c3) => "i80321-600-B0",
737 (0x69, 0x2d0) => "PXA250C/PXA255/PXA26x",
738 (0x69, 0x2d2) => "PXA210C",
739 (0x69, 0x411) => "PXA27x",
740 (0x69, 0x41c) => "IPX425-533",
741 (0x69, 0x41d) => "IPX425-400",
742 (0x69, 0x41f) => "IPX425-266",
743 (0x69, 0x682) => "PXA32x",
744 (0x69, 0x683) => "PXA930/PXA935",
745 (0x69, 0x688) => "PXA30x",
746 (0x69, 0x689) => "PXA31x",
747 (0x69, 0xb11) => "SA1110",
748 (0x69, 0xc12) => "IPX1200",
749
750 (0x70, 0x660) => "FTC660",
752 (0x70, 0x661) => "FTC661",
753 (0x70, 0x662) => "FTC662",
754 (0x70, 0x663) => "FTC663",
755
756 _ => return None,
757 })
758}
759
760pub(crate) fn get_vendor_id_and_brand() -> HashMap<usize, (String, String)> {
762 let mut s = String::new();
763 if File::open("/proc/cpuinfo")
764 .and_then(|mut f| f.read_to_string(&mut s))
765 .is_err()
766 {
767 return HashMap::new();
768 }
769 get_vendor_id_and_brand_inner(&s)
770}
771
772#[inline]
773fn cpuinfo_is_key(line: &str, key: &[u8]) -> bool {
774 let line = line.as_bytes();
775 line.len() > key.len() && line[..key.len()].eq_ignore_ascii_case(key)
776}
777
778fn get_vendor_id_and_brand_inner(data: &str) -> HashMap<usize, (String, String)> {
779 fn get_value(s: &str) -> String {
780 s.split(':')
781 .next_back()
782 .map(|x| x.trim().to_owned())
783 .unwrap_or_default()
784 }
785
786 fn get_hex_value(s: &str) -> u32 {
787 s.split(':')
788 .next_back()
789 .map(|x| x.trim())
790 .filter(|x| x.starts_with("0x"))
791 .map(|x| u32::from_str_radix(&x[2..], 16).unwrap())
792 .unwrap_or_default()
793 }
794
795 #[inline]
796 fn is_new_processor(line: &str) -> bool {
797 line.starts_with("processor\t")
798 }
799
800 #[derive(Default)]
801 struct CpuInfo {
802 index: usize,
803 vendor_id: Option<String>,
804 brand: Option<String>,
805 implementer: Option<u32>,
806 part: Option<u32>,
807 }
808
809 impl CpuInfo {
810 fn has_all_info(&self) -> bool {
811 (self.brand.is_some() && self.vendor_id.is_some())
812 || (self.implementer.is_some() && self.part.is_some())
813 }
814
815 fn convert(mut self) -> (usize, String, String) {
816 let (vendor_id, brand) = if let (Some(implementer), Some(part)) =
817 (self.implementer.take(), self.part.take())
818 {
819 let vendor_id = get_arm_implementer(implementer).map(String::from);
820 let brand = get_arm_part(implementer, part)
831 .map(String::from)
832 .or_else(|| self.brand.take());
833 (vendor_id, brand)
834 } else {
835 (self.vendor_id.take(), self.brand.take())
836 };
837 (
838 self.index,
839 vendor_id.unwrap_or_default(),
840 brand.unwrap_or_default(),
841 )
842 }
843 }
844
845 let mut cpus: HashMap<usize, (String, String)> = HashMap::new();
846 let mut lines = data.split('\n').peekable();
847 while let Some(line) = lines.next() {
848 if is_new_processor(line) {
849 let index = match line
850 .split(':')
851 .nth(1)
852 .and_then(|i| i.trim().parse::<usize>().ok())
853 {
854 Some(index) => index,
855 None => {
856 sysinfo_debug!("Couldn't get processor ID from {line:?}, ignoring this core");
857 continue;
858 }
859 };
860
861 let mut info = CpuInfo {
862 index,
863 ..Default::default()
864 };
865
866 #[allow(clippy::while_let_on_iterator)]
867 while let Some(line) = lines.peek() {
868 if cpuinfo_is_key(line, b"vendor_id\t") {
869 info.vendor_id = Some(get_value(line));
870 } else if cpuinfo_is_key(line, b"model name\t") {
871 info.brand = Some(get_value(line));
872 } else if cpuinfo_is_key(line, b"CPU implementer\t") {
873 info.implementer = Some(get_hex_value(line));
874 } else if cpuinfo_is_key(line, b"CPU part\t") {
875 info.part = Some(get_hex_value(line));
876 } else if info.has_all_info() || is_new_processor(line) {
877 break;
878 }
879 lines.next();
880 }
881 let (index, vendor_id, brand) = info.convert();
882 cpus.insert(index, (vendor_id, brand));
883 }
884 }
885 cpus
886}
887
888#[cfg(test)]
889mod test {
890 use super::get_vendor_id_and_brand_inner;
891
892 #[test]
897 fn test_cpu_retrieval() {
898 const DATA: &str = r#"
899processor : 1
900cpu model : Loongson-3 V0.4 FPU V0.1
901model name : Loongson-3A R4 (Loongson-3B4000) @ 1800MHz
902CPU MHz : 1800.00
903core : 1
904
905processor : 2
906cpu model : Loongson-3 V0.4 FPU V0.1
907model name : Loongson-3A R4 (Loongson-3B4000) @ 1800MHz
908CPU MHz : 1800.00
909package : 0
910core : 2
911
912processor : 3
913cpu model : Loongson-3 V0.4 FPU V0.1
914model name : Loongson-3A R4 (Loongson-3B4000) @ 1800MHz
915CPU MHz : 1800.00
916core : 3
917
918processor : 4
919cpu model : Loongson-3 V0.4 FPU V0.1
920model name : Loongson-3A R4 (Loongson-3B4000) @ 1800MHz
921CPU MHz : 1800.00
922core : 0
923
924processor : 5
925cpu model : Loongson-3 V0.4 FPU V0.1
926model name : Loongson-3A R4 (Loongson-3B4000) @ 1800MHz
927CPU MHz : 1800.00
928core : 1
929
930processor : 6
931cpu model : Loongson-3 V0.4 FPU V0.1
932model name : Loongson-3A R4 (Loongson-3B4000) @ 1800MHz
933CPU MHz : 1800.00
934core : 2
935
936processor : 7
937cpu model : Loongson-3 V0.4 FPU V0.1
938model name : Loongson-3A R4 (Loongson-3B4000) @ 1800MHz
939CPU MHz : 1800.00
940core : 3"#;
941
942 let cpus = get_vendor_id_and_brand_inner(DATA);
943 assert_eq!(cpus.len(), 7);
944 }
945}