diff options
Diffstat (limited to 'src/app/data_harvester')
25 files changed, 778 insertions, 589 deletions
diff --git a/src/app/data_harvester/batteries.rs b/src/app/data_harvester/batteries/battery.rs index 98cf6ae6..7e4644e9 100644 --- a/src/app/data_harvester/batteries.rs +++ b/src/app/data_harvester/batteries/battery.rs @@ -1,3 +1,14 @@ +//! Uses the battery crate from svartalf. +//! Covers battery usage for: +//! - Linux 2.6.39+ +//! - MacOS 10.10+ +//! - iOS +//! - Windows 7+ +//! - FreeBSD +//! - DragonFlyBSD +//! +//! For more information, see https://github.com/svartalf/rust-battery + use battery::{ units::{power::watt, ratio::percent, time::second}, Battery, Manager, diff --git a/src/app/data_harvester/batteries/mod.rs b/src/app/data_harvester/batteries/mod.rs new file mode 100644 index 00000000..8c0e4a92 --- /dev/null +++ b/src/app/data_harvester/batteries/mod.rs @@ -0,0 +1,10 @@ +//! Data collection for batteries. +//! +//! For Linux, macOS, Windows, FreeBSD, Dragonfly, and iOS, this is handled by the battery crate. + +cfg_if::cfg_if! { + if #[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "ios"))] { + pub mod battery; + pub use self::battery::*; + } +} diff --git a/src/app/data_harvester/cpu/heim/linux.rs b/src/app/data_harvester/cpu/heim/linux.rs new file mode 100644 index 00000000..542685d4 --- /dev/null +++ b/src/app/data_harvester/cpu/heim/linux.rs @@ -0,0 +1,16 @@ +//! Linux-specific functions regarding CPU usage. + +use heim::cpu::os::linux::CpuTimeExt; +pub fn convert_cpu_times(cpu_time: &heim::cpu::CpuTime) -> (f64, f64) { + let working_time: f64 = (cpu_time.user() + + cpu_time.nice() + + cpu_time.system() + + cpu_time.irq() + + cpu_time.soft_irq() + + cpu_time.steal()) + .get::<heim::units::time::second>(); + ( + working_time, + working_time + (cpu_time.idle() + cpu_time.io_wait()).get::<heim::units::time::second>(), + ) +} diff --git a/src/app/data_harvester/cpu.rs b/src/app/data_harvester/cpu/heim/mod.rs index 61e79e28..73a97b5b 100644 --- a/src/app/data_harvester/cpu.rs +++ b/src/app/data_harvester/cpu/heim/mod.rs @@ -1,3 +1,23 @@ +//! CPU stats through heim. +//! Supports macOS, Linux, and Windows. + +cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + pub mod linux; + pub use linux::*; + } else if #[cfg(any(target_os = "macos", target_os = "windows"))] { + pub mod windows_macos; + pub use windows_macos::*; + } +} + +cfg_if::cfg_if! { + if #[cfg(target_family = "unix")] { + pub mod unix; + pub use unix::*; + } +} + #[derive(Default, Debug, Clone)] pub struct CpuData { pub cpu_prefix: String, @@ -10,42 +30,13 @@ pub type CpuHarvest = Vec<CpuData>; pub type PastCpuWork = f64; pub type PastCpuTotal = f64; +use futures::StreamExt; +use std::collections::VecDeque; + pub async fn get_cpu_data_list( show_average_cpu: bool, previous_cpu_times: &mut Vec<(PastCpuWork, PastCpuTotal)>, previous_average_cpu_time: &mut Option<(PastCpuWork, PastCpuTotal)>, ) -> crate::error::Result<CpuHarvest> { - use futures::StreamExt; - #[cfg(target_os = "linux")] - use heim::cpu::os::linux::CpuTimeExt; - use std::collections::VecDeque; - - fn convert_cpu_times(cpu_time: &heim::cpu::CpuTime) -> (f64, f64) { - #[cfg(not(target_os = "linux"))] - { - let working_time: f64 = - (cpu_time.user() + cpu_time.system()).get::<heim::units::time::second>(); - ( - working_time, - working_time + cpu_time.idle().get::<heim::units::time::second>(), - ) - } - #[cfg(target_os = "linux")] - { - let working_time: f64 = (cpu_time.user() - + cpu_time.nice() - + cpu_time.system() - + cpu_time.irq() - + cpu_time.soft_irq() - + cpu_time.steal()) - .get::<heim::units::time::second>(); - ( - working_time, - working_time - + (cpu_time.idle() + cpu_time.io_wait()).get::<heim::units::time::second>(), - ) - } - } - fn calculate_cpu_usage_percentage( (previous_working_time, previous_total_time): (f64, f64), (current_working_time, current_total_time): (f64, f64), diff --git a/src/app/data_harvester/load_avg.rs b/src/app/data_harvester/cpu/heim/unix.rs index 0f58ea8b..74340951 100644 --- a/src/app/data_harvester/load_avg.rs +++ b/src/app/data_harvester/cpu/heim/unix.rs @@ -1,6 +1,7 @@ -pub type LoadAvgHarvest = [f32; 3]; +//! Unix-specific functions regarding CPU usage. + +use crate::app::data_harvester::cpu::LoadAvgHarvest; -#[cfg(target_family = "unix")] pub async fn get_load_avg() -> crate::error::Result<LoadAvgHarvest> { let (one, five, fifteen) = heim::cpu::os::unix::loadavg().await?; diff --git a/src/app/data_harvester/cpu/heim/windows_macos.rs b/src/app/data_harvester/cpu/heim/windows_macos.rs new file mode 100644 index 00000000..34abc818 --- /dev/null +++ b/src/app/data_harvester/cpu/heim/windows_macos.rs @@ -0,0 +1,10 @@ +//! Windows and macOS-specific functions regarding CPU usage. + +pub fn convert_cpu_times(cpu_time: &heim::cpu::CpuTime) -> (f64, f64) { + let working_time: f64 = + (cpu_time.user() + cpu_time.system()).get::<heim::units::time::second>(); + ( + working_time, + working_time + cpu_time.idle().get::<heim::units::time::second>(), + ) +} diff --git a/src/app/data_harvester/cpu/mod.rs b/src/app/data_harvester/cpu/mod.rs new file mode 100644 index 00000000..81a0db4c --- /dev/null +++ b/src/app/data_harvester/cpu/mod.rs @@ -0,0 +1,14 @@ +//! Data collection for CPU usage and load average. +//! +//! For CPU usage, Linux, macOS, and Windows are handled by Heim. +//! +//! For load average, macOS and Linux are supported through Heim. + +cfg_if::cfg_if! { + if #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] { + pub mod heim; + pub use self::heim::*; + } +} + +pub type LoadAvgHarvest = [f32; 3]; diff --git a/src/app/data_harvester/disks/heim/linux.rs b/src/app/data_harvester/disks/heim/linux.rs new file mode 100644 index 00000000..cbc99d9f --- /dev/null +++ b/src/app/data_harvester/disks/heim/linux.rs @@ -0,0 +1,34 @@ +//! Linux-specific things for Heim disk data collection. + +use heim::disk::Partition; + +pub fn get_device_name(partition: &Partition) -> String { + if let Some(device) = partition.device() { + // See if this disk is actually mounted elsewhere on Linux... + // This is a workaround to properly map I/O in some cases (i.e. disk encryption), see + // https://github.com/ClementTsang/bottom/issues/419 + if let Ok(path) = std::fs::read_link(device) { + if path.is_absolute() { + path.into_os_string() + } else { + let mut combined_path = std::path::PathBuf::new(); + combined_path.push(device); + combined_path.pop(); // Pop the current file... + combined_path.push(path); + + if let Ok(canon_path) = std::fs::canonicalize(combined_path) { + // Resolve the local path into an absolute one... + canon_path.into_os_string() + } else { + device.to_os_string() + } + } + } else { + device.to_os_string() + } + .into_string() + .unwrap_or_else(|_| "Name Unavailable".to_string()) + } else { + "Name Unavailable".to_string() + } +} diff --git a/src/app/data_harvester/disks.rs b/src/app/data_harvester/disks/heim/mod.rs index 103bb701..a79d00db 100644 --- a/src/app/data_harvester/disks.rs +++ b/src/app/data_harvester/disks/heim/mod.rs @@ -1,5 +1,15 @@ use crate::app::Filter; +cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + pub mod linux; + pub use linux::*; + } else if #[cfg(any(target_os = "macos", target_os = "windows"))] { + pub mod windows_macos; + pub use windows_macos::*; + } +} + #[derive(Debug, Clone, Default)] pub struct DiskHarvest { pub name: String, @@ -62,44 +72,7 @@ pub async fn get_disk_usage( while let Some(part) = partitions_stream.next().await { if let Ok(partition) = part { - let symlink: std::ffi::OsString; - - let name = (if let Some(device) = partition.device() { - // See if this disk is actually mounted elsewhere on Linux... - // This is a workaround to properly map I/O in some cases (i.e. disk encryption), see - // https://github.com/ClementTsang/bottom/issues/419 - if cfg!(target_os = "linux") { - if let Ok(path) = std::fs::read_link(device) { - if path.is_absolute() { - symlink = path.into_os_string(); - symlink.as_os_str() - } else { - let mut combined_path = std::path::PathBuf::new(); - combined_path.push(device); - combined_path.pop(); // Pop the current file... - combined_path.push(path.clone()); - - if let Ok(path) = std::fs::canonicalize(combined_path) { - // Resolve the local path into an absolute one... - symlink = path.into_os_string(); - symlink.as_os_str() - } else { - symlink = path.into_os_string(); - symlink.as_os_str() - } - } - } else { - device - } - } else { - device - } - } else { - std::ffi::OsStr::new("Name Unavailable") - } - .to_str() - .unwrap_or("Name Unavailable")) - .to_string(); + let name = get_device_name(&partition); let mount_point = (partition .mount_point() diff --git a/src/app/data_harvester/disks/heim/windows_macos.rs b/src/app/data_harvester/disks/heim/windows_macos.rs new file mode 100644 index 00000000..428733bf --- /dev/null +++ b/src/app/data_harvester/disks/heim/windows_macos.rs @@ -0,0 +1,14 @@ +//! macOS and Windows-specific things for Heim disk data collection. + +use heim::disk::Partition; + +pub fn get_device_name(partition: &Partition) -> String { + if let Some(device) = partition.device() { + device + .to_os_string() + .into_string() + .unwrap_or_else(|_| "Name Unavailable".to_string()) + } else { + "Name Unavailable".to_string() + } +} diff --git a/src/app/data_harvester/disks/mod.rs b/src/app/data_harvester/disks/mod.rs new file mode 100644 index 00000000..e5a52336 --- /dev/null +++ b/src/app/data_harvester/disks/mod.rs @@ -0,0 +1,10 @@ +//! Data collection for disks (IO, usage, space, etc.). +//! +//! For Linux, macOS, and Windows, this is handled by heim. + +cfg_if::cfg_if! { + if #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] { + pub mod heim; + pub use self::heim::*; + } +} diff --git a/src/app/data_harvester/mem.rs b/src/app/data_harvester/memory/heim.rs index 014cbcdc..5319b1b3 100644 --- a/src/app/data_harvester/mem.rs +++ b/src/app/data_harvester/memory/heim.rs @@ -1,3 +1,5 @@ +//! Data collection for memory via heim. + #[derive(Debug, Clone)] pub struct MemHarvest { pub mem_total_in_kib: u64, diff --git a/src/app/data_harvester/memory/mod.rs b/src/app/data_harvester/memory/mod.rs new file mode 100644 index 00000000..588a3c3b --- /dev/null +++ b/src/app/data_harvester/memory/mod.rs @@ -0,0 +1,10 @@ +//! Data collection for memory. +//! +//! For Linux, macOS, and Windows, this is handled by Heim. + +cfg_if::cfg_if! { + if #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] { + pub mod heim; + pub use self::heim::*; + } +} diff --git a/src/app/data_harvester/network.rs b/src/app/data_harvester/network/heim.rs index 650a68e3..d18287c8 100644 --- a/src/app/data_harvester/network.rs +++ b/src/app/data_harvester/network/heim.rs @@ -1,81 +1,9 @@ -use std::time::Instant; - -#[derive(Default, Clone, Debug)] -/// All units in bits. -pub struct NetworkHarvest { - pub rx: u64, - pub tx: u64, - pub total_rx: u64, - pub total_tx: u64, -} - -impl NetworkHarvest { - pub fn first_run_cleanup(&mut self) { - self.rx = 0; - self.tx = 0; - } -} - -/// Separate Windows implementation required due to https://github.com/heim-rs/heim/issues/26. -#[cfg(target_os = "windows")] -pub async fn get_network_data( - sys: &sysinfo::System, prev_net_access_time: Instant, prev_net_rx: &mut u64, - prev_net_tx: &mut u64, curr_time: Instant, actually_get: bool, - filter: &Option<crate::app::Filter>, -) -> crate::utils::error::Result<Option<NetworkHarvest>> { - use sysinfo::{NetworkExt, SystemExt}; - - if !actually_get { - return Ok(None); - } - - let mut total_rx: u64 = 0; - let mut total_tx: u64 = 0; - - let networks = sys.get_networks(); - for (name, network) in networks { - let to_keep = if let Some(filter) = filter { - let mut ret = filter.is_list_ignored; - for r in &filter.list { - if r.is_match(&name) { - ret = !filter.is_list_ignored; - break; - } - } - ret - } else { - true - }; - - if to_keep { - total_rx += network.get_total_received() * 8; - total_tx += network.get_total_transmitted() * 8; - } - } +//! Gets network data via heim. - let elapsed_time = curr_time.duration_since(prev_net_access_time).as_secs_f64(); - - let (rx, tx) = if elapsed_time == 0.0 { - (0, 0) - } else { - ( - ((total_rx.saturating_sub(*prev_net_rx)) as f64 / elapsed_time) as u64, - ((total_tx.saturating_sub(*prev_net_tx)) as f64 / elapsed_time) as u64, - ) - }; - - *prev_net_rx = total_rx; - *prev_net_tx = total_tx; - Ok(Some(NetworkHarvest { - rx, - tx, - total_rx, - total_tx, - })) -} +use super::NetworkHarvest; +use std::time::Instant; // FIXME: Eventually make it so that this thing also takes individual usage into account, so we can allow for showing per-interface! -#[cfg(not(target_os = "windows"))] pub async fn get_network_data( prev_net_access_time: Instant, prev_net_rx: &mut u64, prev_net_tx: &mut u64, curr_time: Instant, actually_get: bool, filter: &Option<crate::app::Filter>, diff --git a/src/app/data_harvester/network/mod.rs b/src/app/data_harvester/network/mod.rs new file mode 100644 index 00000000..c717e6ac --- /dev/null +++ b/src/app/data_harvester/network/mod.rs @@ -0,0 +1,30 @@ +//! Data collection for network usage/IO. +//! +//! For Linux and macOS, this is handled by Heim. +//! For Windows, this is handled by sysinfo. + +cfg_if::cfg_if! { + if #[cfg(any(target_os = "linux", target_os = "macos"))] { + pub mod heim; + pub use self::heim::*; + } else if #[cfg(target_os = "windows")] { + pub mod sysinfo; + pub use self::sysinfo::*; + } +} + +#[derive(Default, Clone, Debug)] +/// All units in bits. +pub struct NetworkHarvest { + pub rx: u64, + pub tx: u64, + pub total_rx: u64, + pub total_tx: u64, +} + +impl NetworkHarvest { + pub fn first_run_cleanup(&mut self) { + self.rx = 0; + self.tx = 0; + } +} diff --git a/src/app/data_harvester/network/sysinfo.rs b/src/app/data_harvester/network/sysinfo.rs new file mode 100644 index 00000000..c7a7db00 --- /dev/null +++ b/src/app/data_harvester/network/sysinfo.rs @@ -0,0 +1,60 @@ +//! Gets network data via sysinfo. + +use super::NetworkHarvest; +use std::time::Instant; + +pub async fn get_network_data( + sys: &sysinfo::System, prev_net_access_time: Instant, prev_net_rx: &mut u64, + prev_net_tx: &mut u64, curr_time: Instant, actually_get: bool, + filter: &Option<crate::app::Filter>, +) -> crate::utils::error::Result<Option<NetworkHarvest>> { + use sysinfo::{NetworkExt, SystemExt}; + + if !actually_get { + return Ok(None); + } + + let mut total_rx: u64 = 0; + let mut total_tx: u64 = 0; + + let networks = sys.get_networks(); + for (name, network) in networks { + let to_keep = if let Some(filter) = filter { + let mut ret = filter.is_list_ignored; + for r in &filter.list { + if r.is_match(&name) { + ret = !filter.is_list_ignored; + break; + } + } + ret + } else { + true + }; + + if to_keep { + total_rx += network.get_total_received() * 8; + total_tx += network.get_total_transmitted() * 8; + } + } + + let elapsed_time = curr_time.duration_since(prev_net_access_time).as_secs_f64(); + + let (rx, tx) = if elapsed_time == 0.0 { + (0, 0) + } else { + ( + ((total_rx.saturating_sub(*prev_net_rx)) as f64 / elapsed_time) as u64, + ((total_tx.saturating_sub(*prev_net_tx)) as f64 / elapsed_time) as u64, + ) + }; + + *prev_net_rx = total_rx; + *prev_net_tx = total_tx; + Ok(Some(NetworkHarvest { + rx, + tx, + total_rx, + total_tx, + })) +} diff --git a/src/app/data_harvester/processes.rs b/src/app/data_harvester/processes/linux.rs index 94f6fb62..d6778ba3 100644 --- a/src/app/data_harvester/processes.rs +++ b/src/app/data_harvester/processes/linux.rs @@ -1,99 +1,20 @@ +//! Process data collection for Linux. + +use crate::utils::error::{self, BottomError}; use crate::Pid; -use sysinfo::ProcessStatus; +use super::ProcessHarvest; -#[cfg(target_family = "unix")] -use crate::utils::error; +use sysinfo::ProcessStatus; -#[cfg(target_os = "linux")] use procfs::process::{Process, Stat}; -#[cfg(target_os = "linux")] -use crate::utils::error::BottomError; - -#[cfg(target_os = "linux")] use fxhash::{FxHashMap, FxHashSet}; -#[cfg(not(target_os = "linux"))] -use sysinfo::{ProcessExt, ProcessorExt, System, SystemExt}; - /// Maximum character length of a /proc/<PID>/stat process name. /// If it's equal or greater, then we instead refer to the command for the name. -#[cfg(target_os = "linux")] const MAX_STAT_NAME_LEN: usize = 15; -// TODO: Add value so we know if it's sorted ascending or descending by default? -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub enum ProcessSorting { - CpuPercent, - Mem, - MemPercent, - Pid, - ProcessName, - Command, - ReadPerSecond, - WritePerSecond, - TotalRead, - TotalWrite, - State, - User, - Count, -} - -impl std::fmt::Display for ProcessSorting { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match &self { - ProcessSorting::CpuPercent => "CPU%", - ProcessSorting::MemPercent => "Mem%", - ProcessSorting::Mem => "Mem", - ProcessSorting::ReadPerSecond => "R/s", - ProcessSorting::WritePerSecond => "W/s", - ProcessSorting::TotalRead => "T.Read", - ProcessSorting::TotalWrite => "T.Write", - ProcessSorting::State => "State", - ProcessSorting::ProcessName => "Name", - ProcessSorting::Command => "Command", - ProcessSorting::Pid => "PID", - ProcessSorting::Count => "Count", - ProcessSorting::User => "User", - } - ) - } -} - -impl Default for ProcessSorting { - fn default() -> Self { - ProcessSorting::CpuPercent - } -} - -#[derive(Debug, Clone, Default)] -pub struct ProcessHarvest { - pub pid: Pid, - pub parent_pid: Option<Pid>, // Remember, parent_pid 0 is root... - pub cpu_usage_percent: f64, - pub mem_usage_percent: f64, - pub mem_usage_bytes: u64, - // pub rss_kb: u64, - // pub virt_kb: u64, - pub name: String, - pub command: String, - pub read_bytes_per_sec: u64, - pub write_bytes_per_sec: u64, - pub total_read_bytes: u64, - pub total_write_bytes: u64, - pub process_state: String, - pub process_state_char: char, - - /// This is the *effective* user ID. - #[cfg(target_family = "unix")] - pub uid: Option<libc::uid_t>, -} - -#[cfg(target_os = "linux")] #[derive(Debug, Clone)] pub struct PrevProcDetails { pub total_read_bytes: u64, @@ -102,7 +23,6 @@ pub struct PrevProcDetails { pub process: Process, } -#[cfg(target_os = "linux")] impl PrevProcDetails { fn new(pid: Pid) -> error::Result<Self> { Ok(Self { @@ -114,36 +34,6 @@ impl PrevProcDetails { } } -#[cfg(target_family = "unix")] -#[derive(Debug, Default)] -pub struct UserTable { - pub uid_user_mapping: std::collections::HashMap<libc::uid_t, String>, -} - -#[cfg(target_family = "unix")] -impl UserTable { - pub fn get_uid_to_username_mapping(&mut self, uid: libc::uid_t) -> error::Result<String> { - if let Some(user) = self.uid_user_mapping.get(&uid) { - Ok(user.clone()) - } else { - // SAFETY: getpwuid returns a null pointer if no passwd entry is found for the uid - let passwd = unsafe { libc::getpwuid(uid) }; - - if passwd.is_null() { - return Err(error::BottomError::QueryError("Missing passwd".into())); - } - - let username = unsafe { std::ffi::CStr::from_ptr((*passwd).pw_name) } - .to_str()? - .to_string(); - self.uid_user_mapping.insert(uid, username.clone()); - - Ok(username) - } - } -} - -#[cfg(target_os = "linux")] fn cpu_usage_calculation( prev_idle: &mut f64, prev_non_idle: &mut f64, ) -> error::Result<(f64, f64)> { @@ -204,7 +94,6 @@ fn cpu_usage_calculation( } /// Returns the usage and a new set of process times. Note: cpu_fraction should be represented WITHOUT the x100 factor! -#[cfg(target_os = "linux")] fn get_linux_cpu_usage( stat: &Stat, cpu_usage: f64, cpu_fraction: f64, prev_proc_times: u64, use_current_cpu_total: bool, @@ -222,40 +111,7 @@ fn get_linux_cpu_usage( } } -#[cfg(target_os = "macos")] -fn get_macos_process_cpu_usage( - pids: &[i32], -) -> std::io::Result<std::collections::HashMap<i32, f64>> { - use itertools::Itertools; - let output = std::process::Command::new("ps") - .args(&["-o", "pid=,pcpu=", "-p"]) - .arg( - // Has to look like this since otherwise, it you hit a `unstable_name_collisions` warning. - Itertools::intersperse(pids.iter().map(i32::to_string), ",".to_string()) - .collect::<String>(), - ) - .output()?; - let mut result = std::collections::HashMap::new(); - String::from_utf8_lossy(&output.stdout) - .split_whitespace() - .chunks(2) - .into_iter() - .for_each(|chunk| { - let chunk: Vec<&str> = chunk.collect(); - if chunk.len() != 2 { - panic!("Unexpected `ps` output"); - } - let pid = chunk[0].parse(); - let usage = chunk[1].parse(); - if let (Ok(pid), Ok(usage)) = (pid, usage) { - result.insert(pid, usage); - } - }); - Ok(result) -} - #[allow(clippy::too_many_arguments)] -#[cfg(target_os = "linux")] fn read_proc( prev_proc: &PrevProcDetails, stat: &Stat, cpu_usage: f64, cpu_fraction: f64, use_current_cpu_total: bool, time_difference_in_secs: u64, mem_total_kb: u64, @@ -361,7 +217,6 @@ fn read_proc( )) } -#[cfg(target_os = "linux")] pub fn get_process_data( prev_idle: &mut f64, prev_non_idle: &mut f64, pid_mapping: &mut FxHashMap<Pid, PrevProcDetails>, use_current_cpu_total: bool, @@ -437,142 +292,3 @@ pub fn get_process_data( )) } } - -#[cfg(not(target_os = "linux"))] -pub fn get_process_data( - sys: &System, use_current_cpu_total: bool, mem_total_kb: u64, -) -> crate::utils::error::Result<Vec<ProcessHarvest>> { - let mut process_vector: Vec<ProcessHarvest> = Vec::new(); - let process_hashmap = sys.get_processes(); - let cpu_usage = sys.get_global_processor_info().get_cpu_usage() as f64 / 100.0; - let num_cpus = sys.get_processors().len() as f64; - for process_val in process_hashmap.values() { - let name = if process_val.name().is_empty() { - let process_cmd = process_val.cmd(); - if process_cmd.len() > 1 { - process_cmd[0].clone() - } else { - let process_exe = process_val.exe().file_stem(); - if let Some(exe) = process_exe { - let process_exe_opt = exe.to_str(); - if let Some(exe_name) = process_exe_opt { - exe_name.to_string() - } else { - "".to_string() - } - } else { - "".to_string() - } - } - } else { - process_val.name().to_string() - }; - let command = { - let command = process_val.cmd().join(" "); - if command.is_empty() { - name.to_string() - } else { - command - |