#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct RssSample {
pub current_bytes: Option<u64>,
pub peak_bytes: Option<u64>,
}
pub fn sample() -> RssSample {
RssSample {
current_bytes: current_rss(),
peak_bytes: peak_rss(),
}
}
#[cfg(target_os = "macos")]
fn current_rss() -> Option<u64> {
use std::mem;
use mach2::kern_return::KERN_SUCCESS;
use mach2::message::mach_msg_type_number_t;
use mach2::task::task_info;
use mach2::task_info::{MACH_TASK_BASIC_INFO, mach_task_basic_info, task_info_t};
use mach2::traps::mach_task_self;
unsafe {
let mut info = mem::zeroed::<mach_task_basic_info>();
let mut count = (mem::size_of::<mach_task_basic_info>() / mem::size_of::<u32>()) as mach_msg_type_number_t;
let kr = task_info(
mach_task_self(),
MACH_TASK_BASIC_INFO,
(&mut info as *mut mach_task_basic_info).cast::<i32>() as task_info_t,
&mut count,
);
if kr == KERN_SUCCESS {
Some(info.resident_size)
} else {
None
}
}
}
#[cfg(target_os = "linux")]
fn current_rss() -> Option<u64> {
let statm = std::fs::read_to_string("/proc/self/statm").ok()?;
let resident_pages: u64 = statm.split_whitespace().nth(1)?.parse().ok()?;
let page = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
let page = if page > 0 { page as u64 } else { 4096 };
Some(resident_pages.saturating_mul(page))
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
fn current_rss() -> Option<u64> {
None
}
#[cfg(target_os = "macos")]
pub fn phys_footprint() -> Option<u64> {
use std::mem;
use mach2::kern_return::KERN_SUCCESS;
use mach2::message::mach_msg_type_number_t;
use mach2::task::task_info;
use mach2::task_info::{TASK_VM_INFO, task_info_t, task_vm_info};
use mach2::traps::mach_task_self;
unsafe {
let mut info = task_vm_info::default();
let mut count = (mem::size_of::<task_vm_info>() / mem::size_of::<u32>()) as mach_msg_type_number_t;
let kr = task_info(
mach_task_self(),
TASK_VM_INFO,
(&mut info as *mut task_vm_info).cast::<i32>() as task_info_t,
&mut count,
);
if kr == KERN_SUCCESS {
let footprint = info.phys_footprint;
Some(footprint)
} else {
None
}
}
}
#[cfg(not(target_os = "macos"))]
pub fn phys_footprint() -> Option<u64> {
None
}
#[cfg(unix)]
fn peak_rss() -> Option<u64> {
use std::mem;
unsafe {
let mut usage: libc::rusage = mem::zeroed();
if libc::getrusage(libc::RUSAGE_SELF, &mut usage) != 0 {
return None;
}
let maxrss = usage.ru_maxrss.max(0) as u64;
#[cfg(target_os = "macos")]
let bytes = maxrss;
#[cfg(not(target_os = "macos"))]
let bytes = maxrss.saturating_mul(1024);
Some(bytes)
}
}
#[cfg(not(unix))]
fn peak_rss() -> Option<u64> {
None
}
#[cfg(all(test, unix))]
mod tests {
use super::*;
#[test]
fn sample_reports_nonzero_rss_on_unix() {
let s = sample();
let current = s.current_bytes.expect("current RSS should be readable on unix");
assert!(current > 0, "current RSS must be positive, got {current}");
let peak = s.peak_bytes.expect("peak RSS should be readable on unix");
assert!(peak > 0, "peak RSS must be positive, got {peak}");
#[cfg(target_os = "macos")]
assert!(
peak >= current,
"peak RSS ({peak}) should be >= current RSS ({current})"
);
}
#[cfg(target_os = "macos")]
#[test]
fn phys_footprint_reports_positive_bytes_on_macos() {
let footprint = phys_footprint().expect("phys_footprint should be readable on macOS");
assert!(footprint > 0, "phys_footprint must be positive, got {footprint}");
}
}