use std::process::ExitCode;
use nix::{errno::Errno, libc::pid_t, unistd::Pid};
use syd::{
fd::open_static_proc,
human_size,
proc::{proc_mem, proc_smaps, proc_statm},
};
#[cfg(all(
not(coverage),
not(feature = "prof"),
not(target_os = "android"),
not(target_arch = "riscv64"),
target_page_size_4k,
target_pointer_width = "64"
))]
#[global_allocator]
static GLOBAL: hardened_malloc::HardenedMalloc = hardened_malloc::HardenedMalloc;
#[cfg(feature = "prof")]
#[global_allocator]
static GLOBAL: tcmalloc::TCMalloc = tcmalloc::TCMalloc;
syd::main! {
use lexopt::prelude::*;
syd::set_sigpipe_dfl()?;
open_static_proc()?;
let mut opt_smaps = false; let mut opt_human = false; let mut opt_is_vm = false; let mut opt_pid = None;
let mut parser = lexopt::Parser::from_env();
while let Some(arg) = parser.next()? {
match arg {
Short('h') => {
help();
return Ok(ExitCode::SUCCESS);
}
Short('H') => opt_human = true,
Short('V') => opt_is_vm = true,
Short('s') => opt_smaps = true,
Value(pid) if opt_pid.is_none() => {
opt_pid = Some(pid.parse::<pid_t>()?);
}
_ => return Err(arg.unexpected().into()),
}
}
let pid = match opt_pid {
None => Pid::parent(),
Some(pid) => Pid::from_raw(pid),
};
let size = if opt_is_vm {
match proc_statm(pid) {
Ok(statm) => statm.size.saturating_mul(*syd::config::PAGE_SIZE),
Err(error) => {
eprintln!("syd-mem: {error}");
return Ok(ExitCode::FAILURE);
}
}
} else if opt_smaps {
match proc_mem_smaps(pid) {
Ok(size) => size,
Err(error) => {
eprintln!("syd-mem: {error}");
return Ok(ExitCode::FAILURE);
}
}
} else {
match proc_mem(pid) {
Ok(size) => size,
Err(error) => {
eprintln!("syd-mem: {error}");
return Ok(ExitCode::FAILURE);
}
}
};
if opt_human {
println!("{}", human_size(size as usize));
} else {
println!("{size}");
}
Ok(ExitCode::SUCCESS)
}
fn help() {
println!("Usage: syd-mem [-HV] [pid]");
println!("Calculate the memory usage of a given process or the parent process and exit.");
println!("-H Print human-formatted size");
println!("-V Print virtual memory size");
}
fn proc_mem_smaps(pid: Pid) -> Result<u64, Errno> {
proc_smaps(pid).map(|maps| {
let mut sum = 0u64;
for map in &maps {
for key in ["Pss", "Private_Dirty", "Shared_Dirty"] {
let val = map.0.extension.map.get(key).copied().unwrap_or(0);
sum = sum.saturating_add(val);
}
}
sum
})
}