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;
use std::hash::{Hash, Hasher};
use std::io::ErrorKind;
use std::str::from_utf8_unchecked;

use crate::scanner_rust::generic_array::typenum::U1024;
use crate::scanner_rust::{ScannerAscii, ScannerError};

#[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
/// extern crate mprober_lib;
///
/// 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)
}