dataprof_core/
memory_sampler.rs1use sysinfo::{Pid, ProcessRefreshKind, ProcessesToUpdate, System};
2
3pub struct PeakMemorySampler {
12 system: System,
13 pid: Option<Pid>,
14 peak_bytes: u64,
15}
16
17impl PeakMemorySampler {
18 pub fn new() -> Self {
20 let mut sampler = Self {
21 system: System::new(),
22 pid: sysinfo::get_current_pid().ok(),
23 peak_bytes: 0,
24 };
25 sampler.sample();
26 sampler
27 }
28
29 pub fn sample(&mut self) {
31 let Some(pid) = self.pid else { return };
32 self.system.refresh_processes_specifics(
33 ProcessesToUpdate::Some(&[pid]),
34 false,
35 ProcessRefreshKind::nothing().with_memory(),
36 );
37 if let Some(process) = self.system.process(pid) {
38 self.peak_bytes = self.peak_bytes.max(process.memory());
39 }
40 }
41
42 pub fn peak_mb(&self) -> Option<f64> {
47 (self.peak_bytes > 0).then(|| self.peak_bytes as f64 / (1024.0 * 1024.0))
48 }
49}
50
51impl Default for PeakMemorySampler {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn test_sampler_reports_positive_peak() {
63 let sampler = PeakMemorySampler::new();
64 let peak = sampler
65 .peak_mb()
66 .expect("current process RSS should be readable on supported platforms");
67 assert!(peak > 0.0, "peak RSS must be positive, got {peak}");
68 }
69
70 #[test]
71 fn test_peak_is_monotonic_across_samples() {
72 let mut sampler = PeakMemorySampler::new();
73 let first = sampler.peak_mb().expect("initial reading");
74 let ballast = vec![1u8; 4 * 1024 * 1024];
76 sampler.sample();
77 let second = sampler.peak_mb().expect("second reading");
78 assert!(
79 second >= first,
80 "peak must never decrease: {first} -> {second}"
81 );
82 drop(ballast);
83 }
84}