Skip to main content

rigtop_core/linux/
meminfo.rs

1//! Supplementary memory figures from `/proc/meminfo`.
2//!
3//! `sysinfo` covers total/used/available/free; this module only adds the page
4//! cache figure that btop-style memory panels show.
5
6use std::fs;
7
8use super::SysRoots;
9
10/// Returns `Cached + SReclaimable` in bytes, matching what `free` reports as
11/// "buff/cache" minus buffers.
12pub fn read_cached_bytes(roots: &SysRoots) -> Option<u64> {
13    let text = fs::read_to_string(roots.proc.join("meminfo")).ok()?;
14    parse_cached_bytes(&text)
15}
16
17/// Parses the cache figure out of `/proc/meminfo` text.
18pub(crate) fn parse_cached_bytes(text: &str) -> Option<u64> {
19    let mut cached = None;
20    let mut reclaimable = 0;
21    for line in text.lines() {
22        let Some((key, rest)) = line.split_once(':') else {
23            continue;
24        };
25        // Values are in KiB: "Cached:         12345678 kB".
26        let kib = || rest.split_whitespace().next()?.parse::<u64>().ok();
27        match key {
28            "Cached" => cached = kib(),
29            "SReclaimable" => reclaimable = kib().unwrap_or(0),
30            _ => {}
31        }
32    }
33    cached.map(|c| (c + reclaimable) * 1024)
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn cached_includes_reclaimable_slab() {
42        let text = "MemTotal: 100 kB\nCached:  1000 kB\nSwapCached: 5 kB\nSReclaimable: 24 kB\n";
43        assert_eq!(parse_cached_bytes(text), Some(1024 * 1024));
44    }
45
46    #[test]
47    fn missing_cached_line_is_none() {
48        assert_eq!(parse_cached_bytes("MemTotal: 100 kB\n"), None);
49    }
50}