1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
use std::{
collections::BTreeSet,
hash::{Hash, Hasher},
io::ErrorKind,
str::from_utf8_unchecked,
};
use crate::scanner_rust::{generic_array::typenum::U1024, ScannerAscii, ScannerError};
#[allow(clippy::upper_case_acronyms)]
#[derive(Default, Debug, Clone)]
pub struct CPU {
pub physical_id: usize,
pub model_name: String,
pub cpus_mhz: Vec<f64>,
pub siblings: usize,
pub cpu_cores: usize,
}
impl Hash for CPU {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.physical_id.hash(state)
}
}
impl PartialEq for CPU {
#[inline]
fn eq(&self, other: &CPU) -> bool {
self.physical_id.eq(&other.physical_id)
}
}
/// Get CPU information by reading the `/proc/cpuinfo` file.
///
/// ```rust
/// use mprober_lib::cpu;
///
/// let cpus = cpu::get_cpus().unwrap();
///
/// println!("{cpus:#?}");
/// ```
pub fn get_cpus() -> Result<Vec<CPU>, ScannerError> {
const USEFUL_ITEMS: [&[u8]; 5] =
[b"model name", b"cpu MHz", b"physical id", b"siblings", b"cpu cores"];
const MODEL_NAME_INDEX: usize = 0;
const CPU_MHZ_INDEX: usize = 1;
const PHYSICAL_ID_INDEX: usize = 2;
const SIBLINGS_INDEX: usize = 3;
const CPU_CORES: usize = 4;
let mut sc: ScannerAscii<_, U1024> = ScannerAscii::scan_path2("/proc/cpuinfo")?;
let mut cpus = Vec::with_capacity(1);
let mut physical_ids: BTreeSet<usize> = BTreeSet::new();
let mut physical_id = 0;
let mut model_name = String::new();
let mut cpus_mhz = Vec::with_capacity(1);
let mut siblings = 0;
let mut cpu_cores = 0;
'outer: loop {
'item: for (i, &item) in USEFUL_ITEMS.iter().enumerate() {
let item_len = item.len();
loop {
match sc.next_line_raw()? {
Some(line) => {
if line.starts_with(item) {
let colon_index = line[item_len..]
.iter()
.copied()
.position(|b| b == b':')
.ok_or(ErrorKind::InvalidData)?;
let value = unsafe {
from_utf8_unchecked(&line[(item_len + colon_index + 1)..])
}
.trim();
match i {
MODEL_NAME_INDEX => {
if model_name.is_empty() {
model_name.push_str(value);
}
},
CPU_MHZ_INDEX => {
cpus_mhz.push(value.parse()?);
},
PHYSICAL_ID_INDEX => {
physical_id = value.parse()?;
if physical_ids.contains(&physical_id) {
break 'item;
}
},
SIBLINGS_INDEX => {
siblings = value.parse()?;
},
CPU_CORES => {
cpu_cores = value.parse()?;
break 'item;
},
_ => unreachable!(),
}
break;
}
},
None => {
if i == MODEL_NAME_INDEX {
break 'outer;
} else {
return Err(ErrorKind::UnexpectedEof.into());
}
},
}
}
}
if siblings == cpus_mhz.len() {
let cpu = CPU {
physical_id,
model_name,
cpus_mhz,
siblings,
cpu_cores,
};
cpus.push(cpu);
physical_ids.insert(physical_id);
physical_id = 0;
model_name = String::new();
cpus_mhz = Vec::with_capacity(1);
siblings = 0;
cpu_cores = 0;
}
loop {
let line_length = sc.drop_next_line()?;
match line_length {
Some(line_length) => {
if line_length == 0 {
break;
}
},
None => {
break 'outer;
},
}
}
}
Ok(cpus)
}