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"),
allow(
clippy::missing_const_for_fn,
reason = "the Linux implementation queries live scheduler state and cannot be const"
)
)]
#[allow(
unsafe_code,
reason = "reviewed initialized sched_getaffinity output imported from Profiler"
)]
pub fn affinity_cpus(pid: u32) -> Option<u32> {
#[cfg(target_os = "linux")]
{
let pid = libc::pid_t::try_from(pid).ok()?;
let mut set: libc::cpu_set_t = unsafe { std::mem::zeroed() };
if unsafe { libc::sched_getaffinity(pid, std::mem::size_of_val(&set), &raw mut set) } != 0 {
return None;
}
u32::try_from(unsafe { libc::CPU_COUNT(&set) }).ok()
}
#[cfg(not(target_os = "linux"))]
{
let _ = pid;
None
}
}
#[cfg(test)]
mod tests {
#[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());
}
}