summaryrefslogtreecommitdiffstats
path: root/vendor/sysinfo/src/apple/macos/process.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/sysinfo/src/apple/macos/process.rs
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/sysinfo/src/apple/macos/process.rs')
-rw-r--r--vendor/sysinfo/src/apple/macos/process.rs643
1 files changed, 643 insertions, 0 deletions
diff --git a/vendor/sysinfo/src/apple/macos/process.rs b/vendor/sysinfo/src/apple/macos/process.rs
new file mode 100644
index 000000000..f146126cf
--- /dev/null
+++ b/vendor/sysinfo/src/apple/macos/process.rs
@@ -0,0 +1,643 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use std::ffi::CStr;
+use std::mem::{self, MaybeUninit};
+use std::ops::Deref;
+use std::path::{Path, PathBuf};
+
+use std::borrow::Borrow;
+
+use libc::{c_int, c_void, kill, size_t};
+
+use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid};
+
+use crate::sys::process::ThreadStatus;
+use crate::sys::system::Wrap;
+
+#[doc = include_str!("../../../md_doc/process.md")]
+pub struct Process {
+ pub(crate) name: String,
+ pub(crate) cmd: Vec<String>,
+ pub(crate) exe: PathBuf,
+ pid: Pid,
+ parent: Option<Pid>,
+ pub(crate) environ: Vec<String>,
+ cwd: PathBuf,
+ pub(crate) root: PathBuf,
+ pub(crate) memory: u64,
+ pub(crate) virtual_memory: u64,
+ old_utime: u64,
+ old_stime: u64,
+ start_time: u64,
+ run_time: u64,
+ updated: bool,
+ cpu_usage: f32,
+ user_id: Option<Uid>,
+ group_id: Option<Gid>,
+ pub(crate) process_status: ProcessStatus,
+ /// Status of process (running, stopped, waiting, etc). `None` means `sysinfo` doesn't have
+ /// enough rights to get this information.
+ ///
+ /// This is very likely this one that you want instead of `process_status`.
+ pub status: Option<ThreadStatus>,
+ pub(crate) old_read_bytes: u64,
+ pub(crate) old_written_bytes: u64,
+ pub(crate) read_bytes: u64,
+ pub(crate) written_bytes: u64,
+}
+
+impl Process {
+ pub(crate) fn new_empty(pid: Pid, exe: PathBuf, name: String, cwd: PathBuf) -> Process {
+ Process {
+ name,
+ pid,
+ parent: None,
+ cmd: Vec::new(),
+ environ: Vec::new(),
+ exe,
+ cwd,
+ root: PathBuf::new(),
+ memory: 0,
+ virtual_memory: 0,
+ cpu_usage: 0.,
+ old_utime: 0,
+ old_stime: 0,
+ updated: true,
+ start_time: 0,
+ run_time: 0,
+ user_id: None,
+ group_id: None,
+ process_status: ProcessStatus::Unknown(0),
+ status: None,
+ old_read_bytes: 0,
+ old_written_bytes: 0,
+ read_bytes: 0,
+ written_bytes: 0,
+ }
+ }
+
+ pub(crate) fn new(pid: Pid, parent: Option<Pid>, start_time: u64, run_time: u64) -> Process {
+ Process {
+ name: String::new(),
+ pid,
+ parent,
+ cmd: Vec::new(),
+ environ: Vec::new(),
+ exe: PathBuf::new(),
+ cwd: PathBuf::new(),
+ root: PathBuf::new(),
+ memory: 0,
+ virtual_memory: 0,
+ cpu_usage: 0.,
+ old_utime: 0,
+ old_stime: 0,
+ updated: true,
+ start_time,
+ run_time,
+ user_id: None,
+ group_id: None,
+ process_status: ProcessStatus::Unknown(0),
+ status: None,
+ old_read_bytes: 0,
+ old_written_bytes: 0,
+ read_bytes: 0,
+ written_bytes: 0,
+ }
+ }
+}
+
+impl ProcessExt for Process {
+ fn kill_with(&self, signal: Signal) -> Option<bool> {
+ let c_signal = crate::sys::system::convert_signal(signal)?;
+ unsafe { Some(kill(self.pid.0, c_signal) == 0) }
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+
+ fn cmd(&self) -> &[String] {
+ &self.cmd
+ }
+
+ fn exe(&self) -> &Path {
+ self.exe.as_path()
+ }
+
+ fn pid(&self) -> Pid {
+ self.pid
+ }
+
+ fn environ(&self) -> &[String] {
+ &self.environ
+ }
+
+ fn cwd(&self) -> &Path {
+ self.cwd.as_path()
+ }
+
+ fn root(&self) -> &Path {
+ self.root.as_path()
+ }
+
+ fn memory(&self) -> u64 {
+ self.memory
+ }
+
+ fn virtual_memory(&self) -> u64 {
+ self.virtual_memory
+ }
+
+ fn parent(&self) -> Option<Pid> {
+ self.parent
+ }
+
+ fn status(&self) -> ProcessStatus {
+ self.process_status
+ }
+
+ fn start_time(&self) -> u64 {
+ self.start_time
+ }
+
+ fn run_time(&self) -> u64 {
+ self.run_time
+ }
+
+ fn cpu_usage(&self) -> f32 {
+ self.cpu_usage
+ }
+
+ fn disk_usage(&self) -> DiskUsage {
+ DiskUsage {
+ read_bytes: self.read_bytes - self.old_read_bytes,
+ total_read_bytes: self.read_bytes,
+ written_bytes: self.written_bytes - self.old_written_bytes,
+ total_written_bytes: self.written_bytes,
+ }
+ }
+
+ fn user_id(&self) -> Option<&Uid> {
+ self.user_id.as_ref()
+ }
+
+ fn group_id(&self) -> Option<Gid> {
+ self.group_id
+ }
+}
+
+#[allow(deprecated)] // Because of libc::mach_absolute_time.
+pub(crate) fn compute_cpu_usage(
+ p: &mut Process,
+ task_info: libc::proc_taskinfo,
+ system_time: u64,
+ user_time: u64,
+ time_interval: Option<f64>,
+) {
+ if let Some(time_interval) = time_interval {
+ let total_existing_time = p.old_stime.saturating_add(p.old_utime);
+ if time_interval > 0.000001 && total_existing_time > 0 {
+ let total_current_time = task_info
+ .pti_total_system
+ .saturating_add(task_info.pti_total_user);
+
+ let total_time_diff = total_current_time.saturating_sub(total_existing_time);
+ p.cpu_usage = (total_time_diff as f64 / time_interval * 100.) as f32;
+ } else {
+ p.cpu_usage = 0.;
+ }
+ p.old_stime = task_info.pti_total_system;
+ p.old_utime = task_info.pti_total_user;
+ } else {
+ unsafe {
+ // This is the "backup way" of CPU computation.
+ let time = libc::mach_absolute_time();
+ let task_time = user_time
+ .saturating_add(system_time)
+ .saturating_add(task_info.pti_total_user)
+ .saturating_add(task_info.pti_total_system);
+
+ let system_time_delta = if task_time < p.old_utime {
+ task_time
+ } else {
+ task_time.saturating_sub(p.old_utime)
+ };
+ let time_delta = if time < p.old_stime {
+ time
+ } else {
+ time.saturating_sub(p.old_stime)
+ };
+ p.old_utime = task_time;
+ p.old_stime = time;
+ p.cpu_usage = if time_delta == 0 {
+ 0f32
+ } else {
+ (system_time_delta as f64 * 100f64 / time_delta as f64) as f32
+ };
+ }
+ }
+ p.updated = true;
+}
+
+/*pub fn set_time(p: &mut Process, utime: u64, stime: u64) {
+ p.old_utime = p.utime;
+ p.old_stime = p.stime;
+ p.utime = utime;
+ p.stime = stime;
+ p.updated = true;
+}*/
+
+#[inline]
+pub(crate) fn has_been_updated(p: &mut Process) -> bool {
+ let old = p.updated;
+ p.updated = false;
+ old
+}
+
+#[inline]
+pub(crate) fn force_update(p: &mut Process) {
+ p.updated = true;
+}
+
+unsafe fn get_task_info(pid: Pid) -> libc::proc_taskinfo {
+ let mut task_info = mem::zeroed::<libc::proc_taskinfo>();
+ // If it doesn't work, we just don't have memory information for this process
+ // so it's "fine".
+ libc::proc_pidinfo(
+ pid.0,
+ libc::PROC_PIDTASKINFO,
+ 0,
+ &mut task_info as *mut libc::proc_taskinfo as *mut c_void,
+ mem::size_of::<libc::proc_taskinfo>() as _,
+ );
+ task_info
+}
+
+#[inline]
+fn check_if_pid_is_alive(pid: Pid) -> bool {
+ unsafe { kill(pid.0, 0) == 0 }
+ // For the full complete check, it'd need to be (but that seems unneeded):
+ // unsafe {
+ // *libc::__errno_location() == libc::ESRCH
+ // }
+}
+
+#[inline]
+fn do_not_get_env_path(_: &str, _: &mut PathBuf, _: &mut bool) {}
+
+#[inline]
+fn do_get_env_path(env: &str, root: &mut PathBuf, check: &mut bool) {
+ if *check && env.starts_with("PATH=") {
+ *check = false;
+ *root = Path::new(&env[5..]).to_path_buf();
+ }
+}
+
+pub(crate) fn update_process(
+ wrap: &Wrap,
+ pid: Pid,
+ mut size: size_t,
+ time_interval: Option<f64>,
+ now: u64,
+ refresh_kind: ProcessRefreshKind,
+) -> Result<Option<Process>, ()> {
+ let mut proc_args = Vec::with_capacity(size as usize);
+
+ unsafe {
+ if let Some(ref mut p) = (*wrap.0.get()).get_mut(&pid) {
+ if p.memory == 0 {
+ // We don't have access to this process' information.
+ force_update(p);
+ return if check_if_pid_is_alive(pid) {
+ Ok(None)
+ } else {
+ Err(())
+ };
+ }
+ let task_info = get_task_info(pid);
+ let mut thread_info = mem::zeroed::<libc::proc_threadinfo>();
+ let (user_time, system_time, thread_status) = if libc::proc_pidinfo(
+ pid.0,
+ libc::PROC_PIDTHREADINFO,
+ 0,
+ &mut thread_info as *mut libc::proc_threadinfo as *mut c_void,
+ mem::size_of::<libc::proc_threadinfo>() as _,
+ ) != 0
+ {
+ (
+ thread_info.pth_user_time,
+ thread_info.pth_system_time,
+ Some(ThreadStatus::from(thread_info.pth_run_state)),
+ )
+ } else {
+ // It very likely means that the process is dead...
+ return if check_if_pid_is_alive(pid) {
+ Ok(None)
+ } else {
+ Err(())
+ };
+ };
+ p.status = thread_status;
+ if refresh_kind.cpu() {
+ compute_cpu_usage(p, task_info, system_time, user_time, time_interval);
+ }
+
+ p.memory = task_info.pti_resident_size / 1_000;
+ p.virtual_memory = task_info.pti_virtual_size / 1_000;
+ if refresh_kind.disk_usage() {
+ update_proc_disk_activity(p);
+ }
+ return Ok(None);
+ }
+
+ let mut vnodepathinfo = mem::zeroed::<libc::proc_vnodepathinfo>();
+ let result = libc::proc_pidinfo(
+ pid.0,
+ libc::PROC_PIDVNODEPATHINFO,
+ 0,
+ &mut vnodepathinfo as *mut _ as *mut _,
+ mem::size_of::<libc::proc_vnodepathinfo>() as _,
+ );
+ let cwd = if result > 0 {
+ let buffer = vnodepathinfo.pvi_cdir.vip_path;
+ let buffer = CStr::from_ptr(buffer.as_ptr() as _);
+ buffer
+ .to_str()
+ .map(PathBuf::from)
+ .unwrap_or_else(|_| PathBuf::new())
+ } else {
+ PathBuf::new()
+ };
+
+ let mut info = mem::zeroed::<libc::proc_bsdinfo>();
+ if libc::proc_pidinfo(
+ pid.0,
+ libc::PROC_PIDTBSDINFO,
+ 0,
+ &mut info as *mut _ as *mut _,
+ mem::size_of::<libc::proc_bsdinfo>() as _,
+ ) != mem::size_of::<libc::proc_bsdinfo>() as _
+ {
+ let mut buffer: Vec<u8> = Vec::with_capacity(libc::PROC_PIDPATHINFO_MAXSIZE as _);
+ match libc::proc_pidpath(
+ pid.0,
+ buffer.as_mut_ptr() as *mut _,
+ libc::PROC_PIDPATHINFO_MAXSIZE as _,
+ ) {
+ x if x > 0 => {
+ buffer.set_len(x as _);
+ let tmp = String::from_utf8_unchecked(buffer);
+ let exe = PathBuf::from(tmp);
+ let name = exe
+ .file_name()
+ .and_then(|x| x.to_str())
+ .unwrap_or("")
+ .to_owned();
+ return Ok(Some(Process::new_empty(pid, exe, name, cwd)));
+ }
+ _ => {}
+ }
+ return Err(());
+ }
+ let parent = match info.pbi_ppid as i32 {
+ 0 => None,
+ p => Some(Pid(p)),
+ };
+
+ let ptr: *mut u8 = proc_args.as_mut_slice().as_mut_ptr();
+ let mut mib = [libc::CTL_KERN, libc::KERN_PROCARGS2, pid.0 as _];
+ /*
+ * /---------------\ 0x00000000
+ * | ::::::::::::: |
+ * |---------------| <-- Beginning of data returned by sysctl() is here.
+ * | argc |
+ * |---------------|
+ * | exec_path |
+ * |---------------|
+ * | 0 |
+ * |---------------|
+ * | arg[0] |
+ * |---------------|
+ * | 0 |
+ * |---------------|
+ * | arg[n] |
+ * |---------------|
+ * | 0 |
+ * |---------------|
+ * | env[0] |
+ * |---------------|
+ * | 0 |
+ * |---------------|
+ * | env[n] |
+ * |---------------|
+ * | ::::::::::::: |
+ * |---------------| <-- Top of stack.
+ * : :
+ * : :
+ * \---------------/ 0xffffffff
+ */
+ if libc::sysctl(
+ mib.as_mut_ptr(),
+ mib.len() as _,
+ ptr as *mut c_void,
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ ) == -1
+ {
+ return Err(()); // not enough rights I assume?
+ }
+ let mut n_args: c_int = 0;
+ libc::memcpy(
+ (&mut n_args) as *mut c_int as *mut c_void,
+ ptr as *const c_void,
+ mem::size_of::<c_int>(),
+ );
+
+ let mut cp = ptr.add(mem::size_of::<c_int>());
+ let mut start = cp;
+
+ let start_time = info.pbi_start_tvsec;
+ let run_time = now.saturating_sub(start_time);
+
+ let mut p = if cp < ptr.add(size) {
+ while cp < ptr.add(size) && *cp != 0 {
+ cp = cp.offset(1);
+ }
+ let exe = Path::new(get_unchecked_str(cp, start).as_str()).to_path_buf();
+ let name = exe
+ .file_name()
+ .and_then(|x| x.to_str())
+ .unwrap_or("")
+ .to_owned();
+ while cp < ptr.add(size) && *cp == 0 {
+ cp = cp.offset(1);
+ }
+ start = cp;
+ let mut c = 0;
+ let mut cmd = Vec::with_capacity(n_args as usize);
+ while c < n_args && cp < ptr.add(size) {
+ if *cp == 0 {
+ c += 1;
+ cmd.push(get_unchecked_str(cp, start));
+ start = cp.offset(1);
+ }
+ cp = cp.offset(1);
+ }
+
+ #[inline]
+ unsafe fn get_environ<F: Fn(&str, &mut PathBuf, &mut bool)>(
+ ptr: *mut u8,
+ mut cp: *mut u8,
+ size: size_t,
+ mut root: PathBuf,
+ callback: F,
+ ) -> (Vec<String>, PathBuf) {
+ let mut environ = Vec::with_capacity(10);
+ let mut start = cp;
+ let mut check = true;
+ while cp < ptr.add(size) {
+ if *cp == 0 {
+ if cp == start {
+ break;
+ }
+ let e = get_unchecked_str(cp, start);
+ callback(&e, &mut root, &mut check);
+ environ.push(e);
+ start = cp.offset(1);
+ }
+ cp = cp.offset(1);
+ }
+ (environ, root)
+ }
+
+ let (environ, root) = if exe.is_absolute() {
+ if let Some(parent_path) = exe.parent() {
+ get_environ(
+ ptr,
+ cp,
+ size,
+ parent_path.to_path_buf(),
+ do_not_get_env_path,
+ )
+ } else {
+ get_environ(ptr, cp, size, PathBuf::new(), do_get_env_path)
+ }
+ } else {
+ get_environ(ptr, cp, size, PathBuf::new(), do_get_env_path)
+ };
+ let mut p = Process::new(pid, parent, start_time, run_time);
+
+ p.exe = exe;
+ p.name = name;
+ p.cwd = cwd;
+ p.cmd = parse_command_line(&cmd);
+ p.environ = environ;
+ p.root = root;
+ p
+ } else {
+ Process::new(pid, parent, start_time, run_time)
+ };
+
+ let task_info = get_task_info(pid);
+
+ p.memory = task_info.pti_resident_size / 1_000;
+ p.virtual_memory = task_info.pti_virtual_size / 1_000;
+
+ p.user_id = Some(Uid(info.pbi_uid));
+ p.group_id = Some(Gid(info.pbi_gid));
+ p.process_status = ProcessStatus::from(info.pbi_status);
+ if refresh_kind.disk_usage() {
+ update_proc_disk_activity(&mut p);
+ }
+ Ok(Some(p))
+ }
+}
+
+fn update_proc_disk_activity(p: &mut Process) {
+ p.old_read_bytes = p.read_bytes;
+ p.old_written_bytes = p.written_bytes;
+
+ let mut pidrusage = MaybeUninit::<libc::rusage_info_v2>::uninit();
+
+ unsafe {
+ let retval = libc::proc_pid_rusage(
+ p.pid().0 as _,
+ libc::RUSAGE_INFO_V2,
+ pidrusage.as_mut_ptr() as _,
+ );
+
+ if retval < 0 {
+ sysinfo_debug!("proc_pid_rusage failed: {:?}", retval);
+ } else {
+ let pidrusage = pidrusage.assume_init();
+ p.read_bytes = pidrusage.ri_diskio_bytesread;
+ p.written_bytes = pidrusage.ri_diskio_byteswritten;
+ }
+ }
+}
+
+#[allow(clippy::uninit_vec)]
+pub(crate) fn get_proc_list() -> Option<Vec<Pid>> {
+ unsafe {
+ let count = libc::proc_listallpids(::std::ptr::null_mut(), 0);
+ if count < 1 {
+ return None;
+ }
+ let mut pids: Vec<Pid> = Vec::with_capacity(count as usize);
+ pids.set_len(count as usize);
+ let count = count * mem::size_of::<Pid>() as i32;
+ let x = libc::proc_listallpids(pids.as_mut_ptr() as *mut c_void, count);
+
+ if x < 1 || x as usize >= pids.len() {
+ None
+ } else {
+ pids.set_len(x as usize);
+ Some(pids)
+ }
+ }
+}
+
+unsafe fn get_unchecked_str(cp: *mut u8, start: *mut u8) -> String {
+ let len = cp as usize - start as usize;
+ let part = Vec::from_raw_parts(start, len, len);
+ let tmp = String::from_utf8_unchecked(part.clone());
+ mem::forget(part);
+ tmp
+}
+
+fn parse_command_line<T: Deref<Target = str> + Borrow<str>>(cmd: &[T]) -> Vec<String> {
+ let mut x = 0;
+ let mut command = Vec::with_capacity(cmd.len());
+ while x < cmd.len() {
+ let mut y = x;
+ if cmd[y].starts_with('\'') || cmd[y].starts_with('"') {
+ let c = if cmd[y].starts_with('\'') { '\'' } else { '"' };
+ while y < cmd.len() && !cmd[y].ends_with(c) {
+ y += 1;
+ }
+ command.push(cmd[x..y].join(" "));
+ x = y;
+ } else {
+ command.push(cmd[x].to_owned());
+ }
+ x += 1;
+ }
+ command
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_get_path() {
+ let mut path = PathBuf::new();
+ let mut check = true;
+
+ do_get_env_path("PATH=tadam", &mut path, &mut check);
+
+ assert!(!check);
+ assert_eq!(path, PathBuf::from("tadam"));
+ }
+}