lscpu
: Show number of physical CPU sockets, add unit test
This commit is contained in:
parent
99c751bd1d
commit
41bff4fbd6
@ -94,7 +94,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||||||
|
|
||||||
cpu_infos.push(arch_info);
|
cpu_infos.push(arch_info);
|
||||||
|
|
||||||
let cpu_topology = sysfs::read_cpu_topology();
|
let cpu_topology = sysfs::CpuTopology::new();
|
||||||
let mut cores_info = CpuInfo::new("CPU(s)", &format!("{}", cpu_topology.cpus.len()), None);
|
let mut cores_info = CpuInfo::new("CPU(s)", &format!("{}", cpu_topology.cpus.len()), None);
|
||||||
|
|
||||||
cores_info.add_child(CpuInfo::new(
|
cores_info.add_child(CpuInfo::new(
|
||||||
@ -131,8 +131,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||||||
model_name_info.add_child(CpuInfo::new("Frequency boost", s, None));
|
model_name_info.add_child(CpuInfo::new("Frequency boost", s, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model_name_info.add_child(CpuInfo::new(
|
||||||
|
"Socket(s)",
|
||||||
|
&cpu_topology.socket_count().to_string(),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
|
||||||
vendor_info.add_child(model_name_info);
|
vendor_info.add_child(model_name_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu_infos.push(vendor_info);
|
cpu_infos.push(vendor_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use parse_size::parse_size;
|
use parse_size::parse_size;
|
||||||
use std::{fs, path::PathBuf};
|
use std::{collections::HashSet, fs, path::PathBuf};
|
||||||
|
|
||||||
pub struct CpuVulnerability {
|
pub struct CpuVulnerability {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -13,7 +13,7 @@ pub struct CpuTopology {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Cpu {
|
pub struct Cpu {
|
||||||
_index: usize,
|
_index: usize,
|
||||||
pub _pkg_id: usize,
|
pub pkg_id: usize,
|
||||||
pub caches: Vec<CpuCache>,
|
pub caches: Vec<CpuCache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,6 +32,41 @@ pub enum CacheType {
|
|||||||
Unified,
|
Unified,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CpuTopology {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut out: Vec<Cpu> = vec![];
|
||||||
|
|
||||||
|
let online_cpus = parse_cpu_list(&read_online_cpus());
|
||||||
|
|
||||||
|
for cpu_index in online_cpus {
|
||||||
|
let cpu_dir = PathBuf::from(format!("/sys/devices/system/cpu/cpu{}/", cpu_index));
|
||||||
|
|
||||||
|
let physical_pkg_id = fs::read_to_string(cpu_dir.join("topology/physical_package_id"))
|
||||||
|
.unwrap()
|
||||||
|
.trim()
|
||||||
|
.parse::<usize>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let caches = read_cpu_caches(cpu_index);
|
||||||
|
|
||||||
|
out.push(Cpu {
|
||||||
|
_index: cpu_index,
|
||||||
|
pkg_id: physical_pkg_id,
|
||||||
|
caches,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Self { cpus: out }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn socket_count(&self) -> usize {
|
||||||
|
// Each physical socket is represented as its own package_id, so amount of unique pkg_ids = sockets
|
||||||
|
// https://www.kernel.org/doc/html/latest/admin-guide/abi-stable.html#abi-sys-devices-system-cpu-cpux-topology-physical-package-id
|
||||||
|
let physical_sockets: HashSet<usize> = self.cpus.iter().map(|cpu| cpu.pkg_id).collect();
|
||||||
|
|
||||||
|
physical_sockets.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: respect `--hex` option and output the bitmask instead of human-readable range
|
// TODO: respect `--hex` option and output the bitmask instead of human-readable range
|
||||||
pub fn read_online_cpus() -> String {
|
pub fn read_online_cpus() -> String {
|
||||||
fs::read_to_string("/sys/devices/system/cpu/online")
|
fs::read_to_string("/sys/devices/system/cpu/online")
|
||||||
@ -40,54 +75,6 @@ pub fn read_online_cpus() -> String {
|
|||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes in a human-readable list of CPUs, and returns a list of indices parsed from that list
|
|
||||||
// These can come in the form of a plain range like `X-Y`, or a comma-separated ranges and indices ie. `1,3-4,7-8,10`
|
|
||||||
// Kernel docs with examples: https://www.kernel.org/doc/html/latest/admin-guide/cputopology.html
|
|
||||||
fn parse_cpu_list(list: &str) -> Vec<usize> {
|
|
||||||
let mut out: Vec<usize> = vec![];
|
|
||||||
|
|
||||||
for part in list.trim().split(",") {
|
|
||||||
if part.contains("-") {
|
|
||||||
let bounds: Vec<_> = part.split("-").flat_map(|x| x.parse::<usize>()).collect();
|
|
||||||
assert_eq!(bounds.len(), 2);
|
|
||||||
for idx in bounds[0]..bounds[1] + 1 {
|
|
||||||
out.push(idx)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let idx = part.parse::<usize>().expect("Invalid CPU index value");
|
|
||||||
out.push(idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_cpu_topology() -> CpuTopology {
|
|
||||||
let mut out: Vec<Cpu> = vec![];
|
|
||||||
|
|
||||||
let online_cpus = parse_cpu_list(&read_online_cpus());
|
|
||||||
|
|
||||||
for cpu_index in online_cpus {
|
|
||||||
let cpu_dir = PathBuf::from(format!("/sys/devices/system/cpu/cpu{}/", cpu_index));
|
|
||||||
|
|
||||||
let physical_pkg_id = fs::read_to_string(cpu_dir.join("topology/physical_package_id"))
|
|
||||||
.unwrap()
|
|
||||||
.trim()
|
|
||||||
.parse::<usize>()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let caches = read_cpu_caches(cpu_index);
|
|
||||||
|
|
||||||
out.push(Cpu {
|
|
||||||
_index: cpu_index,
|
|
||||||
_pkg_id: physical_pkg_id,
|
|
||||||
caches,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
CpuTopology { cpus: out }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_cpu_caches(cpu_index: usize) -> Vec<CpuCache> {
|
fn read_cpu_caches(cpu_index: usize) -> Vec<CpuCache> {
|
||||||
let cpu_dir = PathBuf::from(format!("/sys/devices/system/cpu/cpu{}/", cpu_index));
|
let cpu_dir = PathBuf::from(format!("/sys/devices/system/cpu/cpu{}/", cpu_index));
|
||||||
let cache_dir = fs::read_dir(cpu_dir.join("cache")).unwrap();
|
let cache_dir = fs::read_dir(cpu_dir.join("cache")).unwrap();
|
||||||
@ -175,3 +162,44 @@ pub fn read_cpu_byte_order() -> Option<&'static str> {
|
|||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Takes in a human-readable list of CPUs, and returns a list of indices parsed from that list
|
||||||
|
// These can come in the form of a plain range like `X-Y`, or a comma-separated ranges and indices ie. `1,3-4,7-8,10`
|
||||||
|
// Kernel docs with examples: https://www.kernel.org/doc/html/latest/admin-guide/cputopology.html
|
||||||
|
fn parse_cpu_list(list: &str) -> Vec<usize> {
|
||||||
|
let mut out: Vec<usize> = vec![];
|
||||||
|
|
||||||
|
if list.is_empty() {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for part in list.trim().split(",") {
|
||||||
|
if part.contains("-") {
|
||||||
|
let bounds: Vec<_> = part.split("-").flat_map(|x| x.parse::<usize>()).collect();
|
||||||
|
assert_eq!(bounds.len(), 2);
|
||||||
|
for idx in bounds[0]..bounds[1] + 1 {
|
||||||
|
out.push(idx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let idx = part.parse::<usize>().expect("Invalid CPU index value");
|
||||||
|
out.push(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_cpu_list() {
|
||||||
|
assert_eq!(parse_cpu_list(""), Vec::<usize>::new());
|
||||||
|
assert_eq!(parse_cpu_list("1-3"), Vec::<usize>::from([1, 2, 3]));
|
||||||
|
assert_eq!(parse_cpu_list("1,2,3"), Vec::<usize>::from([1, 2, 3]));
|
||||||
|
assert_eq!(
|
||||||
|
parse_cpu_list("1,3-6,8"),
|
||||||
|
Vec::<usize>::from([1, 3, 4, 5, 6, 8])
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_cpu_list("1-2,3-5,7"),
|
||||||
|
Vec::<usize>::from([1, 2, 3, 4, 5, 7])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user