| // 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/meminfo data, only contains used fields. | |
| #[derive(Default, Clone)] | |
| pub struct MemInfo { | |
| pub total: u64, | |
| pub free: u64, | |
| pub active_anon: u64, | |
| pub inactive_anon: u64, | |
| pub active_file: u64, | |
| pub inactive_file: u64, | |
| pub dirty: u64, | |
| pub swap_free: u64, | |
| pub swap_total: u64, | |
| } | |
| impl MemInfo { | |
| /// Load /proc/meminfo and parse it. | |
| pub fn load() -> io::Result<Self> { | |
| let reader = File::open("/proc/meminfo")?; | |
| 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 { | |
| "MemTotal:" => &mut result.total, | |
| "MemFree:" => &mut result.free, | |
| "Active(anon):" => &mut result.active_anon, | |
| "Inactive(anon):" => &mut result.inactive_anon, | |
| "Active(file):" => &mut result.active_file, | |
| "Inactive(file):" => &mut result.inactive_file, | |
| "Dirty:" => &mut result.dirty, | |
| "SwapFree:" => &mut result.swap_free, | |
| "SwapTotal:" => &mut result.swap_total, | |
| _ => continue, | |
| }; | |
| let Some(value) = tokens.next() else { | |
| continue; | |
| }; | |
| let Ok(value) = value.parse::<u64>() else { | |
| continue; | |
| }; | |
| *field = value; | |
| } | |
| Ok(result) | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| #[test] | |
| fn test_parse_meminfo() { | |
| let mock_meminfo = r#" | |
| MemTotal: 8025656 kB | |
| MemFree: 4586928 kB | |
| MemAvailable: 6704404 kB | |
| Buffers: 659640 kB | |
| Cached: 1949056 kB | |
| SwapCached: 0 kB | |
| Active: 1430416 kB | |
| Inactive: 1556968 kB | |
| Active(anon): 489640 kB | |
| Inactive(anon): 29188 kB | |
| Active(file): 940776 kB | |
| Inactive(file): 1527780 kB | |
| Unevictable: 151128 kB | |
| Mlocked: 41008 kB | |
| SwapTotal: 11756332 kB | |
| SwapFree: 11756331 kB | |
| Dirty: 5712 kB | |
| Writeback: 0 kB | |
| AnonPages: 529800 kB | |
| Mapped: 321468 kB | |
| Shmem: 140156 kB | |
| Slab: 169252 kB | |
| SReclaimable: 115540 kB | |
| SUnreclaim: 53712 kB | |
| KernelStack: 7072 kB | |
| PageTables: 13340 kB | |
| NFS_Unstable: 0 kB | |
| Bounce: 0 kB | |
| WritebackTmp: 0 kB | |
| CommitLimit: 15769160 kB | |
| Committed_AS: 2483600 kB | |
| VmallocTotal: 34359738367 kB | |
| VmallocUsed: 0 kB | |
| VmallocChunk: 0 kB | |
| Percpu: 2464 kB | |
| AnonHugePages: 40960 kB | |
| ShmemHugePages: 0 kB | |
| ShmemPmdMapped: 0 kB | |
| DirectMap4k: 170216 kB | |
| DirectMap2M: 5992448 kB | |
| DirectMap1G: 3145728 kB"#; | |
| let meminfo = MemInfo::parse(mock_meminfo.as_bytes()).unwrap(); | |
| assert_eq!(meminfo.free, 4586928); | |
| assert_eq!(meminfo.active_anon, 489640); | |
| assert_eq!(meminfo.inactive_anon, 29188); | |
| assert_eq!(meminfo.active_file, 940776); | |
| assert_eq!(meminfo.inactive_file, 1527780); | |
| assert_eq!(meminfo.dirty, 5712); | |
| assert_eq!(meminfo.swap_free, 11756331); | |
| assert_eq!(meminfo.swap_total, 11756332); | |
| } | |
| } |