refactor: Cut out sysinfo from Linux builds (#368)

Refactors to use only heim for Linux builds. This is now much easier to do since the 0.1 version of heim works fine for ARM. This is ideal since having to rely on two separate sources of data isn't the greatest if we can avoid it.

Sysinfo is still required for macOS and Windows, though. Temperature sensors do not work for those from heim, and for some reason, networks also don't work on Windows with heim...?

My personal CPU core calculation is also currently Linux-only, and as such, I'll still rely on sysinfo for Windows and macOS for now.

This isn't really a big optimization or anything btw. Just something I wanted to try.
This commit is contained in:
Clement Tsang 2020-12-21 20:24:27 -05:00 committed by GitHub
parent e014d6fb78
commit 837c382ee9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 265 additions and 27 deletions

27
Cargo.lock generated
View file

@ -249,6 +249,7 @@ dependencies = [
"fern",
"fnv",
"futures",
"futures-timer",
"heim",
"indexmap",
"itertools",
@ -671,6 +672,12 @@ version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "hashbrown"
version = "0.9.1"
@ -684,6 +691,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8a653442b9bdd11a77d3753a60443c60c4437d3acac8e6c3d4a6a9acd7cceed"
dependencies = [
"heim-common",
"heim-cpu",
"heim-disk",
"heim-memory",
"heim-net",
@ -710,6 +718,25 @@ 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 1.0.0",
"futures",
"glob",
"heim-common",
"heim-runtime",
"lazy_static",
"libc",
"mach",
"ntapi",
"smol",
"winapi",
]
[[package]]
name = "heim-disk"
version = "0.1.0-rc.1"

View file

@ -42,7 +42,6 @@ fnv = "1.0.7"
futures = "0.3.8"
indexmap = "~1.6"
itertools = "0.9.0"
libc = "~0.2"
once_cell = "1.5.2"
regex = "1.4.2"
serde = { version = "~1.0", features = ["derive"] }
@ -54,13 +53,22 @@ typed-builder = "0.8.0"
unicode-segmentation = "1.7.1"
unicode-width = "0.1"
# For debugging only...
# For debugging only... disable on release builds with --no-default-target for no? TODO: Redo this.
fern = { version = "0.6.0", optional=true }
log = { version="0.4.11", optional=true }
log = { version = "0.4.11", optional=true }
heim = { version = "0.1.0-rc.1", features = ["disk", "memory", "net", "sensors"] }
[target.'cfg(unix)'.dependencies]
libc = "~0.2"
[target.'cfg(windows)'.dependencies]
[target.'cfg(target_os = "linux")'.dependencies]
heim = { version = "0.1.0-rc.1", features = ["cpu", "disk", "memory", "net", "sensors"] }
futures-timer = "3.0.2"
[target.'cfg(target_os = "macos")'.dependencies]
heim = { version = "0.1.0-rc.1", features = ["disk", "memory", "net"] }
[target.'cfg(target_os = "windows")'.dependencies]
heim = { version = "0.1.0-rc.1", features = ["disk", "memory"] }
winapi = "0.3.9"
[dev-dependencies]

View file

@ -65,10 +65,10 @@ A cross-platform graphical process/system monitor with a customizable interface
Note that bottom is:
- Built on the stable version of Rust
- Officially tested and released for only `x86_64` (and `i686` for Windows)
- Officially tested and released for only `x86_64` (and `i686` for Windows and Linux)
- Developed mainly for macOS, Windows, and Linux
Anything outside of this (i.e: ARM builds, building on Nightly, building on another OS) is currently not guaranteed, even if it does happen to work. For example, ARM is compiled on the CI pipeline and release builds will be provided, but not all features may work (such as R/s and W/s for disks).
Anything outside of this (i.e: ARM builds, building on Nightly, building on another OS) is currently not guaranteed, even if it does happen to work. For example, ARM is compiled on the CI pipeline and release builds will be provided, but not all features may necessarily work. Feel free to file any ARM-related bugs, but know I might not be able to fix them.
### Manually

View file

@ -5,6 +5,7 @@ use std::time::Instant;
#[cfg(target_os = "linux")]
use fnv::FnvHashMap;
#[cfg(not(target_os = "linux"))]
use sysinfo::{System, SystemExt};
use battery::{Battery, Manager};
@ -71,8 +72,13 @@ impl Data {
#[derive(Debug)]
pub struct DataCollector {
pub data: Data,
#[cfg(not(target_os = "linux"))]
sys: System,
#[cfg(target_os = "linux")]
previous_cpu_times: Vec<(cpu::PastCpuWork, cpu::PastCpuTotal)>,
#[cfg(target_os = "linux")]
previous_average_cpu_time: Option<(cpu::PastCpuWork, cpu::PastCpuTotal)>,
#[cfg(target_os = "linux")]
pid_mapping: FnvHashMap<crate::Pid, processes::PrevProcDetails>,
#[cfg(target_os = "linux")]
prev_idle: f64,
@ -97,8 +103,13 @@ impl Default for DataCollector {
// trace!("Creating default data collector...");
DataCollector {
data: Data::default(),
#[cfg(not(target_os = "linux"))]
sys: System::new_with_specifics(sysinfo::RefreshKind::new()), // FIXME: Make this run on only macOS and Windows.
#[cfg(target_os = "linux")]
previous_cpu_times: vec![],
#[cfg(target_os = "linux")]
previous_average_cpu_time: None,
#[cfg(target_os = "linux")]
pid_mapping: FnvHashMap::default(),
#[cfg(target_os = "linux")]
prev_idle: 0_f64,
@ -127,6 +138,12 @@ impl Default for DataCollector {
impl DataCollector {
pub fn init(&mut self) {
#[cfg(target_os = "linux")]
{
futures::executor::block_on(self.initialize_memory_size());
}
#[cfg(not(target_os = "linux"))]
{
self.sys.refresh_memory();
self.mem_total_kb = self.sys.get_total_memory();
@ -139,6 +156,7 @@ impl DataCollector {
if cfg!(target_os = "windows") && self.widgets_to_harvest.use_net {
self.sys.refresh_networks_list();
}
}
if self.widgets_to_harvest.use_battery {
// trace!("First run battery vec creation.");
@ -164,6 +182,15 @@ impl DataCollector {
// trace!("Enabled widgets to harvest: {:#?}", self.widgets_to_harvest);
}
#[cfg(target_os = "linux")]
async fn initialize_memory_size(&mut self) {
self.mem_total_kb = if let Ok(mem) = heim::memory::memory().await {
mem.total().get::<heim::units::information::kilobyte>()
} else {
1
};
}
pub fn set_collected_data(&mut self, used_widgets: UsedWidgets) {
self.widgets_to_harvest = used_widgets;
}
@ -181,29 +208,46 @@ impl DataCollector {
}
pub async fn update_data(&mut self) {
#[cfg(not(target_os = "linux"))]
{
if self.widgets_to_harvest.use_cpu {
self.sys.refresh_cpu();
}
if cfg!(not(target_os = "linux")) {
if self.widgets_to_harvest.use_proc {
self.sys.refresh_processes();
}
if self.widgets_to_harvest.use_temp {
self.sys.refresh_components();
}
}
if cfg!(target_os = "windows") && self.widgets_to_harvest.use_net {
self.sys.refresh_networks();
}
}
let current_instant = std::time::Instant::now();
// CPU
if self.widgets_to_harvest.use_cpu {
#[cfg(not(target_os = "linux"))]
{
self.data.cpu = Some(cpu::get_cpu_data_list(&self.sys, self.show_average_cpu));
}
#[cfg(target_os = "linux")]
{
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);
}
}
}
// Batteries
if let Some(battery_manager) = &self.battery_manager {
if let Some(battery_list) = &mut self.battery_list {

View file

@ -1,5 +1,3 @@
use sysinfo::{ProcessorExt, System, SystemExt};
#[derive(Default, Debug, Clone)]
pub struct CpuData {
pub cpu_prefix: String,
@ -9,6 +7,13 @@ pub struct CpuData {
pub type CpuHarvest = Vec<CpuData>;
pub type PastCpuWork = f64;
pub type PastCpuTotal = f64;
#[cfg(not(target_os = "linux"))]
use sysinfo::{ProcessorExt, System, SystemExt};
#[cfg(not(target_os = "linux"))]
pub fn get_cpu_data_list(sys: &System, show_average_cpu: bool) -> CpuHarvest {
let cpu_data = sys.get_processors();
let avg_cpu_usage = sys.get_global_processor_info().get_cpu_usage();
@ -32,3 +37,157 @@ pub fn get_cpu_data_list(sys: &System, show_average_cpu: bool) -> CpuHarvest {
cpu_vec
}
#[cfg(target_os = "linux")]
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;
use heim::cpu::os::linux::CpuTimeExt;
use std::collections::VecDeque;
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>(),
)
}
fn calculate_cpu_usage_percentage(
(previous_working_time, previous_total_time): (f64, f64),
(current_working_time, current_total_time): (f64, f64),
) -> 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 {
cpu_prefix: "CPU".to_string(),
cpu_count: Some(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 {
cpu_prefix: "CPU".to_string(),
cpu_count: Some(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 {
cpu_prefix: "CPU".to_string(),
cpu_count: Some(itx),
cpu_usage: calculate_cpu_usage_percentage(
(*past_cpu_work, *past_cpu_total),
present_times,
),
},
)
} else {
(
(*past_cpu_work, *past_cpu_total),
CpuData {
cpu_prefix: "CPU".to_string(),
cpu_count: Some(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 {
cpu_prefix: "AVG".to_string(),
cpu_count: None,
cpu_usage,
})
}
Ok(Vec::from(cpu_deque))
}