mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-09 02:29:50 -10:00
Linux on ARM: name the CPU from its implementer and part
An arm64 kernel prints no model name in /proc/cpuinfo, only CPU implementer and CPU part, so telemetry sent an empty cpu, the benchmark report had no hardware.cpu (and the server then refused it), and the crash report said aarch64. linux_cpu_name reads model name, else maps the implementer and part as lscpu does (ARM Neoverse-N1, Apple, Ampere...), else the board's Model. The three readers share it. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
1 parent
975c07d915
commit
ce02c03296
3 files changed
+100
-11
No files matched your search
@@ -202,10 +202,8 @@ fn os_version() -> Option<String> {
|
||||
#[cfg(target_os = "linux")]
|
||||
fn cpu() -> Option<String> {
|
||||
let info = std::fs::read_to_string("/proc/cpuinfo").ok()?;
|
||||
// x86 names the CPU per core; ARM boards name the machine once.
|
||||
key_value(&info, "model name", ':')
|
||||
.or_else(|| key_value(&info, "Model", ':'))
|
||||
.map(str::to_owned)
|
||||
// x86 names the CPU per core; arm64 only gives the core's implementer and part.
|
||||
horizon_telemetry::system::linux_cpu_name(&info)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
|
||||
@@ -227,12 +227,7 @@ fn operating_system() -> String {
|
||||
pub fn cpu() -> String {
|
||||
let threads = std::thread::available_parallelism().map_or(0, |n| n.get());
|
||||
let name = if cfg!(target_os = "linux") {
|
||||
read("/proc/cpuinfo").and_then(|t| {
|
||||
t.lines()
|
||||
.find(|l| l.starts_with("model name"))
|
||||
.and_then(|l| l.split_once(':'))
|
||||
.map(|(_, v)| v.trim().to_owned())
|
||||
})
|
||||
read("/proc/cpuinfo").and_then(|t| horizon_telemetry::system::linux_cpu_name(&t))
|
||||
} else if cfg!(target_os = "macos") {
|
||||
command_output("sysctl", &["-n", "machdep.cpu.brand_string"])
|
||||
} else if cfg!(windows) {
|
||||
|
||||
@@ -64,6 +64,73 @@ fn number(text: &str) -> Option<u32> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The CPU's name from a Linux `/proc/cpuinfo`: x86's `model name`; else an ARM core named by
|
||||
/// its implementer and part (an arm64 kernel prints only `CPU implementer: 0x41` and `CPU part:
|
||||
/// 0xd0c`, as `lscpu` maps them: `ARM Neoverse-N1`); else the board's `Model` (a Raspberry Pi's
|
||||
/// downstream kernel).
|
||||
pub fn linux_cpu_name(cpuinfo: &str) -> Option<String> {
|
||||
if let Some(name) = field(cpuinfo, "model name").filter(|n| !n.is_empty()) {
|
||||
return Some(name);
|
||||
}
|
||||
let hex = |key: &str| {
|
||||
let text = field(cpuinfo, key)?;
|
||||
u32::from_str_radix(text.strip_prefix("0x")?, 16).ok()
|
||||
};
|
||||
if let (Some(implementer), Some(part)) = (hex("CPU implementer"), hex("CPU part")) {
|
||||
return Some(arm_core_name(implementer, part));
|
||||
}
|
||||
field(cpuinfo, "Model").filter(|n| !n.is_empty())
|
||||
}
|
||||
|
||||
/// `lscpu`'s `hw_implementer` and `arm_part` tables, for the cores that run Horizon.
|
||||
fn arm_core_name(implementer: u32, part: u32) -> String {
|
||||
let (vendor, core) = match (implementer, part) {
|
||||
(0x41, 0xd01) => ("ARM", "Cortex-A32"),
|
||||
(0x41, 0xd03) => ("ARM", "Cortex-A53"),
|
||||
(0x41, 0xd04) => ("ARM", "Cortex-A35"),
|
||||
(0x41, 0xd05) => ("ARM", "Cortex-A55"),
|
||||
(0x41, 0xd07) => ("ARM", "Cortex-A57"),
|
||||
(0x41, 0xd08) => ("ARM", "Cortex-A72"),
|
||||
(0x41, 0xd09) => ("ARM", "Cortex-A73"),
|
||||
(0x41, 0xd0a) => ("ARM", "Cortex-A75"),
|
||||
(0x41, 0xd0b) => ("ARM", "Cortex-A76"),
|
||||
(0x41, 0xd0c) => ("ARM", "Neoverse-N1"),
|
||||
(0x41, 0xd0d) => ("ARM", "Cortex-A77"),
|
||||
(0x41, 0xd40) => ("ARM", "Neoverse-V1"),
|
||||
(0x41, 0xd41) => ("ARM", "Cortex-A78"),
|
||||
(0x41, 0xd44) => ("ARM", "Cortex-X1"),
|
||||
(0x41, 0xd46) => ("ARM", "Cortex-A510"),
|
||||
(0x41, 0xd47) => ("ARM", "Cortex-A710"),
|
||||
(0x41, 0xd48) => ("ARM", "Cortex-X2"),
|
||||
(0x41, 0xd49) => ("ARM", "Neoverse-N2"),
|
||||
(0x41, 0xd4d) => ("ARM", "Cortex-A715"),
|
||||
(0x41, 0xd4e) => ("ARM", "Cortex-X3"),
|
||||
(0x41, 0xd4f) => ("ARM", "Neoverse-V2"),
|
||||
(0x41, 0xd80) => ("ARM", "Cortex-A520"),
|
||||
(0x41, 0xd81) => ("ARM", "Cortex-A720"),
|
||||
(0x41, 0xd82) => ("ARM", "Cortex-X4"),
|
||||
(0x41, 0xd84) => ("ARM", "Neoverse-V3"),
|
||||
(0x41, 0xd8e) => ("ARM", "Neoverse-N3"),
|
||||
(0x41, _) => ("ARM", ""),
|
||||
(0x42, _) => ("Broadcom", ""),
|
||||
(0x43, _) => ("Cavium", ""),
|
||||
(0x46, _) => ("Fujitsu", ""),
|
||||
(0x48, _) => ("HiSilicon", ""),
|
||||
(0x4e, _) => ("NVIDIA", ""),
|
||||
(0x50, _) => ("APM", ""),
|
||||
(0x51, _) => ("Qualcomm", ""),
|
||||
(0x53, _) => ("Samsung", ""),
|
||||
(0x61, _) => ("Apple", ""),
|
||||
(0xc0, _) => ("Ampere", ""),
|
||||
_ => ("ARM-compatible", ""),
|
||||
};
|
||||
if core.is_empty() {
|
||||
format!("{vendor} (CPU part {part:#05x})")
|
||||
} else {
|
||||
format!("{vendor} {core}")
|
||||
}
|
||||
}
|
||||
|
||||
fn read(path: &str) -> Option<String> {
|
||||
std::fs::read_to_string(path).ok()
|
||||
}
|
||||
@@ -91,7 +158,7 @@ fn os_version() -> Option<String> {
|
||||
|
||||
fn cpu() -> Option<String> {
|
||||
if cfg!(target_os = "linux") {
|
||||
read("/proc/cpuinfo").and_then(|t| field(&t, "model name"))
|
||||
read("/proc/cpuinfo").and_then(|t| linux_cpu_name(&t))
|
||||
} else if cfg!(target_os = "macos") {
|
||||
command_output("sysctl", &["-n", "machdep.cpu.brand_string"])
|
||||
} else if cfg!(windows) {
|
||||
@@ -608,6 +675,35 @@ pub fn gpu_vendor(name: &str, vendor_id: u32) -> &'static str {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn an_arm64_kernels_cpuinfo_names_the_core_by_implementer_and_part() {
|
||||
// An arm64 kernel prints no `model name` (a Neoverse-N1 and a Cortex-A72 Pi 4).
|
||||
let neoverse = "processor\t: 0\nBogoMIPS\t: 50.00\nFeatures\t: fp asimd\nCPU implementer\t: 0x41\nCPU architecture: 8\nCPU variant\t: 0x3\nCPU part\t: 0xd0c\nCPU revision\t: 1\n";
|
||||
assert_eq!(linux_cpu_name(neoverse).as_deref(), Some("ARM Neoverse-N1"));
|
||||
let pi = "processor\t: 3\nCPU implementer\t: 0x41\nCPU part\t: 0xd08\nCPU revision\t: 3\n\nRevision\t: c03114\nModel\t\t: Raspberry Pi 4 Model B Rev 1.4\n";
|
||||
assert_eq!(linux_cpu_name(pi).as_deref(), Some("ARM Cortex-A72"));
|
||||
// An implementer the table has, a part it does not: the part is kept.
|
||||
let apple = "CPU implementer\t: 0x61\nCPU part\t: 0x023\n";
|
||||
assert_eq!(
|
||||
linux_cpu_name(apple).as_deref(),
|
||||
Some("Apple (CPU part 0x023)")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn x86_keeps_its_model_name_and_a_board_without_either_its_model() {
|
||||
let ryzen = "processor\t: 0\nmodel name\t: AMD Ryzen 9 7950X 16-Core Processor\n";
|
||||
assert_eq!(
|
||||
linux_cpu_name(ryzen).as_deref(),
|
||||
Some("AMD Ryzen 9 7950X 16-Core Processor")
|
||||
);
|
||||
assert_eq!(
|
||||
linux_cpu_name("Model\t: Some Board\n").as_deref(),
|
||||
Some("Some Board")
|
||||
);
|
||||
assert_eq!(linux_cpu_name("processor\t: 0\n"), None);
|
||||
}
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user