blob: 8fd0f18afa63db47aeb543505d2aaee8ee672473 [file] [log] [blame] [edit]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::fs::File;
use std::io;
use std::io::BufRead;
use std::io::BufReader;
/// Struct to hold parsed /proc/vmstat data, only contains used fields.
#[derive(Debug, Default, Clone)]
pub struct Vmstat {
pub workingset_refault_anon: usize,
pub workingset_refault_file: usize,
pub pgsteal_direct: usize,
}
impl Vmstat {
/// Load /proc/vmstat and parse it.
pub fn load() -> io::Result<Self> {
let reader = File::open("/proc/vmstat")?;
let reader = BufReader::new(reader);
Self::parse(reader)
}
fn parse<R: BufRead>(reader: R) -> io::Result<Self> {
let mut result = Self::default();
for line in reader.lines() {
let line = line?;
let mut tokens = line.split_whitespace();
let Some(key) = tokens.next() else {
continue;
};
let field = match key {
"workingset_refault_anon" => &mut result.workingset_refault_anon,
"workingset_refault_file" => &mut result.workingset_refault_file,
// kernels before 5.9 don't have workingset_refault_anon and
// workingset_refault_file but workingset_refault only for file page cache.
"workingset_refault" => &mut result.workingset_refault_file,
"pgsteal_direct" => &mut result.pgsteal_direct,
_ => {
continue;
}
};
let Some(value) = tokens.next() else {
continue;
};
let Ok(value) = value.parse::<usize>() else {
continue;
};
*field = value;
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_vmstat() {
let mock_vmstat = r#"
nr_free_pages 44453
nr_zone_inactive_anon 100986
nr_zone_active_anon 294376
nr_zone_inactive_file 61713
nr_zone_active_file 62802
nr_zone_unevictable 22615
nr_zone_write_pending 400
nr_mlock 18
nr_bounce 0
nr_zspages 270755
nr_free_cma 0
nr_inactive_anon 100986
nr_active_anon 294376
nr_inactive_file 61713
nr_active_file 62802
nr_unevictable 22615
nr_slab_reclaimable 20307
nr_slab_unreclaimable 39410
nr_isolated_anon 0
nr_isolated_file 0
workingset_nodes 2292
workingset_refault_anon 72178310
workingset_refault_file 3998735
workingset_activate_anon 2282026
workingset_activate_file 301341
workingset_restore_anon 70302599
workingset_restore_file 2139605
workingset_nodereclaim 0
nr_anon_pages 299987
nr_mapped 123606
nr_file_pages 243333
nr_dirty 400
nr_writeback 0
nr_writeback_temp 0
nr_shmem 115859
nr_shmem_hugepages 0
nr_shmem_pmdmapped 0
nr_file_hugepages 0
nr_file_pmdmapped 0
nr_anon_transparent_hugepages 0
nr_vmscan_write 103599919
nr_vmscan_immediate_reclaim 0
nr_dirtied 10919793
nr_written 115964117
nr_kernel_misc_reclaimable 0
nr_foll_pin_acquired 0
nr_foll_pin_released 0
nr_kernel_stack 15792
nr_page_table_pages 18652
nr_swapcached 2959
nr_dirty_threshold 95570
nr_dirty_background_threshold 7935
pgpgin 50300534
pgpgout 97467164
pswpin 90414350
pswpout 105433731
pgalloc_dma 18365
pgalloc_dma32 355149358
pgalloc_normal 372416197
pgalloc_movable 0
pgsteal_kswapd 41539543
pgsteal_direct 5087936
pgsteal_anon 34022233
pgsteal_file 12605246"#;
let vmstat = Vmstat::parse(mock_vmstat.as_bytes()).unwrap();
assert_eq!(vmstat.workingset_refault_anon, 72178310);
assert_eq!(vmstat.workingset_refault_file, 3998735);
assert_eq!(vmstat.pgsteal_direct, 5087936);
}
#[test]
fn test_parse_vmstat_workingset_refault() {
let mock_vmstat = "workingset_refault 12345";
let vmstat = Vmstat::parse(mock_vmstat.as_bytes()).unwrap();
assert_eq!(vmstat.workingset_refault_anon, 0);
assert_eq!(vmstat.workingset_refault_file, 12345);
}
}