ic_host_process/observation/
mod.rs1mod clock;
7#[cfg(target_os = "macos")]
8mod macos;
9pub mod procfs;
10
11pub use clock::{monotonic_ns, thread_cpu_ns, tid};
12#[cfg(not(target_os = "macos"))]
13use std::path::Path;
14
15#[derive(Clone, Debug, Default)]
17pub struct ProcessObservation {
18 pub pid: u32,
20 pub ppid: u32,
22 pub start_ticks: u64,
24 pub name: String,
26 pub user_ns: u64,
28 pub system_ns: u64,
30 pub waited_user_ns: u64,
32 pub waited_system_ns: u64,
34 pub rss_bytes: u64,
36 pub pss_bytes: Option<u64>,
38 pub read_bytes: Option<u64>,
40 pub write_bytes: Option<u64>,
42 pub minor_faults: u64,
44 pub major_faults: u64,
46 pub threads: u64,
48}
49
50#[must_use]
52pub fn basic_process(pid: u32) -> Option<ProcessObservation> {
53 #[cfg(target_os = "macos")]
54 return macos::process(pid);
55 #[cfg(not(target_os = "macos"))]
56 procfs::basic_process(Path::new("/proc"), pid)
57}
58
59#[must_use]
61pub fn process_identity(pid: u32) -> Option<u64> {
62 #[cfg(target_os = "macos")]
63 return macos::identity(pid);
64 #[cfg(not(target_os = "macos"))]
65 procfs::process_identity(Path::new("/proc"), pid)
66}
67
68#[must_use]
70pub fn process(pid: u32, memory_detail: bool) -> Option<ProcessObservation> {
71 #[cfg(target_os = "macos")]
72 {
73 let _ = memory_detail;
74 basic_process(pid)
75 }
76 #[cfg(not(target_os = "macos"))]
77 {
78 let mut result = basic_process(pid)?;
79 procfs::read_details(Path::new("/proc"), &mut result, memory_detail);
80 Some(result)
81 }
82}
83
84#[derive(Debug, Default)]
86pub struct ChildProcesses {
87 pub pids: Vec<u32>,
89 pub unavailable: bool,
91 pub incomplete: bool,
93 pub truncated: bool,
95}
96
97#[must_use]
100pub fn children(pid: u32, capacity: usize) -> ChildProcesses {
101 #[cfg(target_os = "macos")]
102 return macos::children(pid, capacity);
103 #[cfg(not(target_os = "macos"))]
104 {
105 let _ = capacity;
106 procfs::children(Path::new("/proc"), pid)
107 }
108}
109
110#[must_use]
113#[cfg_attr(
114 not(target_os = "linux"),
115 allow(
116 clippy::missing_const_for_fn,
117 reason = "the Linux implementation queries live scheduler state and cannot be const"
118 )
119)]
120#[allow(
121 unsafe_code,
122 reason = "reviewed initialized sched_getaffinity output imported from Profiler"
123)]
124pub fn affinity_cpus(pid: u32) -> Option<u32> {
125 #[cfg(target_os = "linux")]
126 {
127 let pid = libc::pid_t::try_from(pid).ok()?;
128 let mut set: libc::cpu_set_t = unsafe { std::mem::zeroed() };
130 if unsafe { libc::sched_getaffinity(pid, std::mem::size_of_val(&set), &raw mut set) } != 0 {
132 return None;
133 }
134 u32::try_from(unsafe { libc::CPU_COUNT(&set) }).ok()
136 }
137 #[cfg(not(target_os = "linux"))]
138 {
139 let _ = pid;
140 None
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 #[test]
147 #[cfg(unix)]
148 fn native_identity_clocks_and_process_facts_are_available() {
149 let pid = std::process::id();
150 let before = super::monotonic_ns();
151 let observation = super::process(pid, true).unwrap();
152 assert_eq!(observation.pid, pid);
153 assert_ne!(observation.rss_bytes, 0);
154 assert_eq!(super::process_identity(pid), Some(observation.start_ticks));
155 assert!(super::monotonic_ns() >= before);
156 assert_ne!(super::thread_cpu_ns(), 0);
157 assert_ne!(super::tid(), 0);
158 #[cfg(target_os = "macos")]
159 assert!(observation.pss_bytes.is_none() && observation.read_bytes.is_none());
160 }
161}