refactor: migrate CPU from heim to sysinfo for all platforms (#1035)

* refactor: migrate CPU from heim to sysinfo for all platforms

* fix windows and macos imports

* simplify a bit of code

* cleanup
This commit is contained in:
Clement Tsang 2023-03-02 00:03:54 -05:00 committed by GitHub
parent e8ae1a265a
commit f26d598410
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 58 additions and 337 deletions

37
Cargo.lock generated
View file

@ -801,12 +801,6 @@ version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793"
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -826,7 +820,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8a653442b9bdd11a77d3753a60443c60c4437d3acac8e6c3d4a6a9acd7cceed"
dependencies = [
"heim-common",
"heim-cpu",
"heim-disk",
"heim-memory",
"heim-net",
@ -853,25 +846,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "heim-cpu"
version = "0.1.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ba5fb13a3b90581d22b4edf99e87c54316444622ae123d36816a227a7caa6df"
dependencies = [
"cfg-if",
"futures",
"glob",
"heim-common",
"heim-runtime",
"lazy_static",
"libc",
"mach",
"ntapi 0.3.7",
"smol",
"winapi",
]
[[package]]
name = "heim-disk"
version = "0.1.0-rc.1"
@ -1188,15 +1162,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "ntapi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
dependencies = [
"winapi",
]
[[package]]
name = "ntapi"
version = "0.4.0"
@ -1698,7 +1663,7 @@ dependencies = [
"cfg-if",
"core-foundation-sys 0.8.3",
"libc",
"ntapi 0.4.0",
"ntapi",
"once_cell",
"rayon",
"winapi",

View file

@ -108,22 +108,16 @@ unicode-width = "0.1.10"
libc = "0.2.124"
[target.'cfg(target_os = "linux")'.dependencies]
heim = { version = "0.1.0-rc.1", features = [
"cpu",
"disk",
"memory",
"net",
"sensors",
] }
heim = { version = "0.1.0-rc.1", features = ["disk", "memory", "net", "sensors"] }
procfs = { version = "0.14.2", default-features = false }
smol = "1.2.5"
[target.'cfg(target_os = "macos")'.dependencies]
heim = { version = "0.1.0-rc.1", features = ["cpu", "disk", "memory", "net"] }
heim = { version = "0.1.0-rc.1", features = ["disk", "memory", "net"] }
mach2 = "0.4.1"
[target.'cfg(target_os = "windows")'.dependencies]
heim = { version = "0.1.0-rc.1", features = ["cpu", "disk", "memory"] }
heim = { version = "0.1.0-rc.1", features = ["disk", "memory"] }
windows = { version = "0.44.0", features = [
"Win32_System_Threading",
"Win32_Foundation",

View file

@ -10,7 +10,6 @@ use fxhash::FxHashMap;
#[cfg(feature = "battery")]
use starship_battery::{Battery, Manager};
#[cfg(not(target_os = "linux"))]
use sysinfo::{System, SystemExt};
use super::DataFilters;
@ -99,10 +98,7 @@ impl Data {
#[derive(Debug)]
pub struct DataCollector {
pub data: Data,
#[cfg(not(target_os = "linux"))]
sys: System,
previous_cpu_times: Vec<(cpu::PastCpuWork, cpu::PastCpuTotal)>,
previous_average_cpu_time: Option<(cpu::PastCpuWork, cpu::PastCpuTotal)>,
#[cfg(target_os = "linux")]
pid_mapping: FxHashMap<crate::Pid, processes::PrevProcDetails>,
#[cfg(target_os = "linux")]
@ -132,10 +128,7 @@ impl DataCollector {
pub fn new(filters: DataFilters) -> Self {
DataCollector {
data: Data::default(),
#[cfg(not(target_os = "linux"))]
sys: System::new_with_specifics(sysinfo::RefreshKind::new()),
previous_cpu_times: vec![],
previous_average_cpu_time: None,
#[cfg(target_os = "linux")]
pid_mapping: FxHashMap::default(),
#[cfg(target_os = "linux")]
@ -186,16 +179,16 @@ impl DataCollector {
self.sys.refresh_users_list();
}
if self.widgets_to_harvest.use_proc || self.widgets_to_harvest.use_cpu {
self.sys.refresh_cpu();
}
// Refresh disk list once...
if cfg!(target_os = "freebsd") && self.widgets_to_harvest.use_disk {
self.sys.refresh_disks_list();
}
}
if self.widgets_to_harvest.use_proc || self.widgets_to_harvest.use_cpu {
self.sys.refresh_cpu();
}
#[cfg(feature = "battery")]
{
if self.widgets_to_harvest.use_battery {
@ -216,8 +209,6 @@ impl DataCollector {
std::thread::sleep(std::time::Duration::from_millis(250));
self.data.cleanup();
// trace!("Enabled widgets to harvest: {:#?}", self.widgets_to_harvest);
}
#[cfg(target_os = "linux")]
@ -250,11 +241,12 @@ impl DataCollector {
}
pub async fn update_data(&mut self) {
if self.widgets_to_harvest.use_proc || self.widgets_to_harvest.use_cpu {
self.sys.refresh_cpu();
}
#[cfg(not(target_os = "linux"))]
{
if self.widgets_to_harvest.use_proc || self.widgets_to_harvest.use_cpu {
self.sys.refresh_cpu();
}
if self.widgets_to_harvest.use_proc {
self.sys.refresh_processes();
}
@ -284,38 +276,12 @@ impl DataCollector {
// CPU
if self.widgets_to_harvest.use_cpu {
#[cfg(not(target_os = "freebsd"))]
{
if let Ok(cpu_data) = cpu::get_cpu_data_list(
self.show_average_cpu,
&mut self.previous_cpu_times,
&mut self.previous_average_cpu_time,
)
.await
{
self.data.cpu = Some(cpu_data);
}
}
#[cfg(target_os = "freebsd")]
{
if let Ok(cpu_data) = cpu::get_cpu_data_list(
&self.sys,
self.show_average_cpu,
&mut self.previous_cpu_times,
&mut self.previous_average_cpu_time,
)
.await
{
self.data.cpu = Some(cpu_data);
}
}
self.data.cpu = cpu::get_cpu_data_list(&self.sys, self.show_average_cpu).ok();
#[cfg(target_family = "unix")]
{
// Load Average
if let Ok(load_avg_data) = cpu::get_load_avg().await {
self.data.load_avg = Some(load_avg_data);
}
self.data.load_avg = cpu::get_load_avg().ok();
}
}
@ -334,24 +300,23 @@ impl DataCollector {
if let Ok(mut process_list) = {
#[cfg(target_os = "linux")]
{
// Must do this here since we otherwise have to make `get_process_data` async.
use self::processes::CpuUsageStrategy;
use self::processes::{PrevProc, ProcHarvestOptions};
let normalize_cpu = if self.unnormalized_cpu {
heim::cpu::logical_count()
.await
.map(|v| CpuUsageStrategy::NonNormalized(v as f64))
.unwrap_or(CpuUsageStrategy::Normalized)
} else {
CpuUsageStrategy::Normalized
let prev_proc = PrevProc {
prev_idle: &mut self.prev_idle,
prev_non_idle: &mut self.prev_non_idle,
};
let proc_harvest_options = ProcHarvestOptions {
use_current_cpu_total: self.use_current_cpu_total,
unnormalized_cpu: self.unnormalized_cpu,
};
processes::get_process_data(
&mut self.prev_idle,
&mut self.prev_non_idle,
&self.sys,
prev_proc,
&mut self.pid_mapping,
self.use_current_cpu_total,
normalize_cpu,
proc_harvest_options,
current_instant
.duration_since(self.last_collection_time)
.as_secs(),

View file

@ -4,15 +4,8 @@
//!
//! For load average, macOS and Linux are supported through Heim, FreeBSD by sysinfo.
cfg_if::cfg_if! {
if #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] {
pub mod heim;
pub use self::heim::*;
} else if #[cfg(target_os = "freebsd")] {
pub mod sysinfo;
pub use self::sysinfo::*;
}
}
pub mod sysinfo;
pub use self::sysinfo::*;
pub type LoadAvgHarvest = [f32; 3];

View file

@ -1,159 +0,0 @@
//! 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::*;
}
}
use std::collections::VecDeque;
use futures::StreamExt;
use crate::{
components::tui_widget::time_chart::Point,
data_harvester::cpu::{CpuData, CpuDataType, CpuHarvest, PastCpuTotal, PastCpuWork},
};
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> {
fn calculate_cpu_usage_percentage(
(previous_working_time, previous_total_time): Point,
(current_working_time, current_total_time): Point,
) -> f64 {
((if current_working_time > previous_working_time {
current_working_time - previous_working_time
} else {
0.0
}) * 100.0)
/ (if current_total_time > previous_total_time {
current_total_time - previous_total_time
} else {
1.0
})
}
// Get all CPU times...
let cpu_times = heim::cpu::times().await?;
futures::pin_mut!(cpu_times);
let mut cpu_deque: VecDeque<CpuData> = if previous_cpu_times.is_empty() {
// Must initialize ourselves. Use a very quick timeout to calculate an initial.
futures_timer::Delay::new(std::time::Duration::from_millis(100)).await;
let second_cpu_times = heim::cpu::times().await?;
futures::pin_mut!(second_cpu_times);
let mut new_cpu_times: Vec<(PastCpuWork, PastCpuTotal)> = Vec::new();
let mut cpu_deque: VecDeque<CpuData> = VecDeque::new();
let mut collected_zip = cpu_times.zip(second_cpu_times).enumerate(); // Gotta move it here, can't on while line.
while let Some((itx, (past, present))) = collected_zip.next().await {
if let (Ok(past), Ok(present)) = (past, present) {
let present_times = convert_cpu_times(&present);
new_cpu_times.push(present_times);
cpu_deque.push_back(CpuData {
data_type: CpuDataType::Cpu(itx),
cpu_usage: calculate_cpu_usage_percentage(
convert_cpu_times(&past),
present_times,
),
});
} else {
new_cpu_times.push((0.0, 0.0));
cpu_deque.push_back(CpuData {
data_type: CpuDataType::Cpu(itx),
cpu_usage: 0.0,
});
}
}
*previous_cpu_times = new_cpu_times;
cpu_deque
} else {
let (new_cpu_times, cpu_deque): (Vec<(PastCpuWork, PastCpuTotal)>, VecDeque<CpuData>) =
cpu_times
.collect::<Vec<_>>()
.await
.iter()
.zip(&*previous_cpu_times)
.enumerate()
.map(|(itx, (current_cpu, (past_cpu_work, past_cpu_total)))| {
if let Ok(cpu_time) = current_cpu {
let present_times = convert_cpu_times(cpu_time);
(
present_times,
CpuData {
data_type: CpuDataType::Cpu(itx),
cpu_usage: calculate_cpu_usage_percentage(
(*past_cpu_work, *past_cpu_total),
present_times,
),
},
)
} else {
(
(*past_cpu_work, *past_cpu_total),
CpuData {
data_type: CpuDataType::Cpu(itx),
cpu_usage: 0.0,
},
)
}
})
.unzip();
*previous_cpu_times = new_cpu_times;
cpu_deque
};
// Get average CPU if needed... and slap it at the top
if show_average_cpu {
let cpu_time = heim::cpu::time().await?;
let (cpu_usage, new_average_cpu_time) = if let Some((past_cpu_work, past_cpu_total)) =
previous_average_cpu_time
{
let present_times = convert_cpu_times(&cpu_time);
(
calculate_cpu_usage_percentage((*past_cpu_work, *past_cpu_total), present_times),
present_times,
)
} else {
// Again, we need to do a quick timeout...
futures_timer::Delay::new(std::time::Duration::from_millis(100)).await;
let second_cpu_time = heim::cpu::time().await?;
let present_times = convert_cpu_times(&second_cpu_time);
(
calculate_cpu_usage_percentage(convert_cpu_times(&cpu_time), present_times),
present_times,
)
};
*previous_average_cpu_time = Some(new_average_cpu_time);
cpu_deque.push_front(CpuData {
data_type: CpuDataType::Avg,
cpu_usage,
})
}
// Ok(Vec::from(cpu_deque.drain(0..3).collect::<Vec<_>>())) // For artificially limiting the CPU results
Ok(Vec::from(cpu_deque))
}

View file

@ -1,19 +0,0 @@
//! Linux-specific functions regarding CPU usage.
use heim::cpu::os::linux::CpuTimeExt;
use crate::components::tui_widget::time_chart::Point;
pub fn convert_cpu_times(cpu_time: &heim::cpu::CpuTime) -> Point {
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>(),
)
}

View file

@ -1,13 +0,0 @@
//! Unix-specific functions regarding CPU usage.
use crate::app::data_harvester::cpu::LoadAvgHarvest;
pub async fn get_load_avg() -> crate::error::Result<LoadAvgHarvest> {
let (one, five, fifteen) = heim::cpu::os::unix::loadavg().await?;
Ok([
one.get::<heim::units::ratio::ratio>(),
five.get::<heim::units::ratio::ratio>(),
fifteen.get::<heim::units::ratio::ratio>(),
])
}

View file

@ -1,12 +0,0 @@
//! Windows and macOS-specific functions regarding CPU usage.
use crate::components::tui_widget::time_chart::Point;
pub fn convert_cpu_times(cpu_time: &heim::cpu::CpuTime) -> Point {
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>(),
)
}

View file

@ -5,13 +5,11 @@ use std::collections::VecDeque;
use sysinfo::{CpuExt, LoadAvg, System, SystemExt};
use super::{CpuData, CpuDataType, CpuHarvest, PastCpuTotal, PastCpuWork};
use super::{CpuData, CpuDataType, CpuHarvest};
use crate::app::data_harvester::cpu::LoadAvgHarvest;
pub async fn get_cpu_data_list(
pub fn get_cpu_data_list(
sys: &sysinfo::System, show_average_cpu: bool,
_previous_cpu_times: &mut [(PastCpuWork, PastCpuTotal)],
_previous_average_cpu_time: &mut Option<(PastCpuWork, PastCpuTotal)>,
) -> crate::error::Result<CpuHarvest> {
let mut cpu_deque: VecDeque<_> = sys
.cpus()
@ -35,7 +33,7 @@ pub async fn get_cpu_data_list(
Ok(Vec::from(cpu_deque))
}
pub async fn get_load_avg() -> crate::error::Result<LoadAvgHarvest> {
pub fn get_load_avg() -> crate::error::Result<LoadAvgHarvest> {
let sys = System::new();
let LoadAvg { one, five, fifteen } = sys.load_average();

View file

@ -5,7 +5,7 @@ use std::io::{BufRead, BufReader};
use fxhash::{FxHashMap, FxHashSet};
use procfs::process::{Process, Stat};
use sysinfo::ProcessStatus;
use sysinfo::{ProcessStatus, System};
use super::{ProcessHarvest, UserTable};
use crate::components::tui_widget::time_chart::Point;
@ -221,25 +221,31 @@ fn read_proc(
))
}
/// How to calculate CPU usage.
pub enum CpuUsageStrategy {
/// Normalized means the displayed usage percentage is divided over the number of CPU cores.
///
/// For example, if the "overall" usage over the entire system is 105%, and there are 5 cores, then
/// the displayed percentage is 21%.
Normalized,
/// Non-normalized means that the overall usage over the entire system is shown, without dividing
/// over the number of cores.
NonNormalized(f64),
pub(crate) struct PrevProc<'a> {
pub prev_idle: &'a mut f64,
pub prev_non_idle: &'a mut f64,
}
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,
normalization: CpuUsageStrategy, time_difference_in_secs: u64, mem_total_kb: u64,
pub(crate) struct ProcHarvestOptions {
pub use_current_cpu_total: bool,
pub unnormalized_cpu: bool,
}
pub(crate) fn get_process_data(
sys: &System, prev_proc: PrevProc<'_>, pid_mapping: &mut FxHashMap<Pid, PrevProcDetails>,
proc_harvest_options: ProcHarvestOptions, time_difference_in_secs: u64, mem_total_kb: u64,
user_table: &mut UserTable,
) -> crate::utils::error::Result<Vec<ProcessHarvest>> {
let ProcHarvestOptions {
use_current_cpu_total,
unnormalized_cpu,
} = proc_harvest_options;
let PrevProc {
prev_idle,
prev_non_idle,
} = prev_proc;
// TODO: [PROC THREADS] Add threads
if let Ok(CpuUsage {
@ -247,10 +253,13 @@ pub fn get_process_data(
cpu_fraction,
}) = cpu_usage_calculation(prev_idle, prev_non_idle)
{
if let CpuUsageStrategy::NonNormalized(num_cores) = normalization {
if unnormalized_cpu {
use sysinfo::SystemExt;
let num_processors = sys.cpus().len() as f64;
// Note we *divide* here because the later calculation divides `cpu_usage` - in effect,
// multiplying over the number of cores.
cpu_usage /= num_cores;
cpu_usage /= num_processors;
}
let mut pids_to_clear: FxHashSet<Pid> = pid_mapping.keys().cloned().collect();