hcl/
stats.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Code for getting kernel stats from /proc/mshv.
5
6use std::io::BufRead;
7use std::io::BufReader;
8use thiserror::Error;
9
10/// Error returned by [`vp_stats`].
11#[derive(Debug, Error)]
12#[expect(missing_docs)]
13pub enum VpStatsError {
14    #[error("failed to read /proc/mshv")]
15    Read(#[source] std::io::Error),
16    #[error("failed to parse stats string")]
17    ParseString(#[source] std::io::Error),
18    #[error("failed to parse stats line")]
19    ParseLine,
20    #[error("a cpu was missing from the stats file")]
21    MissingCpu,
22}
23
24/// The per-VP stats from the kernel.
25#[derive(Debug)]
26pub struct HclVpStats {
27    /// The number of VTL transitions.
28    pub vtl_transitions: u64,
29}
30
31/// Gets the per-VP stats from the kernel.
32pub fn vp_stats() -> Result<Vec<HclVpStats>, VpStatsError> {
33    let v = std::fs::read("/proc/mshv").map_err(VpStatsError::Read)?;
34    // Skip the first two lines (version and header).
35    let lines = BufReader::new(v.as_slice()).lines().skip(2);
36    let mut stats = Vec::new();
37    for line in lines {
38        let line = line.map_err(VpStatsError::ParseString)?;
39        let (cpu, rest) = line.split_once(' ').ok_or(VpStatsError::ParseLine)?;
40        let n: usize = cpu
41            .strip_prefix("cpu")
42            .ok_or(VpStatsError::ParseLine)?
43            .parse()
44            .map_err(|_| VpStatsError::ParseLine)?;
45
46        if n != stats.len() {
47            return Err(VpStatsError::MissingCpu);
48        }
49        let vtl_transitions = rest
50            .split(' ')
51            .next()
52            .ok_or(VpStatsError::ParseLine)?
53            .parse()
54            .map_err(|_| VpStatsError::ParseLine)?;
55
56        stats.push(HclVpStats { vtl_transitions })
57    }
58    Ok(stats)
59}