#![allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_lossless
)]
use std::time::Instant;
fn rusage_cpu_secs() -> Option<f64> {
unsafe {
let mut ru: libc::rusage = std::mem::zeroed();
if libc::getrusage(libc::RUSAGE_SELF, std::ptr::addr_of_mut!(ru)) != 0 {
return None;
}
let secs = |tv: libc::timeval| tv.tv_sec as f64 + tv.tv_usec as f64 / 1_000_000.0;
Some(secs(ru.ru_utime) + secs(ru.ru_stime))
}
}
pub struct CpuSampler {
prev: Option<(Instant, f64)>,
cores: f64,
}
impl CpuSampler {
#[must_use]
pub fn new() -> Self {
let cores = std::thread::available_parallelism().map_or(1.0, |n| n.get() as f64);
Self { prev: None, cores }
}
pub fn poll(&mut self) -> Option<f64> {
let now = Instant::now();
let cpu = rusage_cpu_secs()?;
match self.prev {
None => {
self.prev = Some((now, cpu));
None
}
Some((prev_t, prev_cpu)) => {
let wall = now.duration_since(prev_t).as_secs_f64();
self.prev = Some((now, cpu));
if wall <= 0.0 {
return None;
}
let cpu_delta = (cpu - prev_cpu).max(0.0);
let pct = (cpu_delta / wall) / self.cores * 100.0;
Some(pct.clamp(0.0, 100.0 * self.cores))
}
}
}
}
impl Default for CpuSampler {
fn default() -> Self {
Self::new()
}
}
#[must_use]
pub fn resident_set_bytes() -> Option<u64> {
#[cfg(target_os = "linux")]
{
let statm = std::fs::read_to_string("/proc/self/statm").ok()?;
let resident_pages: u64 = statm.split_whitespace().nth(1)?.parse().ok()?;
let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
if page_size <= 0 {
return None;
}
Some(resident_pages.saturating_mul(page_size as u64))
}
#[cfg(target_os = "macos")]
{
#[allow(deprecated)]
unsafe {
let mut info: libc::mach_task_basic_info = std::mem::zeroed();
let mut count: libc::mach_msg_type_number_t =
(std::mem::size_of::<libc::mach_task_basic_info>()
/ std::mem::size_of::<libc::integer_t>())
as libc::mach_msg_type_number_t;
let kr = libc::task_info(
libc::mach_task_self_,
libc::MACH_TASK_BASIC_INFO,
std::ptr::addr_of_mut!(info).cast(),
std::ptr::addr_of_mut!(count),
);
if kr == libc::KERN_SUCCESS {
Some(info.resident_size)
} else {
None
}
}
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
None
}
}
#[must_use]
pub fn total_ram_bytes() -> Option<u64> {
#[cfg(target_os = "linux")]
{
let pages = unsafe { libc::sysconf(libc::_SC_PHYS_PAGES) };
let page_size = unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) };
if pages <= 0 || page_size <= 0 {
return None;
}
Some((pages as u64).saturating_mul(page_size as u64))
}
#[cfg(target_os = "macos")]
{
let mut memsize: u64 = 0;
let mut len: libc::size_t = std::mem::size_of::<u64>();
let name = c"hw.memsize";
let rc = unsafe {
libc::sysctlbyname(
name.as_ptr(),
std::ptr::addr_of_mut!(memsize).cast(),
std::ptr::addr_of_mut!(len),
std::ptr::null_mut(),
0,
)
};
if rc == 0 && len == std::mem::size_of::<u64>() && memsize > 0 {
Some(memsize)
} else {
None
}
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resources_samplers_are_bounded_and_safe() {
if let Some(rss) = resident_set_bytes() {
assert!(rss > 0, "rss should be positive when sampled: {rss}");
assert!(
rss < 1024u64 * 1024 * 1024 * 1024,
"rss implausibly large: {rss}"
);
}
let cores = std::thread::available_parallelism().map_or(1.0, |n| n.get() as f64);
let mut sampler = CpuSampler::new();
assert!(
sampler.poll().is_none(),
"first poll must be a baseline (None)"
);
let mut acc = 0u64;
for i in 0..1_000_000u64 {
acc = acc.wrapping_add(i);
}
std::hint::black_box(acc);
if let Some(pct) = sampler.poll() {
assert!(pct >= 0.0, "cpu pct negative: {pct}");
assert!(pct.is_finite(), "cpu pct not finite: {pct}");
assert!(
pct <= 100.0 * cores + 1e-6,
"cpu pct exceeds capacity: {pct} (cores {cores})"
);
}
}
#[test]
fn resources_total_ram_is_positive() {
let ram = total_ram_bytes();
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
let n = ram.expect("total_ram_bytes must be Some on linux/macos");
assert!(n > 0, "total ram should be positive: {n}");
assert!(
n < 1024u64 * 1024 * 1024 * 1024 * 1024,
"total ram implausibly large: {n}"
);
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
if let Some(n) = ram {
assert!(n > 0, "total ram should be positive when sampled: {n}");
}
}
}
}