use concinnity_memory::MemStats;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub(crate) struct MemorySnapshot {
pub heap: Option<MemStats>,
pub rss_bytes: Option<u64>,
}
impl MemorySnapshot {
pub(crate) fn capture() -> Self {
Self {
heap: concinnity_memory::stats(),
rss_bytes: crate::app::sysmem::process_resident_bytes(),
}
}
pub(crate) fn write_into(&self, out: &mut String) {
match self.heap {
Some(h) => {
out.push_str(&format!("heap-live: {}\n", bytes(h.live_bytes)));
out.push_str(&format!("heap-peak: {}\n", bytes(h.peak_bytes)));
out.push_str(&format!(
"heap-churn: {} alloc / {} free\n",
h.alloc_count, h.free_count
));
}
None => out.push_str("heap: <unavailable>\n"),
}
match self.rss_bytes {
Some(rss) => out.push_str(&format!("rss: {}\n", bytes(rss))),
None => out.push_str("rss: <unavailable>\n"),
}
}
}
fn bytes(value: u64) -> String {
const KIB: u64 = 1024;
const MIB: u64 = 1024 * KIB;
const GIB: u64 = 1024 * MIB;
let (div, unit) = if value >= GIB {
(GIB, "GiB")
} else if value >= MIB {
(MIB, "MiB")
} else if value >= KIB {
(KIB, "KiB")
} else {
return format!("{value} B");
};
format!("{value} ({:.1} {unit})", value as f64 / div as f64)
}
#[cfg(test)]
mod tests {
use super::*;
const MIB: u64 = 1024 * 1024;
fn rendered(snap: MemorySnapshot) -> String {
let mut out = String::new();
snap.write_into(&mut out);
out
}
#[test]
fn a_live_capture_carries_both_halves() {
let snap = MemorySnapshot::capture();
let heap = snap
.heap
.expect("the engine installs the tracking allocator");
assert!(heap.alloc_count > 0);
assert!(heap.peak_bytes >= heap.live_bytes);
if cfg!(any(
target_os = "macos",
target_os = "linux",
target_os = "windows"
)) {
assert!(snap.rss_bytes.expect("RSS query works here") > 0);
}
}
#[test]
fn both_figures_render_with_exact_bytes_and_a_scale() {
let text = rendered(MemorySnapshot {
heap: Some(MemStats {
live_bytes: 400 * MIB,
peak_bytes: 512 * MIB,
alloc_count: 8_123_456,
free_count: 7_901_234,
}),
rss_bytes: Some(6 * 1024 * MIB),
});
assert!(text.contains("heap-live: 419430400 (400.0 MiB)\n"));
assert!(text.contains("heap-peak: 536870912 (512.0 MiB)\n"));
assert!(text.contains("heap-churn: 8123456 alloc / 7901234 free\n"));
assert!(text.contains("rss: 6442450944 (6.0 GiB)\n"));
}
#[test]
fn an_unavailable_source_says_so_rather_than_reporting_zero() {
let text = rendered(MemorySnapshot::default());
assert!(text.contains("heap: <unavailable>\n"));
assert!(text.contains("rss: <unavailable>\n"));
assert!(!text.contains(" 0 "));
}
#[test]
fn small_values_read_as_plain_bytes() {
assert_eq!(bytes(0), "0 B");
assert_eq!(bytes(512), "512 B");
assert_eq!(bytes(2048), "2048 (2.0 KiB)");
}
}