mod clock;
#[cfg(target_os = "macos")]
mod macos;
pub mod procfs;
pub use clock::{monotonic_ns, thread_cpu_ns, tid};
#[cfg(not(target_os = "macos"))]
use std::path::Path;
#[derive(Clone, Debug, Default)]
pub struct ProcessObservation {
pub pid: u32,
pub ppid: u32,
pub start_ticks: u64,
pub name: String,
pub user_ns: u64,
pub system_ns: u64,
pub waited_user_ns: u64,
pub waited_system_ns: u64,
pub rss_bytes: u64,
pub pss_bytes: Option<u64>,
pub read_bytes: Option<u64>,
pub write_bytes: Option<u64>,
pub minor_faults: u64,
pub major_faults: u64,
pub threads: u64,
}
#[must_use]
pub fn basic_process(pid: u32) -> Option<ProcessObservation> {
#[cfg(target_os = "macos")]
return macos::process(pid);
#[cfg(not(target_os = "macos"))]
procfs::basic_process(Path::new("/proc"), pid)
}
#[must_use]
pub fn process_identity(pid: u32) -> Option<u64> {
#[cfg(target_os = "macos")]
return macos::identity(pid);
#[cfg(not(target_os = "macos"))]
procfs::process_identity(Path::new("/proc"), pid)
}
#[must_use]
pub fn process(pid: u32, memory_detail: bool) -> Option<ProcessObservation> {
#[cfg(target_os = "macos")]
{
let _ = memory_detail;
basic_process(pid)
}
#[cfg(not(target_os = "macos"))]
{
let mut result = basic_process(pid)?;
procfs::read_details(Path::new("/proc"), &mut result, memory_detail);
Some(result)
}
}
#[derive(Debug, Default)]
pub struct ChildProcesses {
pub pids: Vec<u32>,
pub unavailable: bool,
pub incomplete: bool,
pub truncated: bool,
}
#[must_use]
pub fn children(pid: u32, capacity: usize) -> ChildProcesses {
#[cfg(target_os = "macos")]
return macos::children(pid, capacity);
#[cfg(not(target_os = "macos"))]
{
let _ = capacity;
procfs::children(Path::new("/proc"), pid)
}
}
#[must_use]
#[cfg_attr(
not(target_os = "linux"),
expect(
clippy::missing_const_for_fn,
reason = "the Linux implementation queries live scheduler state and cannot be const"
)
)]
pub fn affinity_cpus(pid: u32) -> Option<u32> {
#[cfg(target_os = "linux")]
{
let pid = rustix::thread::Pid::from_raw(i32::try_from(pid).ok()?);
rustix::thread::sched_getaffinity(pid)
.ok()
.map(|set| set.count())
}
#[cfg(not(target_os = "linux"))]
{
let _ = pid;
None
}
}
#[cfg(test)]
mod tests {
#[test]
#[cfg(target_os = "linux")]
fn affinity_preserves_current_thread_selection_and_invalid_pid_refusal() {
let current = super::affinity_cpus(0).unwrap();
assert!(current > 0);
assert_eq!(
super::affinity_cpus(u32::try_from(super::tid()).unwrap()),
Some(current)
);
assert_eq!(super::affinity_cpus(u32::MAX), None);
}
#[test]
#[cfg(unix)]
fn native_identity_clocks_and_process_facts_are_available() {
let pid = std::process::id();
let before = super::monotonic_ns();
let observation = super::process(pid, true).unwrap();
assert_eq!(observation.pid, pid);
assert_ne!(observation.rss_bytes, 0);
assert_eq!(super::process_identity(pid), Some(observation.start_ticks));
assert!(super::monotonic_ns() >= before);
assert_ne!(super::thread_cpu_ns(), 0);
assert_ne!(super::tid(), 0);
#[cfg(target_os = "macos")]
assert!(observation.pss_bytes.is_none() && observation.read_bytes.is_none());
}
}