From e689bee77103ade3320bb5eeea2b7994fbd84e58 Mon Sep 17 00:00:00 2001 From: alxndrv <> Date: Thu, 13 Feb 2025 18:55:26 +0200 Subject: [PATCH] `lscpu`: Read CPU count from `sysfs`, add freq_boost status to output --- src/uu/lscpu/src/lscpu.rs | 26 ++++++++++------ src/uu/lscpu/src/sysfs.rs | 65 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/uu/lscpu/src/lscpu.rs b/src/uu/lscpu/src/lscpu.rs index 4cd5647..bc532f4 100644 --- a/src/uu/lscpu/src/lscpu.rs +++ b/src/uu/lscpu/src/lscpu.rs @@ -7,7 +7,6 @@ use clap::{crate_version, Arg, ArgAction, Command}; use regex::RegexBuilder; use serde::Serialize; use std::{cmp, fs}; -use sysinfo::System; use uucore::{error::UResult, format_usage, help_about, help_usage}; mod options { @@ -72,8 +71,6 @@ struct OutputOptions { pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; - let system = System::new_all(); - let output_opts = OutputOptions { _hex: matches.get_flag(options::HEX), json: matches.get_flag(options::JSON), @@ -91,18 +88,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { arch_info.add_child(CpuInfo::new("Address sizes", &addr_sizes, None)) } - if let Ok(byte_order) = fs::read_to_string("/sys/kernel/cpu_byteorder") { - match byte_order.trim() { - "big" => arch_info.add_child(CpuInfo::new("Byte Order", "Big Endian", None)), - "little" => arch_info.add_child(CpuInfo::new("Byte Order", "Little Endian", None)), - _ => eprintln!("Unrecognised Byte Order: {}", byte_order), - } + if let Some(byte_order) = sysfs::read_cpu_byte_order() { + arch_info.add_child(CpuInfo::new("Byte Order", byte_order, None)); } + let cpu_topology = sysfs::read_cpu_topology(); + cpu_infos.push(arch_info); cpu_infos.push(CpuInfo::new( "CPU(s)", - &format!("{}", system.cpus().len()), + &format!("{}", cpu_topology.cpus.len()), None, )); @@ -123,6 +118,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { model_name_info.add_child(CpuInfo::new("Model", &model, None)); } + if let Some(freq_boost_enabled) = sysfs::read_freq_boost_state() { + let s = if freq_boost_enabled { + "enabled" + } else { + "disabled" + }; + model_name_info.add_child(CpuInfo::new("Frequency boost", s, None)); + } + vendor_info.add_child(model_name_info); } cpu_infos.push(vendor_info); @@ -211,6 +215,8 @@ fn find_cpuinfo_value(contents: &str, key: &str) -> Option<String> { value } +// TODO: This is non-exhaustive and assumes that compile-time arch is the same as runtime +// This is not always guaranteed to be the case, ie. you can run a x86 binary on a x86_64 machine fn get_architecture() -> String { if cfg!(target_arch = "x86") { "x86".to_string() diff --git a/src/uu/lscpu/src/sysfs.rs b/src/uu/lscpu/src/sysfs.rs index 62adc05..8536e52 100644 --- a/src/uu/lscpu/src/sysfs.rs +++ b/src/uu/lscpu/src/sysfs.rs @@ -5,6 +5,60 @@ pub struct CpuVulnerability { pub mitigation: String, } +pub struct CpuTopology { + pub cpus: Vec<Cpu>, +} + +pub struct Cpu { + _index: usize, + _caches: Vec<CpuCache>, +} + +pub struct CpuCache { + _index: usize, + _typ: String, + _level: String, + _size: String, +} + +// TODO: This should go through each CPU in sysfs and calculate things such as cache sizes and physical topology +// For now it just returns a list of CPUs which are enabled +pub fn read_cpu_topology() -> CpuTopology { + let mut out: Vec<Cpu> = vec![]; + + // NOTE: All examples I could find was where this file contains a CPU index range in the form of `<start>-<end>` + // Theoretically, there might be a situation where some cores are disabled, so that `enabled` cannot be represented + // as a continuous range. For now we just assume it's always `X-Y` and use those as our bounds to read CPU information + let enabled_cpus = match fs::read_to_string("/sys/devices/system/cpu/enabled") { + Ok(content) => { + let parts: Vec<_> = content + .trim() + .split("-") + .flat_map(|part| part.parse::<usize>()) + .collect(); + assert_eq!(parts.len(), 2); + (parts[0], parts[1]) + } + Err(e) => panic!("Could not read sysfs: {}", e), + }; + + for cpu_index in enabled_cpus.0..(enabled_cpus.1 + 1) { + out.push(Cpu { + _index: cpu_index, + _caches: vec![], + }) + } + + CpuTopology { cpus: out } +} + +pub fn read_freq_boost_state() -> Option<bool> { + match fs::read_to_string("/sys/devices/system/cpu/cpufreq/boost") { + Ok(content) => Some(content.trim() == "1"), + Err(_) => None, + } +} + pub fn read_cpu_vulnerabilities() -> Vec<CpuVulnerability> { let mut out: Vec<CpuVulnerability> = vec![]; @@ -31,3 +85,14 @@ pub fn read_cpu_vulnerabilities() -> Vec<CpuVulnerability> { out } + +pub fn read_cpu_byte_order() -> Option<&'static str> { + if let Ok(byte_order) = fs::read_to_string("/sys/kernel/cpu_byteorder") { + match byte_order.trim() { + "big" => return Some("Big Endian"), + "little" => return Some("Little Endian"), + _ => eprintln!("Unrecognised Byte Order: {}", byte_order), + } + } + None +}