1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | #![allow (clippy::too_many_arguments)] |
4 | |
5 | use std::collections::HashSet; |
6 | use std::fs::File; |
7 | use std::io::{BufRead, BufReader, Read}; |
8 | use std::time::Instant; |
9 | |
10 | use crate::sys::utils::to_u64; |
11 | use crate::{CpuExt, CpuRefreshKind, SystemExt}; |
12 | |
13 | macro_rules! to_str { |
14 | ($e:expr) => { |
15 | unsafe { std::str::from_utf8_unchecked($e) } |
16 | }; |
17 | } |
18 | |
19 | pub(crate) struct CpusWrapper { |
20 | pub(crate) global_cpu: Cpu, |
21 | pub(crate) cpus: Vec<Cpu>, |
22 | /// Field set to `false` in `update_cpus` and to `true` in `refresh_processes_specifics`. |
23 | /// |
24 | /// The reason behind this is to avoid calling the `update_cpus` more than necessary. |
25 | /// For example when running `refresh_all` or `refresh_specifics`. |
26 | need_cpus_update: bool, |
27 | got_cpu_frequency: bool, |
28 | /// This field is needed to prevent updating when not enough time passed since last update. |
29 | last_update: Option<Instant>, |
30 | } |
31 | |
32 | impl CpusWrapper { |
33 | pub(crate) fn new() -> Self { |
34 | Self { |
35 | global_cpu: Cpu::new_with_values( |
36 | "" , |
37 | 0, |
38 | 0, |
39 | 0, |
40 | 0, |
41 | 0, |
42 | 0, |
43 | 0, |
44 | 0, |
45 | 0, |
46 | 0, |
47 | 0, |
48 | String::new(), |
49 | String::new(), |
50 | ), |
51 | cpus: Vec::with_capacity(4), |
52 | need_cpus_update: true, |
53 | got_cpu_frequency: false, |
54 | last_update: None, |
55 | } |
56 | } |
57 | |
58 | pub(crate) fn refresh_if_needed( |
59 | &mut self, |
60 | only_update_global_cpu: bool, |
61 | refresh_kind: CpuRefreshKind, |
62 | ) { |
63 | if self.need_cpus_update { |
64 | self.refresh(only_update_global_cpu, refresh_kind); |
65 | } |
66 | } |
67 | |
68 | pub(crate) fn refresh(&mut self, only_update_global_cpu: bool, refresh_kind: CpuRefreshKind) { |
69 | let need_cpu_usage_update = self |
70 | .last_update |
71 | .map(|last_update| last_update.elapsed() > crate::System::MINIMUM_CPU_UPDATE_INTERVAL) |
72 | .unwrap_or(true); |
73 | |
74 | let first = self.cpus.is_empty(); |
75 | let (vendor_id, brand) = if first { |
76 | get_vendor_id_and_brand() |
77 | } else { |
78 | (String::new(), String::new()) |
79 | }; |
80 | |
81 | // If the last CPU usage update is too close (less than `MINIMUM_CPU_UPDATE_INTERVAL`), |
82 | // we don't want to update CPUs times. |
83 | if need_cpu_usage_update { |
84 | self.last_update = Some(Instant::now()); |
85 | let f = match File::open("/proc/stat" ) { |
86 | Ok(f) => f, |
87 | Err(_e) => { |
88 | sysinfo_debug!("failed to retrieve CPU information: {:?}" , _e); |
89 | return; |
90 | } |
91 | }; |
92 | let buf = BufReader::new(f); |
93 | |
94 | self.need_cpus_update = false; |
95 | let mut i: usize = 0; |
96 | let mut it = buf.split(b' \n' ); |
97 | |
98 | if first || refresh_kind.cpu_usage() { |
99 | if let Some(Ok(line)) = it.next() { |
100 | if &line[..4] != b"cpu " { |
101 | return; |
102 | } |
103 | let mut parts = line.split(|x| *x == b' ' ).filter(|s| !s.is_empty()); |
104 | if first { |
105 | self.global_cpu.name = to_str!(parts.next().unwrap_or(&[])).to_owned(); |
106 | } else { |
107 | parts.next(); |
108 | } |
109 | self.global_cpu.set( |
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 | parts.next().map(to_u64).unwrap_or(0), |
119 | parts.next().map(to_u64).unwrap_or(0), |
120 | ); |
121 | } |
122 | if first || !only_update_global_cpu { |
123 | while let Some(Ok(line)) = it.next() { |
124 | if &line[..3] != b"cpu" { |
125 | break; |
126 | } |
127 | |
128 | let mut parts = line.split(|x| *x == b' ' ).filter(|s| !s.is_empty()); |
129 | if first { |
130 | self.cpus.push(Cpu::new_with_values( |
131 | to_str!(parts.next().unwrap_or(&[])), |
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 | parts.next().map(to_u64).unwrap_or(0), |
138 | parts.next().map(to_u64).unwrap_or(0), |
139 | parts.next().map(to_u64).unwrap_or(0), |
140 | parts.next().map(to_u64).unwrap_or(0), |
141 | parts.next().map(to_u64).unwrap_or(0), |
142 | 0, |
143 | vendor_id.clone(), |
144 | brand.clone(), |
145 | )); |
146 | } else { |
147 | parts.next(); // we don't want the name again |
148 | self.cpus[i].set( |
149 | parts.next().map(to_u64).unwrap_or(0), |
150 | parts.next().map(to_u64).unwrap_or(0), |
151 | parts.next().map(to_u64).unwrap_or(0), |
152 | parts.next().map(to_u64).unwrap_or(0), |
153 | parts.next().map(to_u64).unwrap_or(0), |
154 | parts.next().map(to_u64).unwrap_or(0), |
155 | parts.next().map(to_u64).unwrap_or(0), |
156 | parts.next().map(to_u64).unwrap_or(0), |
157 | parts.next().map(to_u64).unwrap_or(0), |
158 | parts.next().map(to_u64).unwrap_or(0), |
159 | ); |
160 | } |
161 | |
162 | i += 1; |
163 | } |
164 | } |
165 | } |
166 | } |
167 | |
168 | if refresh_kind.frequency() { |
169 | #[cfg (feature = "multithread" )] |
170 | use rayon::iter::{ |
171 | IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator, |
172 | }; |
173 | |
174 | #[cfg (feature = "multithread" )] |
175 | // This function is voluntarily made generic in case we want to generalize it. |
176 | fn iter_mut<'a, T>( |
177 | val: &'a mut T, |
178 | ) -> <&'a mut T as rayon::iter::IntoParallelIterator>::Iter |
179 | where |
180 | &'a mut T: rayon::iter::IntoParallelIterator, |
181 | { |
182 | val.par_iter_mut() |
183 | } |
184 | |
185 | #[cfg (not(feature = "multithread" ))] |
186 | fn iter_mut<'a>(val: &'a mut Vec<Cpu>) -> std::slice::IterMut<'a, Cpu> { |
187 | val.iter_mut() |
188 | } |
189 | |
190 | // `get_cpu_frequency` is very slow, so better run it in parallel. |
191 | self.global_cpu.frequency = iter_mut(&mut self.cpus) |
192 | .enumerate() |
193 | .map(|(pos, proc_)| { |
194 | proc_.frequency = get_cpu_frequency(pos); |
195 | proc_.frequency |
196 | }) |
197 | .max() |
198 | .unwrap_or(0); |
199 | |
200 | self.got_cpu_frequency = true; |
201 | } |
202 | |
203 | if first { |
204 | self.global_cpu.vendor_id = vendor_id; |
205 | self.global_cpu.brand = brand; |
206 | } |
207 | } |
208 | |
209 | pub(crate) fn get_global_raw_times(&self) -> (u64, u64) { |
210 | (self.global_cpu.total_time, self.global_cpu.old_total_time) |
211 | } |
212 | |
213 | pub(crate) fn len(&self) -> usize { |
214 | self.cpus.len() |
215 | } |
216 | |
217 | pub(crate) fn is_empty(&self) -> bool { |
218 | self.cpus.is_empty() |
219 | } |
220 | |
221 | pub(crate) fn set_need_cpus_update(&mut self) { |
222 | self.need_cpus_update = true; |
223 | } |
224 | } |
225 | |
226 | /// Struct containing values to compute a CPU usage. |
227 | #[derive (Clone, Copy)] |
228 | pub(crate) struct CpuValues { |
229 | user: u64, |
230 | nice: u64, |
231 | system: u64, |
232 | idle: u64, |
233 | iowait: u64, |
234 | irq: u64, |
235 | softirq: u64, |
236 | steal: u64, |
237 | guest: u64, |
238 | guest_nice: u64, |
239 | } |
240 | |
241 | impl CpuValues { |
242 | /// Creates a new instance of `CpuValues` with everything set to `0`. |
243 | pub fn new() -> CpuValues { |
244 | CpuValues { |
245 | user: 0, |
246 | nice: 0, |
247 | system: 0, |
248 | idle: 0, |
249 | iowait: 0, |
250 | irq: 0, |
251 | softirq: 0, |
252 | steal: 0, |
253 | guest: 0, |
254 | guest_nice: 0, |
255 | } |
256 | } |
257 | |
258 | /// Sets the given argument to the corresponding fields. |
259 | pub fn set( |
260 | &mut self, |
261 | user: u64, |
262 | nice: u64, |
263 | system: u64, |
264 | idle: u64, |
265 | iowait: u64, |
266 | irq: u64, |
267 | softirq: u64, |
268 | steal: u64, |
269 | guest: u64, |
270 | guest_nice: u64, |
271 | ) { |
272 | // `guest` is already accounted in `user`. |
273 | self.user = user.saturating_sub(guest); |
274 | // `guest_nice` is already accounted in `nice`. |
275 | self.nice = nice.saturating_sub(guest_nice); |
276 | self.system = system; |
277 | self.idle = idle; |
278 | self.iowait = iowait; |
279 | self.irq = irq; |
280 | self.softirq = softirq; |
281 | self.steal = steal; |
282 | self.guest = guest; |
283 | self.guest_nice = guest_nice; |
284 | } |
285 | |
286 | /// Returns work time. |
287 | pub fn work_time(&self) -> u64 { |
288 | self.user |
289 | .saturating_add(self.nice) |
290 | .saturating_add(self.system) |
291 | .saturating_add(self.irq) |
292 | .saturating_add(self.softirq) |
293 | } |
294 | |
295 | /// Returns total time. |
296 | pub fn total_time(&self) -> u64 { |
297 | self.work_time() |
298 | .saturating_add(self.idle) |
299 | .saturating_add(self.iowait) |
300 | // `steal`, `guest` and `guest_nice` are only used if we want to account the "guest" |
301 | // into the computation. |
302 | .saturating_add(self.guest) |
303 | .saturating_add(self.guest_nice) |
304 | .saturating_add(self.steal) |
305 | } |
306 | } |
307 | |
308 | #[doc = include_str!("../../md_doc/cpu.md" )] |
309 | pub struct Cpu { |
310 | old_values: CpuValues, |
311 | new_values: CpuValues, |
312 | pub(crate) name: String, |
313 | cpu_usage: f32, |
314 | total_time: u64, |
315 | old_total_time: u64, |
316 | pub(crate) frequency: u64, |
317 | pub(crate) vendor_id: String, |
318 | pub(crate) brand: String, |
319 | } |
320 | |
321 | impl Cpu { |
322 | pub(crate) fn new_with_values( |
323 | name: &str, |
324 | user: u64, |
325 | nice: u64, |
326 | system: u64, |
327 | idle: u64, |
328 | iowait: u64, |
329 | irq: u64, |
330 | softirq: u64, |
331 | steal: u64, |
332 | guest: u64, |
333 | guest_nice: u64, |
334 | frequency: u64, |
335 | vendor_id: String, |
336 | brand: String, |
337 | ) -> Cpu { |
338 | let mut new_values = CpuValues::new(); |
339 | new_values.set( |
340 | user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice, |
341 | ); |
342 | Cpu { |
343 | name: name.to_owned(), |
344 | old_values: CpuValues::new(), |
345 | new_values, |
346 | cpu_usage: 0f32, |
347 | total_time: 0, |
348 | old_total_time: 0, |
349 | frequency, |
350 | vendor_id, |
351 | brand, |
352 | } |
353 | } |
354 | |
355 | pub(crate) fn set( |
356 | &mut self, |
357 | user: u64, |
358 | nice: u64, |
359 | system: u64, |
360 | idle: u64, |
361 | iowait: u64, |
362 | irq: u64, |
363 | softirq: u64, |
364 | steal: u64, |
365 | guest: u64, |
366 | guest_nice: u64, |
367 | ) { |
368 | macro_rules! min { |
369 | ($a:expr, $b:expr, $def:expr) => { |
370 | if $a > $b { |
371 | ($a - $b) as f32 |
372 | } else { |
373 | $def |
374 | } |
375 | }; |
376 | } |
377 | self.old_values = self.new_values; |
378 | self.new_values.set( |
379 | user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice, |
380 | ); |
381 | self.total_time = self.new_values.total_time(); |
382 | self.old_total_time = self.old_values.total_time(); |
383 | self.cpu_usage = min!(self.new_values.work_time(), self.old_values.work_time(), 0.) |
384 | / min!(self.total_time, self.old_total_time, 1.) |
385 | * 100.; |
386 | if self.cpu_usage > 100. { |
387 | self.cpu_usage = 100.; // to prevent the percentage to go above 100% |
388 | } |
389 | } |
390 | } |
391 | |
392 | impl CpuExt for Cpu { |
393 | fn cpu_usage(&self) -> f32 { |
394 | self.cpu_usage |
395 | } |
396 | |
397 | fn name(&self) -> &str { |
398 | &self.name |
399 | } |
400 | |
401 | /// Returns the CPU frequency in MHz. |
402 | fn frequency(&self) -> u64 { |
403 | self.frequency |
404 | } |
405 | |
406 | fn vendor_id(&self) -> &str { |
407 | &self.vendor_id |
408 | } |
409 | |
410 | fn brand(&self) -> &str { |
411 | &self.brand |
412 | } |
413 | } |
414 | |
415 | pub(crate) fn get_cpu_frequency(cpu_core_index: usize) -> u64 { |
416 | let mut s = String::new(); |
417 | if File::open(format!( |
418 | "/sys/devices/system/cpu/cpu {cpu_core_index}/cpufreq/scaling_cur_freq" , |
419 | )) |
420 | .and_then(|mut f| f.read_to_string(&mut s)) |
421 | .is_ok() |
422 | { |
423 | let freq_option = s.trim().split(' \n' ).next(); |
424 | if let Some(freq_string) = freq_option { |
425 | if let Ok(freq) = freq_string.parse::<u64>() { |
426 | return freq / 1000; |
427 | } |
428 | } |
429 | } |
430 | s.clear(); |
431 | if File::open("/proc/cpuinfo" ) |
432 | .and_then(|mut f| f.read_to_string(&mut s)) |
433 | .is_err() |
434 | { |
435 | return 0; |
436 | } |
437 | let find_cpu_mhz = s.split(' \n' ).find(|line| { |
438 | line.starts_with("cpu MHz \t" ) |
439 | || line.starts_with("BogoMIPS" ) |
440 | || line.starts_with("clock \t" ) |
441 | || line.starts_with("bogomips per cpu" ) |
442 | }); |
443 | find_cpu_mhz |
444 | .and_then(|line| line.split(':' ).last()) |
445 | .and_then(|val| val.replace("MHz" , "" ).trim().parse::<f64>().ok()) |
446 | .map(|speed| speed as u64) |
447 | .unwrap_or_default() |
448 | } |
449 | |
450 | #[allow (unused_assignments)] |
451 | pub(crate) fn get_physical_core_count() -> Option<usize> { |
452 | let mut s = String::new(); |
453 | if let Err(_e) = File::open("/proc/cpuinfo" ).and_then(|mut f| f.read_to_string(&mut s)) { |
454 | sysinfo_debug!("Cannot read `/proc/cpuinfo` file: {:?}" , _e); |
455 | return None; |
456 | } |
457 | |
458 | macro_rules! add_core { |
459 | ($core_ids_and_physical_ids:ident, $core_id:ident, $physical_id:ident, $cpu:ident) => {{ |
460 | if !$core_id.is_empty() && !$physical_id.is_empty() { |
461 | $core_ids_and_physical_ids.insert(format!("{} {}" , $core_id, $physical_id)); |
462 | } else if !$cpu.is_empty() { |
463 | // On systems with only physical cores like raspberry, there is no "core id" or |
464 | // "physical id" fields. So if one of them is missing, we simply use the "CPU" |
465 | // info and count it as a physical core. |
466 | $core_ids_and_physical_ids.insert($cpu.to_owned()); |
467 | } |
468 | $core_id = "" ; |
469 | $physical_id = "" ; |
470 | $cpu = "" ; |
471 | }}; |
472 | } |
473 | |
474 | let mut core_ids_and_physical_ids: HashSet<String> = HashSet::new(); |
475 | let mut core_id = "" ; |
476 | let mut physical_id = "" ; |
477 | let mut cpu = "" ; |
478 | |
479 | for line in s.lines() { |
480 | if line.is_empty() { |
481 | add_core!(core_ids_and_physical_ids, core_id, physical_id, cpu); |
482 | } else if line.starts_with("processor" ) { |
483 | cpu = line |
484 | .splitn(2, ':' ) |
485 | .last() |
486 | .map(|x| x.trim()) |
487 | .unwrap_or_default(); |
488 | } else if line.starts_with("core id" ) { |
489 | core_id = line |
490 | .splitn(2, ':' ) |
491 | .last() |
492 | .map(|x| x.trim()) |
493 | .unwrap_or_default(); |
494 | } else if line.starts_with("physical id" ) { |
495 | physical_id = line |
496 | .splitn(2, ':' ) |
497 | .last() |
498 | .map(|x| x.trim()) |
499 | .unwrap_or_default(); |
500 | } |
501 | } |
502 | add_core!(core_ids_and_physical_ids, core_id, physical_id, cpu); |
503 | |
504 | Some(core_ids_and_physical_ids.len()) |
505 | } |
506 | |
507 | /// Obtain the implementer of this CPU core. |
508 | /// |
509 | /// This has been obtained from util-linux's lscpu implementation, see |
510 | /// https://github.com/util-linux/util-linux/blob/7076703b529d255600631306419cca1b48ab850a/sys-utils/lscpu-arm.c#L240 |
511 | /// |
512 | /// This list will have to be updated every time a new vendor appears, please keep it synchronized |
513 | /// with util-linux and update the link above with the commit you have used. |
514 | fn get_arm_implementer(implementer: u32) -> Option<&'static str> { |
515 | Some(match implementer { |
516 | 0x41 => "ARM" , |
517 | 0x42 => "Broadcom" , |
518 | 0x43 => "Cavium" , |
519 | 0x44 => "DEC" , |
520 | 0x46 => "FUJITSU" , |
521 | 0x48 => "HiSilicon" , |
522 | 0x49 => "Infineon" , |
523 | 0x4d => "Motorola/Freescale" , |
524 | 0x4e => "NVIDIA" , |
525 | 0x50 => "APM" , |
526 | 0x51 => "Qualcomm" , |
527 | 0x53 => "Samsung" , |
528 | 0x56 => "Marvell" , |
529 | 0x61 => "Apple" , |
530 | 0x66 => "Faraday" , |
531 | 0x69 => "Intel" , |
532 | 0x70 => "Phytium" , |
533 | 0xc0 => "Ampere" , |
534 | _ => return None, |
535 | }) |
536 | } |
537 | |
538 | /// Obtain the part of this CPU core. |
539 | /// |
540 | /// This has been obtained from util-linux's lscpu implementation, see |
541 | /// https://github.com/util-linux/util-linux/blob/7076703b529d255600631306419cca1b48ab850a/sys-utils/lscpu-arm.c#L34 |
542 | /// |
543 | /// This list will have to be updated every time a new core appears, please keep it synchronized |
544 | /// with util-linux and update the link above with the commit you have used. |
545 | fn get_arm_part(implementer: u32, part: u32) -> Option<&'static str> { |
546 | Some(match (implementer, part) { |
547 | // ARM |
548 | (0x41, 0x810) => "ARM810" , |
549 | (0x41, 0x920) => "ARM920" , |
550 | (0x41, 0x922) => "ARM922" , |
551 | (0x41, 0x926) => "ARM926" , |
552 | (0x41, 0x940) => "ARM940" , |
553 | (0x41, 0x946) => "ARM946" , |
554 | (0x41, 0x966) => "ARM966" , |
555 | (0x41, 0xa20) => "ARM1020" , |
556 | (0x41, 0xa22) => "ARM1022" , |
557 | (0x41, 0xa26) => "ARM1026" , |
558 | (0x41, 0xb02) => "ARM11 MPCore" , |
559 | (0x41, 0xb36) => "ARM1136" , |
560 | (0x41, 0xb56) => "ARM1156" , |
561 | (0x41, 0xb76) => "ARM1176" , |
562 | (0x41, 0xc05) => "Cortex-A5" , |
563 | (0x41, 0xc07) => "Cortex-A7" , |
564 | (0x41, 0xc08) => "Cortex-A8" , |
565 | (0x41, 0xc09) => "Cortex-A9" , |
566 | (0x41, 0xc0d) => "Cortex-A17" , // Originally A12 |
567 | (0x41, 0xc0f) => "Cortex-A15" , |
568 | (0x41, 0xc0e) => "Cortex-A17" , |
569 | (0x41, 0xc14) => "Cortex-R4" , |
570 | (0x41, 0xc15) => "Cortex-R5" , |
571 | (0x41, 0xc17) => "Cortex-R7" , |
572 | (0x41, 0xc18) => "Cortex-R8" , |
573 | (0x41, 0xc20) => "Cortex-M0" , |
574 | (0x41, 0xc21) => "Cortex-M1" , |
575 | (0x41, 0xc23) => "Cortex-M3" , |
576 | (0x41, 0xc24) => "Cortex-M4" , |
577 | (0x41, 0xc27) => "Cortex-M7" , |
578 | (0x41, 0xc60) => "Cortex-M0+" , |
579 | (0x41, 0xd01) => "Cortex-A32" , |
580 | (0x41, 0xd02) => "Cortex-A34" , |
581 | (0x41, 0xd03) => "Cortex-A53" , |
582 | (0x41, 0xd04) => "Cortex-A35" , |
583 | (0x41, 0xd05) => "Cortex-A55" , |
584 | (0x41, 0xd06) => "Cortex-A65" , |
585 | (0x41, 0xd07) => "Cortex-A57" , |
586 | (0x41, 0xd08) => "Cortex-A72" , |
587 | (0x41, 0xd09) => "Cortex-A73" , |
588 | (0x41, 0xd0a) => "Cortex-A75" , |
589 | (0x41, 0xd0b) => "Cortex-A76" , |
590 | (0x41, 0xd0c) => "Neoverse-N1" , |
591 | (0x41, 0xd0d) => "Cortex-A77" , |
592 | (0x41, 0xd0e) => "Cortex-A76AE" , |
593 | (0x41, 0xd13) => "Cortex-R52" , |
594 | (0x41, 0xd20) => "Cortex-M23" , |
595 | (0x41, 0xd21) => "Cortex-M33" , |
596 | (0x41, 0xd40) => "Neoverse-V1" , |
597 | (0x41, 0xd41) => "Cortex-A78" , |
598 | (0x41, 0xd42) => "Cortex-A78AE" , |
599 | (0x41, 0xd43) => "Cortex-A65AE" , |
600 | (0x41, 0xd44) => "Cortex-X1" , |
601 | (0x41, 0xd46) => "Cortex-A510" , |
602 | (0x41, 0xd47) => "Cortex-A710" , |
603 | (0x41, 0xd48) => "Cortex-X2" , |
604 | (0x41, 0xd49) => "Neoverse-N2" , |
605 | (0x41, 0xd4a) => "Neoverse-E1" , |
606 | (0x41, 0xd4b) => "Cortex-A78C" , |
607 | (0x41, 0xd4c) => "Cortex-X1C" , |
608 | (0x41, 0xd4d) => "Cortex-A715" , |
609 | (0x41, 0xd4e) => "Cortex-X3" , |
610 | |
611 | // Broadcom |
612 | (0x42, 0x00f) => "Brahma-B15" , |
613 | (0x42, 0x100) => "Brahma-B53" , |
614 | (0x42, 0x516) => "ThunderX2" , |
615 | |
616 | // Cavium |
617 | (0x43, 0x0a0) => "ThunderX" , |
618 | (0x43, 0x0a1) => "ThunderX-88XX" , |
619 | (0x43, 0x0a2) => "ThunderX-81XX" , |
620 | (0x43, 0x0a3) => "ThunderX-83XX" , |
621 | (0x43, 0x0af) => "ThunderX2-99xx" , |
622 | |
623 | // DEC |
624 | (0x44, 0xa10) => "SA110" , |
625 | (0x44, 0xa11) => "SA1100" , |
626 | |
627 | // Fujitsu |
628 | (0x46, 0x001) => "A64FX" , |
629 | |
630 | // HiSilicon |
631 | (0x48, 0xd01) => "Kunpeng-920" , // aka tsv110 |
632 | |
633 | // NVIDIA |
634 | (0x4e, 0x000) => "Denver" , |
635 | (0x4e, 0x003) => "Denver 2" , |
636 | (0x4e, 0x004) => "Carmel" , |
637 | |
638 | // APM |
639 | (0x50, 0x000) => "X-Gene" , |
640 | |
641 | // Qualcomm |
642 | (0x51, 0x00f) => "Scorpion" , |
643 | (0x51, 0x02d) => "Scorpion" , |
644 | (0x51, 0x04d) => "Krait" , |
645 | (0x51, 0x06f) => "Krait" , |
646 | (0x51, 0x201) => "Kryo" , |
647 | (0x51, 0x205) => "Kryo" , |
648 | (0x51, 0x211) => "Kryo" , |
649 | (0x51, 0x800) => "Falkor-V1/Kryo" , |
650 | (0x51, 0x801) => "Kryo-V2" , |
651 | (0x51, 0x802) => "Kryo-3XX-Gold" , |
652 | (0x51, 0x803) => "Kryo-3XX-Silver" , |
653 | (0x51, 0x804) => "Kryo-4XX-Gold" , |
654 | (0x51, 0x805) => "Kryo-4XX-Silver" , |
655 | (0x51, 0xc00) => "Falkor" , |
656 | (0x51, 0xc01) => "Saphira" , |
657 | |
658 | // Samsung |
659 | (0x53, 0x001) => "exynos-m1" , |
660 | |
661 | // Marvell |
662 | (0x56, 0x131) => "Feroceon-88FR131" , |
663 | (0x56, 0x581) => "PJ4/PJ4b" , |
664 | (0x56, 0x584) => "PJ4B-MP" , |
665 | |
666 | // Apple |
667 | (0x61, 0x020) => "Icestorm-A14" , |
668 | (0x61, 0x021) => "Firestorm-A14" , |
669 | (0x61, 0x022) => "Icestorm-M1" , |
670 | (0x61, 0x023) => "Firestorm-M1" , |
671 | (0x61, 0x024) => "Icestorm-M1-Pro" , |
672 | (0x61, 0x025) => "Firestorm-M1-Pro" , |
673 | (0x61, 0x028) => "Icestorm-M1-Max" , |
674 | (0x61, 0x029) => "Firestorm-M1-Max" , |
675 | (0x61, 0x030) => "Blizzard-A15" , |
676 | (0x61, 0x031) => "Avalanche-A15" , |
677 | (0x61, 0x032) => "Blizzard-M2" , |
678 | (0x61, 0x033) => "Avalanche-M2" , |
679 | |
680 | // Faraday |
681 | (0x66, 0x526) => "FA526" , |
682 | (0x66, 0x626) => "FA626" , |
683 | |
684 | // Intel |
685 | (0x69, 0x200) => "i80200" , |
686 | (0x69, 0x210) => "PXA250A" , |
687 | (0x69, 0x212) => "PXA210A" , |
688 | (0x69, 0x242) => "i80321-400" , |
689 | (0x69, 0x243) => "i80321-600" , |
690 | (0x69, 0x290) => "PXA250B/PXA26x" , |
691 | (0x69, 0x292) => "PXA210B" , |
692 | (0x69, 0x2c2) => "i80321-400-B0" , |
693 | (0x69, 0x2c3) => "i80321-600-B0" , |
694 | (0x69, 0x2d0) => "PXA250C/PXA255/PXA26x" , |
695 | (0x69, 0x2d2) => "PXA210C" , |
696 | (0x69, 0x411) => "PXA27x" , |
697 | (0x69, 0x41c) => "IPX425-533" , |
698 | (0x69, 0x41d) => "IPX425-400" , |
699 | (0x69, 0x41f) => "IPX425-266" , |
700 | (0x69, 0x682) => "PXA32x" , |
701 | (0x69, 0x683) => "PXA930/PXA935" , |
702 | (0x69, 0x688) => "PXA30x" , |
703 | (0x69, 0x689) => "PXA31x" , |
704 | (0x69, 0xb11) => "SA1110" , |
705 | (0x69, 0xc12) => "IPX1200" , |
706 | |
707 | // Phytium |
708 | (0x70, 0x660) => "FTC660" , |
709 | (0x70, 0x661) => "FTC661" , |
710 | (0x70, 0x662) => "FTC662" , |
711 | (0x70, 0x663) => "FTC663" , |
712 | |
713 | _ => return None, |
714 | }) |
715 | } |
716 | |
717 | /// Returns the brand/vendor string for the first CPU (which should be the same for all CPUs). |
718 | pub(crate) fn get_vendor_id_and_brand() -> (String, String) { |
719 | let mut s = String::new(); |
720 | if File::open("/proc/cpuinfo" ) |
721 | .and_then(|mut f| f.read_to_string(&mut s)) |
722 | .is_err() |
723 | { |
724 | return (String::new(), String::new()); |
725 | } |
726 | |
727 | fn get_value(s: &str) -> String { |
728 | s.split(':' ) |
729 | .last() |
730 | .map(|x| x.trim().to_owned()) |
731 | .unwrap_or_default() |
732 | } |
733 | |
734 | fn get_hex_value(s: &str) -> u32 { |
735 | s.split(':' ) |
736 | .last() |
737 | .map(|x| x.trim()) |
738 | .filter(|x| x.starts_with("0x" )) |
739 | .map(|x| u32::from_str_radix(&x[2..], 16).unwrap()) |
740 | .unwrap_or_default() |
741 | } |
742 | |
743 | let mut vendor_id = None; |
744 | let mut brand = None; |
745 | let mut implementer = None; |
746 | let mut part = None; |
747 | |
748 | for it in s.split(' \n' ) { |
749 | if it.starts_with("vendor_id \t" ) { |
750 | vendor_id = Some(get_value(it)); |
751 | } else if it.starts_with("model name \t" ) { |
752 | brand = Some(get_value(it)); |
753 | } else if it.starts_with("CPU implementer \t" ) { |
754 | implementer = Some(get_hex_value(it)); |
755 | } else if it.starts_with("CPU part \t" ) { |
756 | part = Some(get_hex_value(it)); |
757 | } else { |
758 | continue; |
759 | } |
760 | if (brand.is_some() && vendor_id.is_some()) || (implementer.is_some() && part.is_some()) { |
761 | break; |
762 | } |
763 | } |
764 | if let (Some(implementer), Some(part)) = (implementer, part) { |
765 | vendor_id = get_arm_implementer(implementer).map(String::from); |
766 | // It's possible to "model name" even with an ARM CPU, so just in case we can't retrieve |
767 | // the brand from "CPU part", we will then use the value from "model name". |
768 | // |
769 | // Example from raspberry pi 3B+: |
770 | // |
771 | // ``` |
772 | // model name : ARMv7 Processor rev 4 (v7l) |
773 | // CPU implementer : 0x41 |
774 | // CPU part : 0xd03 |
775 | // ``` |
776 | brand = get_arm_part(implementer, part).map(String::from).or(brand); |
777 | } |
778 | (vendor_id.unwrap_or_default(), brand.unwrap_or_default()) |
779 | } |
780 | |