Skip to content

Commit

Permalink
Implement smaps_rollup.
Browse files Browse the repository at this point in the history
  • Loading branch information
TaborKelly committed Nov 23, 2022
1 parent b8a2fc4 commit 4aae8c4
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/process/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ pub use status::*;
mod schedstat;
pub use schedstat::*;

mod smaps_rollup;
pub use smaps_rollup::*;

mod task;
pub use task::*;

Expand Down Expand Up @@ -1062,6 +1065,15 @@ impl Process {
Ok(vec)
}

/// This is the sum of all the smaps data but it is much more performant to get it this way.
///
/// Since 4.14 and requires CONFIG_PROC_PAGE_MONITOR.
pub fn smaps_rollup(&self) -> ProcResult<SmapsRollup> {
let file = FileWrapper::open_at(&self.root, &self.fd, "smaps_rollup")?;

SmapsRollup::from_reader(file)
}

/// Returns a struct that can be used to access information in the `/proc/pid/pagemap` file.
pub fn pagemap(&self) -> ProcResult<PageMap> {
let path = self.root.join("pagemap");
Expand Down
58 changes: 58 additions & 0 deletions src/process/smaps_rollup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use super::{MemoryMap, MemoryMapData};
use crate::{ProcError, ProcResult};
use std::io::{BufRead, BufReader, Read};

#[derive(Debug)]
pub struct SmapsRollup {
pub memory_map: MemoryMap,
pub memory_map_data: MemoryMapData,
}

impl SmapsRollup {
// this implemenation is similar but not identical to Process::smaps()
pub fn from_reader<R: Read>(r: R) -> ProcResult<SmapsRollup> {
let reader = BufReader::new(r);

let mut memory_map = MemoryMap::new();
let mut memory_map_data: MemoryMapData = Default::default();
let mut first = true;
for line in reader.lines() {
let line = line.map_err(|_| ProcError::Incomplete(None))?;

if first {
memory_map = MemoryMap::from_line(&line)?;
first = false;
continue;
}

let mut parts = line.split_ascii_whitespace();

let key = parts.next();
let value = parts.next();

if let (Some(k), Some(v)) = (key, value) {
// While most entries do have one, not all of them do.
let size_suffix = parts.next();

// Limited poking at /proc/<pid>/smaps and then checking if "MB", "GB", and "TB" appear in the C file that is
// supposedly responsible for creating smaps, has lead me to believe that the only size suffixes we'll ever encounter
// "kB", which is most likely kibibytes. Actually checking if the size suffix is any of the above is a way to
// future-proof the code, but I am not sure it is worth doing so.
let size_multiplier = if size_suffix.is_some() { 1024 } else { 1 };

let v = v
.parse::<u64>()
.map_err(|_| ProcError::Other("Value in `Key: Value` pair was not actually a number".into()))?;

// This ignores the case when our Key: Value pairs are really Key Value pairs. Is this a good idea?
let k = k.trim_end_matches(':');

memory_map_data.map.insert(k.into(), v * size_multiplier);
}
}
Ok(SmapsRollup {
memory_map,
memory_map_data,
})
}
}
14 changes: 14 additions & 0 deletions src/process/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,20 @@ fn test_smaps() {
println!("{:#?}", smaps);
}

#[test]
fn test_smaps_rollup() {
let me = Process::myself().unwrap();
let smaps_rollup = match me.smaps_rollup() {
Ok(x) => x,
Err(ProcError::NotFound(_)) => {
// ignored because not all kernerls have smaps_rollup
return;
}
Err(e) => panic!("{}", e),
};
println!("{:#?}", smaps_rollup);
}

#[test]
fn test_proc_alive() {
let myself = Process::myself().unwrap();
Expand Down

0 comments on commit 4aae8c4

Please sign in to comment.