#![allow(non_snake_case)]
#![allow(dead_code)]
use core::any::Any;
use std::mem::{size_of, size_of_val, zeroed};
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use crate::ported::crt::{ColorElements as CE, ColorScheme};
use crate::ported::darwin::darwinmachine::DarwinMachine;
use crate::ported::darwin::darwinprocesstable::{kinfo_proc, DarwinProcessTable};
use crate::ported::darwin::platform::Platform_machTicksToNanoseconds;
use crate::ported::machine::Machine;
use crate::ported::object::{Object, ObjectClass};
use crate::ported::process::{
Process, ProcessClass, ProcessState, Process_compareByKey_Base, Process_fillStarttimeBuffer,
Process_getPid, Process_init, Process_setParent, Process_setPid, Process_setThreadGroup,
Process_updateCPUFieldWidths, Process_updateCmdline, Process_updateComm, Process_updateExe,
Process_writeField, PROCESS_FLAG_CWD,
};
use crate::ported::processtable::ProcessTable_getProcess;
use crate::ported::richstring::{RichString, RichString_appendWide};
use crate::ported::row::{spaceship_number, Row, RowClass};
use crate::ported::settings::RowField;
extern "C" {
fn mach_port_deallocate(
task: libc::mach_port_t,
name: libc::mach_port_t,
) -> libc::kern_return_t;
}
#[repr(C)]
pub struct DarwinProcess {
pub super_: Process,
pub utime: u64,
pub stime: u64,
pub taskAccess: bool,
pub translated: bool,
}
impl Object for DarwinProcess {
fn klass(&self) -> &'static ObjectClass {
self.super_.klass()
}
fn display(&self, out: &mut RichString) {
self.super_.display(out)
}
fn compare(&self, other: &dyn Object) -> i32 {
self.super_.compare(other)
}
fn row_class(&self) -> Option<&'static RowClass> {
self.super_.row_class()
}
fn process_class(&self) -> Option<&'static ProcessClass> {
self.super_.process_class()
}
fn as_row(&self) -> Option<&Row> {
Some(&self.super_.super_)
}
fn as_row_mut(&mut self) -> Option<&mut Row> {
Some(&mut self.super_.super_)
}
fn as_process(&self) -> Option<&Process> {
Some(&self.super_)
}
fn as_process_mut(&mut self) -> Option<&mut Process> {
Some(&mut self.super_)
}
}
pub fn DarwinProcess_new(host: *const Machine) -> Box<DarwinProcess> {
let mut this = Box::new(DarwinProcess {
super_: Process::default(),
utime: 0,
stime: 0,
taskAccess: true,
translated: false,
});
Process_init(&mut this.super_, host as *const c_void);
this.super_.state = ProcessState::UNKNOWN;
this
}
pub fn Process_delete() {
todo!("port of DarwinProcess.c:71 — pure free() teardown; Rust Drop handles it")
}
const TRANSLATED: RowField = 100;
pub fn DarwinProcess_rowWriteField(super_: &dyn Object, str: &mut RichString, field: RowField) {
let dp = (super_ as &dyn Any)
.downcast_ref::<DarwinProcess>()
.expect("DarwinProcess_rowWriteField: row is not a DarwinProcess");
let scheme = ColorScheme::active();
let attr = CE::DEFAULT_COLOR.packed(scheme);
let buffer: String;
match field {
f if f == TRANSLATED => {
buffer = format!("{} ", if dp.translated { 'T' } else { 'N' });
}
_ => {
Process_writeField(&dp.super_, str, field);
return;
}
}
RichString_appendWide(str, attr, buffer.as_bytes());
}
pub fn DarwinProcess_compareByKey(v1: &dyn Object, v2: &dyn Object, key: RowField) -> i32 {
let p1 = (v1 as &dyn Any)
.downcast_ref::<DarwinProcess>()
.expect("DarwinProcess_compareByKey: v1 is not a DarwinProcess");
let p2 = (v2 as &dyn Any)
.downcast_ref::<DarwinProcess>()
.expect("DarwinProcess_compareByKey: v2 is not a DarwinProcess");
match key {
k if k == TRANSLATED => spaceship_number!(p1.translated as i32, p2.translated as i32),
_ => Process_compareByKey_Base(&p1.super_, &p2.super_, key),
}
}
pub fn DarwinProcess_updateExe(pid: libc::pid_t, proc: &mut Process) {
let mut path = [0u8; libc::PROC_PIDPATHINFO_MAXSIZE as usize];
let r = unsafe { libc::proc_pidpath(pid, path.as_mut_ptr() as *mut c_void, path.len() as u32) };
if r <= 0 {
return;
}
let exe = String::from_utf8_lossy(&path[..r as usize]);
Process_updateExe(proc, Some(&exe));
}
pub fn DarwinProcess_updateCwd(pid: libc::pid_t, proc: &mut Process) {
let mut vpi: libc::proc_vnodepathinfo = unsafe { zeroed() };
let r = unsafe {
libc::proc_pidinfo(
pid,
libc::PROC_PIDVNODEPATHINFO,
0,
&mut vpi as *mut libc::proc_vnodepathinfo as *mut c_void,
size_of::<libc::proc_vnodepathinfo>() as c_int,
)
};
if r <= 0 {
proc.procCwd = None;
return;
}
let path: &[u8] = unsafe {
core::slice::from_raw_parts(
vpi.pvi_cdir.vip_path.as_ptr() as *const u8,
size_of_val(&vpi.pvi_cdir.vip_path),
)
};
if path[0] == 0 {
proc.procCwd = None;
return;
}
let end = path.iter().position(|&b| b == 0).unwrap_or(path.len());
proc.procCwd = Some(String::from_utf8_lossy(&path[..end]).into_owned());
}
pub fn DarwinProcess_updateCmdLine(k: &kinfo_proc, proc: &mut Process) {
let pid = k.kp_proc.p_pid;
let comm_field: &[u8] = unsafe {
core::slice::from_raw_parts(
k.kp_proc.p_comm.as_ptr() as *const u8,
k.kp_proc.p_comm.len(),
)
};
let comm_len = comm_field
.iter()
.position(|&b| b == 0)
.unwrap_or(comm_field.len());
let comm = String::from_utf8_lossy(&comm_field[..comm_len]).into_owned();
Process_updateComm(proc, Some(&comm));
let parsed = (|| -> Option<(String, usize)> {
let mut argmax: c_int = 0;
let mut sz = size_of::<c_int>();
let mut mib_max = [libc::CTL_KERN, libc::KERN_ARGMAX];
if unsafe {
libc::sysctl(
mib_max.as_mut_ptr(),
2,
&mut argmax as *mut c_int as *mut c_void,
&mut sz,
ptr::null_mut(),
0,
)
} == -1
|| argmax <= 0
{
return None;
}
let mut procargs = vec![0u8; argmax as usize];
let mut size = argmax as usize;
let mut mib_args = [libc::CTL_KERN, libc::KERN_PROCARGS2, pid];
if unsafe {
libc::sysctl(
mib_args.as_mut_ptr(),
3,
procargs.as_mut_ptr() as *mut c_void,
&mut size,
ptr::null_mut(),
0,
)
} == -1
{
return None;
}
procargs.truncate(size);
if procargs.len() < size_of::<c_int>() {
return None;
}
let nargs = i32::from_ne_bytes([procargs[0], procargs[1], procargs[2], procargs[3]]);
let n = procargs.len();
let mut cp = size_of::<c_int>();
while cp < n && procargs[cp] != 0 {
cp += 1;
}
if cp == n {
return None;
}
while cp < n && procargs[cp] == 0 {
cp += 1;
}
if cp == n {
return None;
}
let sp = cp;
let mut c = 0i32;
let mut np: Option<usize> = None;
let mut end = 0usize;
while c < nargs && cp < n {
if procargs[cp] == 0 {
c += 1;
if let Some(prev) = np {
procargs[prev] = b' ';
}
np = Some(cp);
if end == 0 {
end = cp - sp;
}
}
cp += 1;
}
let np = np?;
if np == sp {
return None;
}
if end == 0 {
end = np - sp;
}
let cmdline = String::from_utf8_lossy(&procargs[sp..np]).into_owned();
let end = end.min(cmdline.len());
Some((cmdline, end))
})();
match parsed {
Some((cmdline, end)) => Process_updateCmdline(proc, Some(&cmdline), 0, end),
None => {
let end = comm.len();
let arg = if comm.is_empty() {
None
} else {
Some(comm.as_str())
};
Process_updateCmdline(proc, arg, 0, end);
}
}
}
const NODEV: libc::dev_t = -1;
const MAXNAMLEN: c_int = 255;
extern "C" {
fn devname_r(dev: libc::dev_t, mode: libc::mode_t, buf: *mut c_char, len: c_int)
-> *mut c_char;
}
pub fn DarwinProcess_getDevname(dev: libc::dev_t) -> Option<String> {
if dev == NODEV {
return None;
}
let mut buf = [0 as c_char; 6 + MAXNAMLEN as usize];
let name = unsafe { devname_r(dev, libc::S_IFCHR, buf.as_mut_ptr(), MAXNAMLEN) };
if name.is_null() {
return None;
}
let s = unsafe { std::ffi::CStr::from_ptr(name) }
.to_string_lossy()
.into_owned();
Some(s)
}
const P_TRANSLATED: c_int = 0x00020000;
const PROCESS_FLAG_TTY: u32 = 0x00000100;
pub fn DarwinProcess_setFromKInfoProc(proc: &mut Process, ps: &kinfo_proc, exists: bool) {
let ep = &ps.kp_proc;
let flags: u32 = unsafe {
let host = proc.super_.host as *const Machine;
host.as_ref()
.and_then(|m| m.settings.as_ref())
.and_then(|s| s.screens.get(s.ssIndex as usize))
.map_or(0, |ss| ss.flags)
};
if !exists {
Process_setPid(proc, ep.p_pid);
Process_setThreadGroup(proc, ep.p_pid);
Process_setParent(proc, ps.kp_eproc.e_ppid);
proc.pgrp = ps.kp_eproc.e_pgid;
proc.session = 0;
proc.tpgid = ps.kp_eproc.e_tpgid;
proc.isKernelThread = false;
proc.isUserlandThread = false;
let translated = (ep.p_flag & P_TRANSLATED) != 0;
unsafe {
(*(proc as *mut Process as *mut DarwinProcess)).translated = translated;
}
proc.tty_nr = ps.kp_eproc.e_tdev as u64;
proc.tty_name = None;
proc.starttime_ctime = ep.p_starttime.tv_sec;
Process_fillStarttimeBuffer(proc);
DarwinProcess_updateExe(ep.p_pid, proc);
DarwinProcess_updateCmdLine(ps, proc);
if flags & PROCESS_FLAG_CWD != 0 {
DarwinProcess_updateCwd(ep.p_pid, proc);
}
}
if proc.tty_name.is_none() && proc.tty_nr as libc::dev_t != NODEV {
if flags & PROCESS_FLAG_TTY != 0 {
proc.tty_name = DarwinProcess_getDevname(proc.tty_nr as libc::dev_t);
if proc.tty_name.is_none() {
proc.tty_nr = NODEV as u64;
}
}
}
proc.nice = ep.p_nice as i32;
proc.priority = ep.p_priority as i64;
proc.state = if ep.p_stat as u32 == libc::SZOMB {
ProcessState::ZOMBIE
} else {
ProcessState::UNKNOWN
};
proc.super_.updated = true;
}
const ONE_K: u64 = 1024;
pub fn DarwinProcess_setFromLibprocPidinfo(
proc: &mut DarwinProcess,
dpt: &mut DarwinProcessTable,
timeIntervalNS: f64,
) {
let mut pti: libc::proc_taskinfo = unsafe { zeroed() };
let size = size_of::<libc::proc_taskinfo>() as c_int;
let got = unsafe {
libc::proc_pidinfo(
Process_getPid(&proc.super_),
libc::PROC_PIDTASKINFO,
0,
&mut pti as *mut libc::proc_taskinfo as *mut c_void,
size,
)
};
if got != size {
proc.taskAccess = false;
return;
}
let dhost = proc.super_.super_.host as *const DarwinMachine;
let max_mem = unsafe { (*dhost).host_info.max_mem };
let total_existing_time_ns = proc.stime + proc.utime;
let user_time_ns = Platform_machTicksToNanoseconds(pti.pti_total_user);
let system_time_ns = Platform_machTicksToNanoseconds(pti.pti_total_system);
let total_current_time_ns = user_time_ns + system_time_ns;
if total_existing_time_ns < total_current_time_ns {
let total_time_diff_ns = total_current_time_ns - total_existing_time_ns;
proc.super_.percent_cpu = ((total_time_diff_ns as f64 / timeIntervalNS) * 100.0) as f32;
} else {
proc.super_.percent_cpu = 0.0;
}
Process_updateCPUFieldWidths(proc.super_.percent_cpu);
proc.super_.state = if pti.pti_numrunning > 0 {
ProcessState::RUNNING
} else {
ProcessState::SLEEPING
};
proc.super_.time = total_current_time_ns / 10_000_000;
proc.super_.nlwp = pti.pti_threadnum as i64;
proc.super_.m_virt = (pti.pti_virtual_size / ONE_K) as i64;
proc.super_.m_resident = (pti.pti_resident_size / ONE_K) as i64;
proc.super_.majflt = pti.pti_faults as u64;
proc.super_.percent_mem = (pti.pti_resident_size as f64 * 100.0 / max_mem as f64) as f32;
proc.stime = system_time_ns;
proc.utime = user_time_ns;
dpt.super_.userlandThreads += pti.pti_threadnum as u32;
dpt.super_.totalTasks += pti.pti_threadnum as u32;
dpt.super_.runningTasks += pti.pti_numrunning as u32;
}
#[allow(deprecated)]
pub unsafe fn DarwinProcess_scanThreads(dp: *mut DarwinProcess, dpt: *mut DarwinProcessTable) {
let proc_: *mut Process = &mut (*dp).super_;
if !(*dp).taskAccess {
return;
}
if (*proc_).state == ProcessState::ZOMBIE {
return;
}
let pid = Process_getPid(&*proc_);
let mut task: libc::task_t = 0;
let ret = libc::task_for_pid(libc::mach_task_self(), pid, &mut task);
if ret != libc::KERN_SUCCESS {
(*dp).taskAccess = false;
return;
}
let mut thread_list: libc::thread_act_array_t = ptr::null_mut();
let mut thread_count: libc::mach_msg_type_number_t = 0;
let ret = libc::task_threads(task, &mut thread_list, &mut thread_count);
if ret != libc::KERN_SUCCESS {
(*dp).taskAccess = false;
mach_port_deallocate(libc::mach_task_self(), task);
return;
}
let host = (*dpt).super_.super_.host;
let hideUserlandThreads = (*host)
.settings
.as_ref()
.map(|s| s.hideUserlandThreads)
.unwrap_or(false);
let mut isProcessStuck = false;
for i in 0..thread_count as usize {
let thread = *thread_list.add(i);
let mut identifer_info: libc::thread_identifier_info_data_t = zeroed();
let mut identifer_info_count = libc::THREAD_IDENTIFIER_INFO_COUNT;
let ret = libc::thread_info(
thread,
libc::THREAD_IDENTIFIER_INFO as libc::thread_flavor_t,
&mut identifer_info as *mut libc::thread_identifier_info_data_t
as libc::thread_info_t,
&mut identifer_info_count,
);
if ret != libc::KERN_SUCCESS {
continue;
}
let tid = identifer_info.thread_id;
let (pre_existing, idx) =
ProcessTable_getProcess(&mut (*dpt).super_, tid as libc::pid_t, |h| {
DarwinProcess_new(h) as Box<dyn Object>
});
let tdproc: *mut DarwinProcess = {
let rows = &mut (*dpt).super_.super_.rows;
let obj: &mut dyn Object = rows[idx].as_mut().unwrap().as_mut();
let any: &mut dyn Any = obj;
any.downcast_mut::<DarwinProcess>().unwrap()
};
(*tdproc).super_.super_.updated = true;
(*dpt).super_.totalTasks += 1;
if hideUserlandThreads {
(*tdproc).super_.super_.show = false;
continue;
}
let tprocessPid = Process_getPid(&(*tdproc).super_);
debug_assert!(tprocessPid >= 0);
debug_assert_eq!(tprocessPid as u64, tid);
Process_setParent(&mut (*tdproc).super_, pid);
Process_setThreadGroup(&mut (*tdproc).super_, pid);
(*tdproc).super_.super_.show = true;
(*tdproc).super_.isUserlandThread = true;
(*tdproc).super_.st_uid = (*proc_).st_uid;
(*tdproc).super_.user = (*proc_).user.clone();
let mut extended_info: libc::thread_extended_info_data_t = zeroed();
let mut extended_info_count = libc::THREAD_EXTENDED_INFO_COUNT;
let ret = libc::thread_info(
thread,
libc::THREAD_EXTENDED_INFO as libc::thread_flavor_t,
&mut extended_info as *mut libc::thread_extended_info_data_t as libc::thread_info_t,
&mut extended_info_count,
);
if ret != libc::KERN_SUCCESS {
continue;
}
(*tdproc).super_.percent_cpu = extended_info.pth_cpu_usage as f32 / 10.0;
(*tdproc).stime = extended_info.pth_system_time;
(*tdproc).utime = extended_info.pth_user_time;
(*tdproc).super_.time =
(extended_info.pth_system_time + extended_info.pth_user_time) / 10_000_000;
(*tdproc).super_.priority = extended_info.pth_curpri as i64;
if extended_info.pth_run_state == libc::TH_STATE_UNINTERRUPTIBLE {
isProcessStuck = true;
(*tdproc).super_.state = ProcessState::UNINTERRUPTIBLE_WAIT;
}
let ext_name: Option<String> = if extended_info.pth_name[0] != 0 {
Some(
std::ffi::CStr::from_ptr(extended_info.pth_name.as_ptr())
.to_string_lossy()
.into_owned(),
)
} else {
None
};
let name: Option<&str> = match &ext_name {
Some(s) => Some(s.as_str()),
None => (*proc_).procComm.as_deref(),
};
let namelen = name.map(str::len).unwrap_or(0);
Process_updateCmdline(&mut (*tdproc).super_, name, 0, namelen);
let _ = pre_existing;
}
if isProcessStuck {
(*dp).super_.state = ProcessState::UNINTERRUPTIBLE_WAIT;
}
libc::vm_deallocate(
libc::mach_task_self(),
thread_list as libc::vm_address_t,
size_of::<libc::thread_act_array_t>() * thread_count as usize,
);
mach_port_deallocate(libc::mach_task_self(), task);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_sets_darwin_defaults_and_unknown_state() {
let host = 0xF00D as *const Machine;
let p = DarwinProcess_new(host);
assert_eq!(p.utime, 0);
assert_eq!(p.stime, 0);
assert!(p.taskAccess);
assert!(!p.translated);
assert_eq!(p.super_.state, ProcessState::UNKNOWN);
assert_eq!(p.super_.super_.host, host as *const c_void);
}
#[test]
fn super_is_at_offset_zero_for_sound_downcast() {
assert_eq!(core::mem::offset_of!(DarwinProcess, super_), 0);
let host = 0xF00D as *const Machine;
let dp = DarwinProcess_new(host);
let base: *const Process = &dp.super_;
let back = base as *const DarwinProcess;
assert_eq!(back, &*dp as *const DarwinProcess);
}
#[test]
fn updateExe_sets_exe_from_own_pid() {
let host = 0xF00D as *const Machine;
let mut dp = DarwinProcess_new(host);
let pid = unsafe { libc::getpid() };
DarwinProcess_updateExe(pid, &mut dp.super_);
let exe = dp.super_.procExe.as_deref().unwrap_or("");
assert!(!exe.is_empty());
assert!(exe.starts_with('/'));
}
#[test]
fn updateCwd_sets_absolute_cwd_from_own_pid() {
let host = 0xF00D as *const Machine;
let mut dp = DarwinProcess_new(host);
let pid = unsafe { libc::getpid() };
DarwinProcess_updateCwd(pid, &mut dp.super_);
let cwd = dp.super_.procCwd.as_deref().expect("own cwd resolves");
assert!(cwd.starts_with('/'));
}
#[test]
fn updateCmdLine_sets_comm_and_cmdline_from_own_procargs() {
let pid = unsafe { libc::getpid() };
let mut mib = [libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_PID, pid];
let mut kp: kinfo_proc = unsafe { zeroed() };
let mut size = size_of::<kinfo_proc>();
let rc = unsafe {
libc::sysctl(
mib.as_mut_ptr(),
4,
&mut kp as *mut kinfo_proc as *mut c_void,
&mut size,
ptr::null_mut(),
0,
)
};
assert_eq!(rc, 0);
let host = 0xF00D as *const Machine;
let mut dp = DarwinProcess_new(host);
DarwinProcess_updateCmdLine(&kp, &mut dp.super_);
assert!(dp.super_.procComm.is_some());
let cmdline = dp.super_.cmdline.as_deref().expect("own cmdline resolves");
assert!(!cmdline.is_empty());
assert!(dp.super_.cmdlineBasenameEnd <= cmdline.len());
}
#[test]
fn getDevname_resolves_dev_null_and_rejects_nodev() {
assert_eq!(DarwinProcess_getDevname(NODEV), None);
let mut st: libc::stat = unsafe { zeroed() };
let rc = unsafe { libc::stat(b"/dev/null\0".as_ptr() as *const c_char, &mut st) };
assert_eq!(rc, 0);
assert_eq!(
DarwinProcess_getDevname(st.st_rdev).as_deref(),
Some("null")
);
}
#[test]
fn setFromKInfoProc_fills_identity_from_own_kinfo() {
use crate::ported::process::{Process_getParent, Process_getPid};
use crate::ported::settings::{ScreenSettings, Settings};
let pid = unsafe { libc::getpid() };
let ppid = unsafe { libc::getppid() };
let mut mib = [libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_PID, pid];
let mut kp: kinfo_proc = unsafe { zeroed() };
let mut size = size_of::<kinfo_proc>();
let rc = unsafe {
libc::sysctl(
mib.as_mut_ptr(),
4,
&mut kp as *mut kinfo_proc as *mut c_void,
&mut size,
ptr::null_mut(),
0,
)
};
assert_eq!(rc, 0);
let mut machine = Machine::default();
machine.realtimeMs = 1_700_000_000_000;
machine.settings = Some(Settings {
screens: vec![ScreenSettings::default()],
ssIndex: 0,
..Default::default()
});
let mut dp = DarwinProcess_new(&machine as *const Machine);
DarwinProcess_setFromKInfoProc(&mut dp.super_, &kp, false);
assert_eq!(Process_getPid(&dp.super_), pid);
assert_eq!(Process_getParent(&dp.super_), ppid);
assert_eq!(dp.super_.pgrp, kp.kp_eproc.e_pgid);
assert_eq!(dp.super_.tpgid, kp.kp_eproc.e_tpgid);
assert!(dp.super_.cmdline.is_some());
assert!(dp.super_.super_.updated);
assert_eq!(dp.super_.state, ProcessState::UNKNOWN);
}
#[test]
fn setFromLibprocPidinfo_fills_stats_for_own_pid() {
use crate::ported::darwin::darwinmachine::{host_basic_info_data_t, DarwinMachine};
use crate::ported::darwin::darwinprocesstable::DarwinProcessTable;
use crate::ported::linux::linuxmachine::ZfsArcStats;
use crate::ported::processtable::ProcessTable;
let dm = Box::new(DarwinMachine {
super_: Machine::default(),
host_info: host_basic_info_data_t {
max_mem: 16u64 << 30,
..Default::default()
},
vm_stats: unsafe { zeroed() },
prev_load: ptr::null_mut(),
curr_load: ptr::null_mut(),
GPUService: 0,
zfs: ZfsArcStats::default(),
});
let mut dp = DarwinProcess_new(&dm.super_ as *const Machine);
Process_setPid(&mut dp.super_, unsafe { libc::getpid() });
let mut dpt = Box::new(DarwinProcessTable {
super_: ProcessTable::empty(),
global_diff: 0,
});
DarwinProcess_setFromLibprocPidinfo(&mut dp, &mut dpt, 1e9);
assert!(dp.taskAccess);
assert!(dp.super_.nlwp >= 1); assert!(dp.super_.m_resident > 0); assert!(dp.super_.percent_mem >= 0.0);
assert!(dpt.super_.totalTasks >= 1);
assert!(matches!(
dp.super_.state,
ProcessState::RUNNING | ProcessState::SLEEPING
));
}
#[test]
fn scanThreads_enumerates_own_threads() {
use crate::ported::darwin::darwinmachine::host_basic_info_data_t;
use crate::ported::darwin::darwinprocesstable::ProcessTable_new;
use crate::ported::linux::linuxmachine::ZfsArcStats;
use crate::ported::settings::Settings;
let mut dm = Box::new(DarwinMachine {
super_: Machine::default(),
host_info: host_basic_info_data_t::default(),
vm_stats: unsafe { zeroed() },
prev_load: ptr::null_mut(),
curr_load: ptr::null_mut(),
GPUService: 0,
zfs: ZfsArcStats::default(),
});
dm.super_.settings = Some(Settings {
hideUserlandThreads: false,
..Default::default()
});
let mut dpt = ProcessTable_new(&dm.super_ as *const Machine, None);
let mypid = unsafe { libc::getpid() };
let (_pre, idx) = ProcessTable_getProcess(&mut dpt.super_, mypid, |h| {
DarwinProcess_new(h) as Box<dyn Object>
});
let dp: *mut DarwinProcess = {
let obj: &mut dyn Object = dpt.super_.super_.rows[idx].as_mut().unwrap().as_mut();
(obj as &mut dyn Any)
.downcast_mut::<DarwinProcess>()
.unwrap()
};
unsafe {
(*dp).taskAccess = true;
(*dp).super_.state = ProcessState::RUNNING;
}
let rows_before = dpt.super_.super_.rows.len();
let dpt_ptr: *mut DarwinProcessTable = &mut *dpt;
unsafe { DarwinProcess_scanThreads(dp, dpt_ptr) };
assert!(unsafe { (*dp).taskAccess });
assert!(
dpt.super_.super_.rows.len() > rows_before,
"thread rows were added"
);
assert!(dpt.super_.totalTasks >= 1);
let thread_rows = dpt
.super_
.super_
.rows
.iter()
.flatten()
.filter(|o| o.as_process().map(|p| p.isUserlandThread).unwrap_or(false))
.count();
assert!(thread_rows >= 1, "at least one userland thread row");
}
}