#[cfg(target_os = "macos")]
use crate::collection::processes::macos::sysctl_bindings;
use cfg_if::cfg_if;
use std::{io, time::Duration};
use itertools::Itertools;
use nohash::IntMap;
use sysinfo::{ProcessStatus, System};
use super::{ProcessHarvest, process_status_str};
use crate::collection::{Pid, error::CollectionResult, processes::UserTable};
fn get_nice(pid: Pid) -> i32 {
cfg_if! {
if #[cfg(target_os = "freebsd")] {
unsafe { libc::getpriority(libc::PRIO_PROCESS, pid) }
} else if #[cfg(target_os = "macos")] {
unsafe { libc::getpriority(libc::PRIO_PROCESS, pid as u32) }
} else {
0
}
}
}
fn get_priority(pid: Pid) -> i32 {
cfg_if! {
if #[cfg(target_os = "macos")] {
if let Ok(kinfo) = sysctl_bindings::kinfo_process(pid) {
kinfo.kp_proc.p_priority as i32
} else {
0
}
} else if #[cfg(target_os = "freebsd")] {
use libc::{c_int, c_void};
use std::{mem, ptr};
let mib = [libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_PID, pid as c_int];
let mut kp: libc::kinfo_proc = unsafe { mem::zeroed() };
let mut size = mem::size_of::<libc::kinfo_proc>();
let ret = unsafe {
libc::sysctl(
mib.as_ptr(),
mib.len() as u32,
&mut kp as *mut _ as *mut c_void,
&mut size,
ptr::null_mut(),
0,
)
};
if ret == 0 { kp.ki_pri.pri_level as i32 } else { 0 }
} else {
0
}
}
}
pub(crate) trait UnixProcessExt {
fn sysinfo_process_data(
sys: &System, use_current_cpu_total: bool, unnormalized_cpu: bool, total_memory: u64,
user_table: &mut UserTable,
) -> CollectionResult<Vec<ProcessHarvest>> {
let mut process_vector: Vec<ProcessHarvest> = Vec::new();
let process_hashmap = sys.processes();
let cpu_usage = sys.global_cpu_usage() / 100.0;
let num_processors = sys.cpus().len();
for process_val in process_hashmap.values() {
let name = if process_val.name().is_empty() {
let process_cmd = process_val.cmd();
if let Some(name) = process_cmd.first() {
name.to_string_lossy().to_string()
} else {
process_val
.exe()
.and_then(|exe| exe.file_stem())
.and_then(|stem| stem.to_str())
.map(|s| s.to_string())
.unwrap_or(String::new())
}
} else {
process_val.name().to_string_lossy().to_string()
};
let command = {
let command = process_val
.cmd()
.iter()
.map(|s| s.to_string_lossy())
.join(" ");
if command.is_empty() {
name.clone()
} else {
command
}
};
let pcu = {
let usage = process_val.cpu_usage();
if unnormalized_cpu || num_processors == 0 {
usage
} else {
usage / num_processors as f32
}
};
let process_cpu_usage = if use_current_cpu_total && cpu_usage > 0.0 {
pcu / cpu_usage
} else {
pcu
};
let disk_usage = process_val.disk_usage();
let process_state = {
let ps = process_val.status();
(process_status_str(ps), convert_process_status_to_char(ps))
};
let uid = process_val.user_id().map(|u| **u);
let pid = process_val.pid().as_u32() as Pid;
let nice = get_nice(pid);
let priority = get_priority(pid);
process_vector.push(ProcessHarvest {
pid,
parent_pid: Self::parent_pid(process_val),
name,
command,
mem_usage_percent: if total_memory > 0 {
(process_val.memory() as f64 * 100.0 / total_memory as f64) as f32
} else {
0.0
},
mem_usage: process_val.memory(),
virtual_mem: process_val.virtual_memory(),
cpu_usage_percent: process_cpu_usage,
read_per_sec: disk_usage.read_bytes,
write_per_sec: disk_usage.written_bytes,
total_read: disk_usage.total_read_bytes,
total_write: disk_usage.total_written_bytes,
process_state,
uid,
user: uid.and_then(|uid| user_table.uid_to_username(uid).ok()),
time: if process_val.start_time() == 0 {
Duration::ZERO
} else {
Duration::from_secs(process_val.run_time())
},
#[cfg(feature = "gpu")]
gpu_mem: 0,
#[cfg(feature = "gpu")]
gpu_mem_percent: 0.0,
#[cfg(feature = "gpu")]
gpu_util: 0,
#[cfg(unix)]
nice,
priority,
});
}
if Self::has_backup_proc_cpu_fn() {
let unknown_state = ProcessStatus::Unknown(0).to_string();
let cpu_usage_unknown_pids: Vec<Pid> = process_vector
.iter()
.filter(|process| process.process_state.0 == unknown_state)
.map(|process| process.pid)
.collect();
let cpu_usages = Self::backup_proc_cpu(&cpu_usage_unknown_pids)?;
for process in &mut process_vector {
if let Some(&cpu_usage) = cpu_usages.get(&process.pid) {
process.cpu_usage_percent = if unnormalized_cpu || num_processors == 0 {
cpu_usage
} else {
cpu_usage / num_processors as f32
};
}
}
}
Ok(process_vector)
}
#[inline]
fn has_backup_proc_cpu_fn() -> bool {
false
}
fn backup_proc_cpu(_pids: &[Pid]) -> io::Result<IntMap<Pid, f32>> {
Ok(IntMap::default())
}
fn parent_pid(process_val: &sysinfo::Process) -> Option<Pid> {
process_val.parent().map(|p| p.as_u32() as _)
}
}
fn convert_process_status_to_char(status: ProcessStatus) -> char {
cfg_if::cfg_if! {
if #[cfg(target_os = "macos")] {
const SIDL: char = unsafe { char::from_u32_unchecked(libc::SIDL) };
const SRUN: char = unsafe { char::from_u32_unchecked(libc::SRUN) };
const SSLEEP: char = unsafe { char::from_u32_unchecked(libc::SSLEEP) };
const SSTOP: char = unsafe { char::from_u32_unchecked(libc::SSTOP) };
const SZOMB: char = unsafe { char::from_u32_unchecked(libc::SZOMB) };
match status {
ProcessStatus::Idle => SIDL,
ProcessStatus::Run => SRUN,
ProcessStatus::Sleep => SSLEEP,
ProcessStatus::Stop => SSTOP,
ProcessStatus::Zombie => SZOMB,
_ => '?'
}
} else if #[cfg(target_os = "freebsd")] {
const fn assert_u8(val: libc::c_char) -> u8 {
if val < 0 { panic!("there was an invalid i8 constant that is supposed to be a char") } else { val as u8 }
}
const SIDL: u8 = assert_u8(libc::SIDL);
const SRUN: u8 = assert_u8(libc::SRUN);
const SSLEEP: u8 = assert_u8(libc::SSLEEP);
const SSTOP: u8 = assert_u8(libc::SSTOP);
const SZOMB: u8 = assert_u8(libc::SZOMB);
const SWAIT: u8 = assert_u8(libc::SWAIT);
const SLOCK: u8 = assert_u8(libc::SLOCK);
match status {
ProcessStatus::Idle => SIDL as char,
ProcessStatus::Run => SRUN as char,
ProcessStatus::Sleep => SSLEEP as char,
ProcessStatus::Stop => SSTOP as char,
ProcessStatus::Zombie => SZOMB as char,
ProcessStatus::Dead => SWAIT as char,
ProcessStatus::LockBlocked => SLOCK as char,
_ => '?'
}
} else {
'?'
}
}
}