summaryrefslogtreecommitdiffstats
path: root/vendor/sysinfo/src/linux/cpu.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/sysinfo/src/linux/cpu.rs')
-rw-r--r--vendor/sysinfo/src/linux/cpu.rs474
1 files changed, 350 insertions, 124 deletions
diff --git a/vendor/sysinfo/src/linux/cpu.rs b/vendor/sysinfo/src/linux/cpu.rs
index 103f5362a..d99b325c1 100644
--- a/vendor/sysinfo/src/linux/cpu.rs
+++ b/vendor/sysinfo/src/linux/cpu.rs
@@ -5,9 +5,10 @@
use std::collections::HashSet;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
+use std::time::Instant;
use crate::sys::utils::to_u64;
-use crate::{CpuExt, CpuRefreshKind};
+use crate::{CpuExt, CpuRefreshKind, SystemExt};
macro_rules! to_str {
($e:expr) => {
@@ -24,6 +25,8 @@ pub(crate) struct CpusWrapper {
/// For example when running `refresh_all` or `refresh_specifics`.
need_cpus_update: bool,
got_cpu_frequency: bool,
+ /// This field is needed to prevent updating when not enough time passed since last update.
+ last_update: Option<Instant>,
}
impl CpusWrapper {
@@ -48,6 +51,7 @@ impl CpusWrapper {
cpus: Vec::with_capacity(4),
need_cpus_update: true,
got_cpu_frequency: false,
+ last_update: None,
}
}
@@ -62,90 +66,101 @@ impl CpusWrapper {
}
pub(crate) fn refresh(&mut self, only_update_global_cpu: bool, refresh_kind: CpuRefreshKind) {
- let f = match File::open("/proc/stat") {
- Ok(f) => f,
- Err(_e) => {
- sysinfo_debug!("failed to retrieve CPU information: {:?}", _e);
- return;
- }
- };
- let buf = BufReader::new(f);
+ let need_cpu_usage_update = self
+ .last_update
+ .map(|last_update| last_update.elapsed() > crate::System::MINIMUM_CPU_UPDATE_INTERVAL)
+ .unwrap_or(true);
- self.need_cpus_update = false;
- let mut i: usize = 0;
let first = self.cpus.is_empty();
- let mut it = buf.split(b'\n');
let (vendor_id, brand) = if first {
get_vendor_id_and_brand()
} else {
(String::new(), String::new())
};
- if first || refresh_kind.cpu_usage() {
- if let Some(Ok(line)) = it.next() {
- if &line[..4] != b"cpu " {
+ // If the last CPU usage update is too close (less than `MINIMUM_CPU_UPDATE_INTERVAL`),
+ // we don't want to update CPUs times.
+ if need_cpu_usage_update {
+ self.last_update = Some(Instant::now());
+ let f = match File::open("/proc/stat") {
+ Ok(f) => f,
+ Err(_e) => {
+ sysinfo_debug!("failed to retrieve CPU information: {:?}", _e);
return;
}
- let mut parts = line.split(|x| *x == b' ').filter(|s| !s.is_empty());
- if first {
- self.global_cpu.name = to_str!(parts.next().unwrap_or(&[])).to_owned();
- } else {
- parts.next();
- }
- self.global_cpu.set(
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- );
- }
- if first || !only_update_global_cpu {
- while let Some(Ok(line)) = it.next() {
- if &line[..3] != b"cpu" {
- break;
- }
+ };
+ let buf = BufReader::new(f);
+
+ self.need_cpus_update = false;
+ let mut i: usize = 0;
+ let mut it = buf.split(b'\n');
+ if first || refresh_kind.cpu_usage() {
+ if let Some(Ok(line)) = it.next() {
+ if &line[..4] != b"cpu " {
+ return;
+ }
let mut parts = line.split(|x| *x == b' ').filter(|s| !s.is_empty());
if first {
- self.cpus.push(Cpu::new_with_values(
- to_str!(parts.next().unwrap_or(&[])),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- 0,
- vendor_id.clone(),
- brand.clone(),
- ));
+ self.global_cpu.name = to_str!(parts.next().unwrap_or(&[])).to_owned();
} else {
- parts.next(); // we don't want the name again
- self.cpus[i].set(
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- parts.next().map(to_u64).unwrap_or(0),
- );
+ parts.next();
+ }
+ self.global_cpu.set(
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ );
+ }
+ if first || !only_update_global_cpu {
+ while let Some(Ok(line)) = it.next() {
+ if &line[..3] != b"cpu" {
+ break;
+ }
+
+ let mut parts = line.split(|x| *x == b' ').filter(|s| !s.is_empty());
+ if first {
+ self.cpus.push(Cpu::new_with_values(
+ to_str!(parts.next().unwrap_or(&[])),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ 0,
+ vendor_id.clone(),
+ brand.clone(),
+ ));
+ } else {
+ parts.next(); // we don't want the name again
+ self.cpus[i].set(
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ parts.next().map(to_u64).unwrap_or(0),
+ );
+ }
+
+ i += 1;
}
-
- i += 1;
}
}
}
@@ -219,8 +234,8 @@ pub(crate) struct CpuValues {
irq: u64,
softirq: u64,
steal: u64,
- _guest: u64,
- _guest_nice: u64,
+ guest: u64,
+ guest_nice: u64,
}
impl CpuValues {
@@ -235,44 +250,11 @@ impl CpuValues {
irq: 0,
softirq: 0,
steal: 0,
- _guest: 0,
- _guest_nice: 0,
- }
- }
-
- /// Creates a new instance of `CpuValues` with everything set to the corresponding argument.
- pub fn new_with_values(
- user: u64,
- nice: u64,
- system: u64,
- idle: u64,
- iowait: u64,
- irq: u64,
- softirq: u64,
- steal: u64,
- guest: u64,
- guest_nice: u64,
- ) -> CpuValues {
- CpuValues {
- user,
- nice,
- system,
- idle,
- iowait,
- irq,
- softirq,
- steal,
- _guest: guest,
- _guest_nice: guest_nice,
+ guest: 0,
+ guest_nice: 0,
}
}
- /*pub fn is_zero(&self) -> bool {
- self.user == 0 && self.nice == 0 && self.system == 0 && self.idle == 0 &&
- self.iowait == 0 && self.irq == 0 && self.softirq == 0 && self.steal == 0 &&
- self.guest == 0 && self.guest_nice == 0
- }*/
-
/// Sets the given argument to the corresponding fields.
pub fn set(
&mut self,
@@ -287,16 +269,18 @@ impl CpuValues {
guest: u64,
guest_nice: u64,
) {
- self.user = user;
- self.nice = nice;
+ // `guest` is already accounted in `user`.
+ self.user = user.saturating_sub(guest);
+ // `guest_nice` is already accounted in `nice`.
+ self.nice = nice.saturating_sub(guest_nice);
self.system = system;
self.idle = idle;
self.iowait = iowait;
self.irq = irq;
self.softirq = softirq;
self.steal = steal;
- self._guest = guest;
- self._guest_nice = guest_nice;
+ self.guest = guest;
+ self.guest_nice = guest_nice;
}
/// Returns work time.
@@ -306,16 +290,18 @@ impl CpuValues {
.saturating_add(self.system)
.saturating_add(self.irq)
.saturating_add(self.softirq)
- .saturating_add(self.steal)
}
/// Returns total time.
pub fn total_time(&self) -> u64 {
- // `guest` is already included in `user`
- // `guest_nice` is already included in `nice`
self.work_time()
.saturating_add(self.idle)
.saturating_add(self.iowait)
+ // `steal`, `guest` and `guest_nice` are only used if we want to account the "guest"
+ // into the computation.
+ .saturating_add(self.guest)
+ .saturating_add(self.guest_nice)
+ .saturating_add(self.steal)
}
}
@@ -349,12 +335,14 @@ impl Cpu {
vendor_id: String,
brand: String,
) -> Cpu {
+ let mut new_values = CpuValues::new();
+ new_values.set(
+ user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice,
+ );
Cpu {
name: name.to_owned(),
old_values: CpuValues::new(),
- new_values: CpuValues::new_with_values(
- user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice,
- ),
+ new_values,
cpu_usage: 0f32,
total_time: 0,
old_total_time: 0,
@@ -378,11 +366,11 @@ impl Cpu {
guest_nice: u64,
) {
macro_rules! min {
- ($a:expr, $b:expr) => {
+ ($a:expr, $b:expr, $def:expr) => {
if $a > $b {
($a - $b) as f32
} else {
- 1.
+ $def
}
};
}
@@ -392,8 +380,8 @@ impl Cpu {
);
self.total_time = self.new_values.total_time();
self.old_total_time = self.old_values.total_time();
- self.cpu_usage = min!(self.new_values.work_time(), self.old_values.work_time())
- / min!(self.total_time, self.old_total_time)
+ self.cpu_usage = min!(self.new_values.work_time(), self.old_values.work_time(), 0.)
+ / min!(self.total_time, self.old_total_time, 1.)
* 100.;
if self.cpu_usage > 100. {
self.cpu_usage = 100.; // to prevent the percentage to go above 100%
@@ -427,8 +415,7 @@ impl CpuExt for Cpu {
pub(crate) fn get_cpu_frequency(cpu_core_index: usize) -> u64 {
let mut s = String::new();
if File::open(format!(
- "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq",
- cpu_core_index
+ "/sys/devices/system/cpu/cpu{cpu_core_index}/cpufreq/scaling_cur_freq",
))
.and_then(|mut f| f.read_to_string(&mut s))
.is_ok()
@@ -517,6 +504,216 @@ pub(crate) fn get_physical_core_count() -> Option<usize> {
Some(core_ids_and_physical_ids.len())
}
+/// Obtain the implementer of this CPU core.
+///
+/// This has been obtained from util-linux's lscpu implementation, see
+/// https://github.com/util-linux/util-linux/blob/7076703b529d255600631306419cca1b48ab850a/sys-utils/lscpu-arm.c#L240
+///
+/// This list will have to be updated every time a new vendor appears, please keep it synchronized
+/// with util-linux and update the link above with the commit you have used.
+fn get_arm_implementer(implementer: u32) -> Option<&'static str> {
+ Some(match implementer {
+ 0x41 => "ARM",
+ 0x42 => "Broadcom",
+ 0x43 => "Cavium",
+ 0x44 => "DEC",
+ 0x46 => "FUJITSU",
+ 0x48 => "HiSilicon",
+ 0x49 => "Infineon",
+ 0x4d => "Motorola/Freescale",
+ 0x4e => "NVIDIA",
+ 0x50 => "APM",
+ 0x51 => "Qualcomm",
+ 0x53 => "Samsung",
+ 0x56 => "Marvell",
+ 0x61 => "Apple",
+ 0x66 => "Faraday",
+ 0x69 => "Intel",
+ 0x70 => "Phytium",
+ 0xc0 => "Ampere",
+ _ => return None,
+ })
+}
+
+/// Obtain the part of this CPU core.
+///
+/// This has been obtained from util-linux's lscpu implementation, see
+/// https://github.com/util-linux/util-linux/blob/7076703b529d255600631306419cca1b48ab850a/sys-utils/lscpu-arm.c#L34
+///
+/// This list will have to be updated every time a new core appears, please keep it synchronized
+/// with util-linux and update the link above with the commit you have used.
+fn get_arm_part(implementer: u32, part: u32) -> Option<&'static str> {
+ Some(match (implementer, part) {
+ // ARM
+ (0x41, 0x810) => "ARM810",
+ (0x41, 0x920) => "ARM920",
+ (0x41, 0x922) => "ARM922",
+ (0x41, 0x926) => "ARM926",
+ (0x41, 0x940) => "ARM940",
+ (0x41, 0x946) => "ARM946",
+ (0x41, 0x966) => "ARM966",
+ (0x41, 0xa20) => "ARM1020",
+ (0x41, 0xa22) => "ARM1022",
+ (0x41, 0xa26) => "ARM1026",
+ (0x41, 0xb02) => "ARM11 MPCore",
+ (0x41, 0xb36) => "ARM1136",
+ (0x41, 0xb56) => "ARM1156",
+ (0x41, 0xb76) => "ARM1176",
+ (0x41, 0xc05) => "Cortex-A5",
+ (0x41, 0xc07) => "Cortex-A7",
+ (0x41, 0xc08) => "Cortex-A8",
+ (0x41, 0xc09) => "Cortex-A9",
+ (0x41, 0xc0d) => "Cortex-A17", // Originally A12
+ (0x41, 0xc0f) => "Cortex-A15",
+ (0x41, 0xc0e) => "Cortex-A17",
+ (0x41, 0xc14) => "Cortex-R4",
+ (0x41, 0xc15) => "Cortex-R5",
+ (0x41, 0xc17) => "Cortex-R7",
+ (0x41, 0xc18) => "Cortex-R8",
+ (0x41, 0xc20) => "Cortex-M0",
+ (0x41, 0xc21) => "Cortex-M1",
+ (0x41, 0xc23) => "Cortex-M3",
+ (0x41, 0xc24) => "Cortex-M4",
+ (0x41, 0xc27) => "Cortex-M7",
+ (0x41, 0xc60) => "Cortex-M0+",
+ (0x41, 0xd01) => "Cortex-A32",
+ (0x41, 0xd02) => "Cortex-A34",
+ (0x41, 0xd03) => "Cortex-A53",
+ (0x41, 0xd04) => "Cortex-A35",
+ (0x41, 0xd05) => "Cortex-A55",
+ (0x41, 0xd06) => "Cortex-A65",
+ (0x41, 0xd07) => "Cortex-A57",
+ (0x41, 0xd08) => "Cortex-A72",
+ (0x41, 0xd09) => "Cortex-A73",
+ (0x41, 0xd0a) => "Cortex-A75",
+ (0x41, 0xd0b) => "Cortex-A76",
+ (0x41, 0xd0c) => "Neoverse-N1",
+ (0x41, 0xd0d) => "Cortex-A77",
+ (0x41, 0xd0e) => "Cortex-A76AE",
+ (0x41, 0xd13) => "Cortex-R52",
+ (0x41, 0xd20) => "Cortex-M23",
+ (0x41, 0xd21) => "Cortex-M33",
+ (0x41, 0xd40) => "Neoverse-V1",
+ (0x41, 0xd41) => "Cortex-A78",
+ (0x41, 0xd42) => "Cortex-A78AE",
+ (0x41, 0xd43) => "Cortex-A65AE",
+ (0x41, 0xd44) => "Cortex-X1",
+ (0x41, 0xd46) => "Cortex-A510",
+ (0x41, 0xd47) => "Cortex-A710",
+ (0x41, 0xd48) => "Cortex-X2",
+ (0x41, 0xd49) => "Neoverse-N2",
+ (0x41, 0xd4a) => "Neoverse-E1",
+ (0x41, 0xd4b) => "Cortex-A78C",
+ (0x41, 0xd4c) => "Cortex-X1C",
+ (0x41, 0xd4d) => "Cortex-A715",
+ (0x41, 0xd4e) => "Cortex-X3",
+
+ // Broadcom
+ (0x42, 0x00f) => "Brahma-B15",
+ (0x42, 0x100) => "Brahma-B53",
+ (0x42, 0x516) => "ThunderX2",
+
+ // Cavium
+ (0x43, 0x0a0) => "ThunderX",
+ (0x43, 0x0a1) => "ThunderX-88XX",
+ (0x43, 0x0a2) => "ThunderX-81XX",
+ (0x43, 0x0a3) => "ThunderX-83XX",
+ (0x43, 0x0af) => "ThunderX2-99xx",
+
+ // DEC
+ (0x44, 0xa10) => "SA110",
+ (0x44, 0xa11) => "SA1100",
+
+ // Fujitsu
+ (0x46, 0x001) => "A64FX",
+
+ // HiSilicon
+ (0x48, 0xd01) => "Kunpeng-920", // aka tsv110
+
+ // NVIDIA
+ (0x4e, 0x000) => "Denver",
+ (0x4e, 0x003) => "Denver 2",
+ (0x4e, 0x004) => "Carmel",
+
+ // APM
+ (0x50, 0x000) => "X-Gene",
+
+ // Qualcomm
+ (0x51, 0x00f) => "Scorpion",
+ (0x51, 0x02d) => "Scorpion",
+ (0x51, 0x04d) => "Krait",
+ (0x51, 0x06f) => "Krait",
+ (0x51, 0x201) => "Kryo",
+ (0x51, 0x205) => "Kryo",
+ (0x51, 0x211) => "Kryo",
+ (0x51, 0x800) => "Falkor-V1/Kryo",
+ (0x51, 0x801) => "Kryo-V2",
+ (0x51, 0x802) => "Kryo-3XX-Gold",
+ (0x51, 0x803) => "Kryo-3XX-Silver",
+ (0x51, 0x804) => "Kryo-4XX-Gold",
+ (0x51, 0x805) => "Kryo-4XX-Silver",
+ (0x51, 0xc00) => "Falkor",
+ (0x51, 0xc01) => "Saphira",
+
+ // Samsung
+ (0x53, 0x001) => "exynos-m1",
+
+ // Marvell
+ (0x56, 0x131) => "Feroceon-88FR131",
+ (0x56, 0x581) => "PJ4/PJ4b",
+ (0x56, 0x584) => "PJ4B-MP",
+
+ // Apple
+ (0x61, 0x020) => "Icestorm-A14",
+ (0x61, 0x021) => "Firestorm-A14",
+ (0x61, 0x022) => "Icestorm-M1",
+ (0x61, 0x023) => "Firestorm-M1",
+ (0x61, 0x024) => "Icestorm-M1-Pro",
+ (0x61, 0x025) => "Firestorm-M1-Pro",
+ (0x61, 0x028) => "Icestorm-M1-Max",
+ (0x61, 0x029) => "Firestorm-M1-Max",
+ (0x61, 0x030) => "Blizzard-A15",
+ (0x61, 0x031) => "Avalanche-A15",
+ (0x61, 0x032) => "Blizzard-M2",
+ (0x61, 0x033) => "Avalanche-M2",
+
+ // Faraday
+ (0x66, 0x526) => "FA526",
+ (0x66, 0x626) => "FA626",
+
+ // Intel
+ (0x69, 0x200) => "i80200",
+ (0x69, 0x210) => "PXA250A",
+ (0x69, 0x212) => "PXA210A",
+ (0x69, 0x242) => "i80321-400",
+ (0x69, 0x243) => "i80321-600",
+ (0x69, 0x290) => "PXA250B/PXA26x",
+ (0x69, 0x292) => "PXA210B",
+ (0x69, 0x2c2) => "i80321-400-B0",
+ (0x69, 0x2c3) => "i80321-600-B0",
+ (0x69, 0x2d0) => "PXA250C/PXA255/PXA26x",
+ (0x69, 0x2d2) => "PXA210C",
+ (0x69, 0x411) => "PXA27x",
+ (0x69, 0x41c) => "IPX425-533",
+ (0x69, 0x41d) => "IPX425-400",
+ (0x69, 0x41f) => "IPX425-266",
+ (0x69, 0x682) => "PXA32x",
+ (0x69, 0x683) => "PXA930/PXA935",
+ (0x69, 0x688) => "PXA30x",
+ (0x69, 0x689) => "PXA31x",
+ (0x69, 0xb11) => "SA1110",
+ (0x69, 0xc12) => "IPX1200",
+
+ // Phytium
+ (0x70, 0x660) => "FTC660",
+ (0x70, 0x661) => "FTC661",
+ (0x70, 0x662) => "FTC662",
+ (0x70, 0x663) => "FTC663",
+
+ _ => return None,
+ })
+}
+
/// Returns the brand/vendor string for the first CPU (which should be the same for all CPUs).
pub(crate) fn get_vendor_id_and_brand() -> (String, String) {
let mut s = String::new();
@@ -534,20 +731,49 @@ pub(crate) fn get_vendor_id_and_brand() -> (String, String) {
.unwrap_or_default()
}
+ fn get_hex_value(s: &str) -> u32 {
+ s.split(':')
+ .last()
+ .map(|x| x.trim())
+ .filter(|x| x.starts_with("0x"))
+ .map(|x| u32::from_str_radix(&x[2..], 16).unwrap())
+ .unwrap_or_default()
+ }
+
let mut vendor_id = None;
let mut brand = None;
+ let mut implementer = None;
+ let mut part = None;
for it in s.split('\n') {
if it.starts_with("vendor_id\t") {
vendor_id = Some(get_value(it));
} else if it.starts_with("model name\t") {
brand = Some(get_value(it));
+ } else if it.starts_with("CPU implementer\t") {
+ implementer = Some(get_hex_value(it));
+ } else if it.starts_with("CPU part\t") {
+ part = Some(get_hex_value(it));
} else {
continue;
}
- if brand.is_some() && vendor_id.is_some() {
+ if (brand.is_some() && vendor_id.is_some()) || (implementer.is_some() && part.is_some()) {
break;
}
}
+ if let (Some(implementer), Some(part)) = (implementer, part) {
+ vendor_id = get_arm_implementer(implementer).map(String::from);
+ // It's possible to "model name" even with an ARM CPU, so just in case we can't retrieve
+ // the brand from "CPU part", we will then use the value from "model name".
+ //
+ // Example from raspberry pi 3B+:
+ //
+ // ```
+ // model name : ARMv7 Processor rev 4 (v7l)
+ // CPU implementer : 0x41
+ // CPU part : 0xd03
+ // ```
+ brand = get_arm_part(implementer, part).map(String::from).or(brand);
+ }
(vendor_id.unwrap_or_default(), brand.unwrap_or_default())
}