diff options
Diffstat (limited to 'vendor/sysinfo/src/linux/cpu.rs')
-rw-r--r-- | vendor/sysinfo/src/linux/cpu.rs | 474 |
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()) } |