mirror of
https://github.com/ClementTsang/bottom
synced 2024-11-22 20:23:12 +00:00
some progress on linux
This commit is contained in:
parent
1b98b967a8
commit
2ba435f22e
29 changed files with 406 additions and 204 deletions
|
@ -18,6 +18,3 @@ pub struct CpuData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type CpuHarvest = Vec<CpuData>;
|
pub type CpuHarvest = Vec<CpuData>;
|
||||||
|
|
||||||
pub type PastCpuWork = f64;
|
|
||||||
pub type PastCpuTotal = f64;
|
|
||||||
|
|
|
@ -19,6 +19,6 @@ pub mod arc;
|
||||||
pub struct MemHarvest {
|
pub struct MemHarvest {
|
||||||
pub used_bytes: u64,
|
pub used_bytes: u64,
|
||||||
pub total_bytes: u64,
|
pub total_bytes: u64,
|
||||||
pub use_percent: Option<f64>, /* TODO: Might be find to just make this an f64, and any
|
pub use_percent: Option<f64>, /* TODO: Might be fine to just make this an f64, and any
|
||||||
* consumer checks NaN. */
|
* consumer checks NaN. */
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,34 @@
|
||||||
//! Common code amongst all data collectors.
|
//! Common code amongst all data collectors.
|
||||||
|
|
||||||
use crate::new_data_collection::{
|
use crate::{
|
||||||
error::CollectionResult,
|
data_collection::Data,
|
||||||
sources::common::{
|
new_data_collection::{
|
||||||
disk::DiskHarvest, processes::ProcessHarvest, temperature::TemperatureData,
|
error::CollectionResult,
|
||||||
|
sources::{
|
||||||
|
cpu::CpuHarvest, disk::DiskHarvest, memory::MemHarvest, processes::ProcessHarvest,
|
||||||
|
temperature::TemperatureData,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "battery")]
|
||||||
|
use crate::new_data_collection::sources::battery::BatteryHarvest;
|
||||||
|
|
||||||
|
// /// Represents data collected at an instance.
|
||||||
|
// #[derive(Debug)]
|
||||||
|
// pub struct Data {
|
||||||
|
// pub collection_time: Instant,
|
||||||
|
// pub temperature_data: Option<Vec<TemperatureData>>,
|
||||||
|
// pub process_data: Option<Vec<ProcessHarvest>>,
|
||||||
|
// pub disk_data: Option<DiskHarvest>,
|
||||||
|
// }
|
||||||
|
|
||||||
/// The trait representing what a per-platform data collector should implement.
|
/// The trait representing what a per-platform data collector should implement.
|
||||||
pub(crate) trait DataCollector {
|
pub trait DataCollector {
|
||||||
/// Refresh inner data sources to prepare them for gathering data.
|
/// Return data.
|
||||||
///
|
///
|
||||||
/// Note that depending on the implementation, this may
|
/// For now, this returns the old data type for cross-compatibility as we migrate.
|
||||||
/// not actually need to do anything.
|
fn get_data(&mut self) -> Data;
|
||||||
fn refresh_data(&mut self) -> CollectionResult<()>;
|
|
||||||
|
|
||||||
/// Return temperature data.
|
/// Return temperature data.
|
||||||
fn get_temperature_data(&mut self) -> CollectionResult<Vec<TemperatureData>>;
|
fn get_temperature_data(&mut self) -> CollectionResult<Vec<TemperatureData>>;
|
||||||
|
@ -23,4 +38,14 @@ pub(crate) trait DataCollector {
|
||||||
|
|
||||||
/// Return disk data.
|
/// Return disk data.
|
||||||
fn get_disk_data(&mut self) -> CollectionResult<DiskHarvest>;
|
fn get_disk_data(&mut self) -> CollectionResult<DiskHarvest>;
|
||||||
|
|
||||||
|
/// Return CPU data.
|
||||||
|
fn get_cpu_data(&mut self) -> CollectionResult<CpuHarvest>;
|
||||||
|
|
||||||
|
/// Return memory data.
|
||||||
|
fn get_memory_data(&mut self) -> CollectionResult<MemHarvest>;
|
||||||
|
|
||||||
|
#[cfg(feature = "battery")]
|
||||||
|
/// Return battery data.
|
||||||
|
fn get_battery_data(&mut self) -> CollectionResult<Vec<BatteryHarvest>>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
//! The data collector for FreeBSD.
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
app::filter::Filter,
|
|
||||||
new_data_collection::{
|
|
||||||
error::CollectionResult,
|
|
||||||
sources::{
|
|
||||||
common::temperature::{TemperatureData, TemperatureType},
|
|
||||||
sysinfo::temperature::get_temperature_data,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::common::DataCollector;
|
|
||||||
|
|
||||||
/// The [`DataCollector`] for FreeBSD.
|
|
||||||
pub struct FreeBsdDataCollector {
|
|
||||||
temp_type: TemperatureType,
|
|
||||||
temp_filters: Option<Filter>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DataCollector for FreeBsdDataCollector {
|
|
||||||
fn refresh_data(&mut self) -> CollectionResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_temperature_data(&self) -> CollectionResult<Option<Vec<TemperatureData>>> {
|
|
||||||
let mut results = get_temperature_data(&self.temp_type, &self.temp_filters);
|
|
||||||
|
|
||||||
for entry in sysctl_temp_iter(&self.temp_type, &self.temp_filters) {
|
|
||||||
results.push(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Some(results))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,28 +2,36 @@
|
||||||
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use starship_battery::{Battery, Manager};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::filter::Filter,
|
app::filter::Filter,
|
||||||
|
data_collection::Data,
|
||||||
new_data_collection::{
|
new_data_collection::{
|
||||||
error::CollectionResult,
|
error::CollectionResult,
|
||||||
sources::{
|
sources::{
|
||||||
common::{
|
cpu::CpuHarvest,
|
||||||
disk::DiskHarvest,
|
disk::DiskHarvest,
|
||||||
processes::ProcessHarvest,
|
linux::{get_temperature_data, linux_process_data, ProcessCollector},
|
||||||
temperature::{TemperatureData, TemperatureType},
|
memory::MemHarvest,
|
||||||
},
|
processes::ProcessHarvest,
|
||||||
linux::{
|
sysinfo::{
|
||||||
processes::{linux_process_data, ProcessCollector},
|
cpu::{get_cpu_data_list, get_load_avg},
|
||||||
temperature::get_temperature_data,
|
memory::{get_cache_usage, get_ram_usage, get_swap_usage},
|
||||||
},
|
},
|
||||||
|
temperature::{TemperatureData, TemperatureType},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::common::DataCollector;
|
use super::common::DataCollector;
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(feature = "battery")] {
|
||||||
|
use starship_battery::{Battery, Manager};
|
||||||
|
use crate::new_data_collection::sources::battery::BatteryHarvest;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// The [`DataCollector`] for Linux.
|
/// The [`DataCollector`] for Linux.
|
||||||
pub struct LinuxDataCollector {
|
pub struct LinuxDataCollector {
|
||||||
current_collection_time: Instant,
|
current_collection_time: Instant,
|
||||||
|
@ -37,19 +45,30 @@ pub struct LinuxDataCollector {
|
||||||
system: sysinfo::System,
|
system: sysinfo::System,
|
||||||
network: sysinfo::Networks,
|
network: sysinfo::Networks,
|
||||||
|
|
||||||
|
show_average_cpu: bool,
|
||||||
|
|
||||||
#[cfg(feature = "battery")]
|
#[cfg(feature = "battery")]
|
||||||
battery_manager: Option<Manager>,
|
batteries: Option<(Manager, Vec<Battery>)>,
|
||||||
#[cfg(feature = "battery")]
|
|
||||||
battery_list: Option<Vec<Battery>>,
|
#[cfg(feature = "gpu")]
|
||||||
|
nvml: nvml_wrapper::Nvml,
|
||||||
|
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
gpus_total_mem: Option<u64>,
|
gpus_total_mem: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DataCollector for LinuxDataCollector {
|
impl LinuxDataCollector {
|
||||||
fn refresh_data(&mut self) -> CollectionResult<()> {
|
fn refresh_data(&mut self) -> CollectionResult<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataCollector for LinuxDataCollector {
|
||||||
|
fn get_data(&mut self) -> Data {
|
||||||
|
let collection_time = Instant::now();
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_temperature_data(&mut self) -> CollectionResult<Vec<TemperatureData>> {
|
fn get_temperature_data(&mut self) -> CollectionResult<Vec<TemperatureData>> {
|
||||||
Ok(get_temperature_data(&self.temp_type, &self.temp_filters))
|
Ok(get_temperature_data(&self.temp_type, &self.temp_filters))
|
||||||
|
@ -73,4 +92,44 @@ impl DataCollector for LinuxDataCollector {
|
||||||
fn get_disk_data(&mut self) -> CollectionResult<DiskHarvest> {
|
fn get_disk_data(&mut self) -> CollectionResult<DiskHarvest> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_cpu_data(&mut self) -> CollectionResult<CpuHarvest> {
|
||||||
|
let usages = get_cpu_data_list(&self.system, self.show_average_cpu);
|
||||||
|
let load_average = get_load_avg();
|
||||||
|
|
||||||
|
CollectionResult::Ok(CpuHarvest {
|
||||||
|
usages,
|
||||||
|
load_average,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_memory_data(&mut self) -> CollectionResult<MemHarvest> {
|
||||||
|
let memory = get_ram_usage(&self.system);
|
||||||
|
let swap = get_swap_usage(&self.system);
|
||||||
|
let cache = get_cache_usage(&self.system);
|
||||||
|
|
||||||
|
CollectionResult::Ok(MemHarvest {
|
||||||
|
memory,
|
||||||
|
swap,
|
||||||
|
cache,
|
||||||
|
#[cfg(feature = "zfs")]
|
||||||
|
arc: crate::new_data_collection::sources::linux::get_arc_usage(),
|
||||||
|
#[cfg(feature = "gpu")]
|
||||||
|
gpu: crate::new_data_collection::sources::nvidia::get_gpu_memory_usage(&self.nvml),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "battery")]
|
||||||
|
fn get_battery_data(&mut self) -> CollectionResult<Vec<BatteryHarvest>> {
|
||||||
|
use crate::new_data_collection::{
|
||||||
|
error::CollectionError, sources::starship::refresh_batteries,
|
||||||
|
};
|
||||||
|
|
||||||
|
match &mut self.batteries {
|
||||||
|
Some((battery_manager, battery_list)) => {
|
||||||
|
CollectionResult::Ok(refresh_batteries(battery_manager, battery_list))
|
||||||
|
}
|
||||||
|
None => CollectionResult::Err(CollectionError::NoData),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
//! The data collector for macOS.
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
app::filter::Filter,
|
|
||||||
new_data_collection::{
|
|
||||||
error::CollectionResult,
|
|
||||||
sources::{
|
|
||||||
common::temperature::{TemperatureData, TemperatureType},
|
|
||||||
sysinfo::temperature::get_temperature_data,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::common::DataCollector;
|
|
||||||
|
|
||||||
/// The [`DataCollector`] for macOS.
|
|
||||||
pub struct MacOsDataCollector {
|
|
||||||
temp_type: TemperatureType,
|
|
||||||
temp_filters: Option<Filter>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DataCollector for MacOsDataCollector {
|
|
||||||
fn refresh_data(&mut self) -> CollectionResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_temperature_data(&self) -> CollectionResult<Option<Vec<TemperatureData>>> {
|
|
||||||
Ok(Some(get_temperature_data(
|
|
||||||
&self.temp_type,
|
|
||||||
&self.temp_filters,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
//! The data collector for Windows.
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
app::filter::Filter,
|
|
||||||
new_data_collection::{
|
|
||||||
error::CollectionResult,
|
|
||||||
sources::{
|
|
||||||
common::temperature::{TemperatureData, TemperatureType},
|
|
||||||
sysinfo::temperature::get_temperature_data,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::common::DataCollector;
|
|
||||||
|
|
||||||
/// The [`DataCollector`] for Windows.
|
|
||||||
pub struct WindowsDataCollector {
|
|
||||||
temp_type: TemperatureType,
|
|
||||||
temp_filters: Option<Filter>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DataCollector for WindowsDataCollector {
|
|
||||||
fn refresh_data(&mut self) -> CollectionResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_temperature_data(&self) -> CollectionResult<Option<Vec<TemperatureData>>> {
|
|
||||||
Ok(Some(get_temperature_data(
|
|
||||||
&self.temp_type,
|
|
||||||
&self.temp_filters,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,6 +6,9 @@ pub enum CollectionError {
|
||||||
/// A general error to propagate back up. A wrapper around [`anyhow::Error`].
|
/// A general error to propagate back up. A wrapper around [`anyhow::Error`].
|
||||||
General(anyhow::Error),
|
General(anyhow::Error),
|
||||||
|
|
||||||
|
/// No data.
|
||||||
|
NoData,
|
||||||
|
|
||||||
/// Collection is unsupported.
|
/// Collection is unsupported.
|
||||||
Unsupported,
|
Unsupported,
|
||||||
}
|
}
|
||||||
|
@ -14,6 +17,9 @@ impl std::fmt::Display for CollectionError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
CollectionError::General(err) => err.fmt(f),
|
CollectionError::General(err) => err.fmt(f),
|
||||||
|
CollectionError::NoData => {
|
||||||
|
write!(f, "no data found")
|
||||||
|
}
|
||||||
CollectionError::Unsupported => {
|
CollectionError::Unsupported => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
|
|
@ -9,15 +9,15 @@ mod collectors {
|
||||||
if #[cfg(target_os = "linux")] {
|
if #[cfg(target_os = "linux")] {
|
||||||
pub mod linux;
|
pub mod linux;
|
||||||
pub use linux::LinuxDataCollector as DataCollectorImpl;
|
pub use linux::LinuxDataCollector as DataCollectorImpl;
|
||||||
} else if #[cfg(target_os = "macos")] {
|
// } else if #[cfg(target_os = "macos")] {
|
||||||
pub mod macos;
|
// pub mod macos;
|
||||||
pub use macos::MacOsDataCollector as DataCollectorImpl;
|
// pub use macos::MacOsDataCollector as DataCollectorImpl;
|
||||||
} else if #[cfg(target_os = "windows")] {
|
// } else if #[cfg(target_os = "windows")] {
|
||||||
pub mod windows;
|
// pub mod windows;
|
||||||
pub use windows::WindowsDataCollector as DataCollectorImpl;
|
// pub use windows::WindowsDataCollector as DataCollectorImpl;
|
||||||
} else if #[cfg(target_os = "freebsd")] {
|
// } else if #[cfg(target_os = "freebsd")] {
|
||||||
pub mod freebsd;
|
// pub mod freebsd;
|
||||||
pub use freebsd::FreeBsdDataCollector as DataCollectorImpl;
|
// pub use freebsd::FreeBsdDataCollector as DataCollectorImpl;
|
||||||
} else {
|
} else {
|
||||||
pub mod fallback;
|
pub mod fallback;
|
||||||
pub use fallback::FallbackDataCollector as DataCollectorImpl;
|
pub use fallback::FallbackDataCollector as DataCollectorImpl;
|
||||||
|
|
20
src/new_data_collection/sources/common/battery.rs
Normal file
20
src/new_data_collection/sources/common/battery.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//! Common code for retrieving battery data.
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum State {
|
||||||
|
Unknown,
|
||||||
|
Charging,
|
||||||
|
Discharging,
|
||||||
|
Empty,
|
||||||
|
Full,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct BatteryHarvest {
|
||||||
|
pub charge_percent: f64,
|
||||||
|
pub secs_until_full: Option<i64>,
|
||||||
|
pub secs_until_empty: Option<i64>,
|
||||||
|
pub power_consumption_rate_watts: f64,
|
||||||
|
pub health_percent: f64,
|
||||||
|
pub state: State,
|
||||||
|
}
|
21
src/new_data_collection/sources/common/cpu.rs
Normal file
21
src/new_data_collection/sources/common/cpu.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//! Common code for retrieving CPU data.
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub(crate) enum CpuDataType {
|
||||||
|
Avg,
|
||||||
|
Cpu(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a single core/thread and its usage.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct CpuData {
|
||||||
|
pub entry_type: CpuDataType,
|
||||||
|
pub usage: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collected CPU data at an instance.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct CpuHarvest {
|
||||||
|
pub usages: Vec<CpuData>,
|
||||||
|
pub load_average: [f32; 3],
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ pub struct IoData {
|
||||||
pub write_bytes: u64,
|
pub write_bytes: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct DiskHarvest {
|
pub struct DiskHarvest {
|
||||||
/// Disk entries.
|
/// Disk entries.
|
||||||
pub entries: Vec<DiskEntry>,
|
pub entries: Vec<DiskEntry>,
|
||||||
|
|
22
src/new_data_collection/sources/common/memory.rs
Normal file
22
src/new_data_collection/sources/common/memory.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
//! Code pertaining to memory data retrieval.
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct MemData {
|
||||||
|
pub used_bytes: u64,
|
||||||
|
pub total_bytes: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct MemHarvest {
|
||||||
|
pub memory: MemData,
|
||||||
|
pub swap: MemData,
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
pub cache: MemData,
|
||||||
|
|
||||||
|
#[cfg(feature = "zfs")]
|
||||||
|
pub arc: MemData,
|
||||||
|
|
||||||
|
#[cfg(feature = "gpu")]
|
||||||
|
pub gpu: Vec<(String, MemData)>,
|
||||||
|
}
|
|
@ -1,3 +1,7 @@
|
||||||
|
#[cfg(feature = "battery")]
|
||||||
|
pub mod battery;
|
||||||
|
pub mod cpu;
|
||||||
pub mod disk;
|
pub mod disk;
|
||||||
|
pub mod memory;
|
||||||
pub mod processes;
|
pub mod processes;
|
||||||
pub mod temperature;
|
pub mod temperature;
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
mod temperature;
|
|
|
@ -1,35 +0,0 @@
|
||||||
//! FreeBSD-specific temperature extraction code.
|
|
||||||
|
|
||||||
// For RockPro64 boards on FreeBSD, they apparently use "hw.temperature" for
|
|
||||||
// sensors.
|
|
||||||
use sysctl::Sysctl;
|
|
||||||
|
|
||||||
/// Return an iterator of temperature data pulled from sysctl.
|
|
||||||
pub(crate) fn sysctl_temp_iter(
|
|
||||||
temp_type: &TemperatureType, filter: &Option<Filter>,
|
|
||||||
) -> impl Iterator<Item = TemperatureData> {
|
|
||||||
const KEY: &str = "hw.temperature";
|
|
||||||
|
|
||||||
if let Ok(root) = sysctl::Ctl::new(KEY) {
|
|
||||||
sysctl::CtlIter::below(root).flatten().filter_map(|ctl| {
|
|
||||||
if let (Ok(name), Ok(temp)) = (ctl.name(), ctl.value()) {
|
|
||||||
if let Some(temp) = temp.as_temperature() {
|
|
||||||
if Filter::optional_should_keep(filter, &name) {
|
|
||||||
return Some(TemperatureData {
|
|
||||||
name,
|
|
||||||
temperature: Some(match temp_type {
|
|
||||||
TemperatureType::Celsius => temp.celsius(),
|
|
||||||
TemperatureType::Kelvin => temp.kelvin(),
|
|
||||||
TemperatureType::Fahrenheit => temp.fahrenheit(),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
std::iter::empty()
|
|
||||||
}
|
|
||||||
}
|
|
46
src/new_data_collection/sources/linux/memory.rs
Normal file
46
src/new_data_collection/sources/linux/memory.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use crate::new_data_collection::sources::memory::MemData;
|
||||||
|
|
||||||
|
pub(crate) fn get_arc_usage() -> MemData {
|
||||||
|
// TODO: [OPT] is this efficient?
|
||||||
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
|
let (total_bytes, used_bytes) =
|
||||||
|
if let Ok(arc_stats) = read_to_string("/proc/spl/kstat/zfs/arcstats") {
|
||||||
|
let mut mem_arc = 0;
|
||||||
|
let mut mem_total = 0;
|
||||||
|
let mut zfs_keys_read: u8 = 0;
|
||||||
|
const ZFS_KEYS_NEEDED: u8 = 2;
|
||||||
|
|
||||||
|
for line in arc_stats.lines() {
|
||||||
|
if let Some((label, value)) = line.split_once(' ') {
|
||||||
|
let to_write = match label {
|
||||||
|
"size" => &mut mem_arc,
|
||||||
|
"c_max" => &mut mem_total,
|
||||||
|
_ => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((_type, number)) = value.trim_start().rsplit_once(' ') {
|
||||||
|
// Parse the value, remember it's in bytes!
|
||||||
|
if let Ok(number) = number.parse::<u64>() {
|
||||||
|
*to_write = number;
|
||||||
|
// We only need a few keys, so we can bail early.
|
||||||
|
zfs_keys_read += 1;
|
||||||
|
if zfs_keys_read == ZFS_KEYS_NEEDED {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(mem_total, mem_arc)
|
||||||
|
} else {
|
||||||
|
(0, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
MemData {
|
||||||
|
used_bytes,
|
||||||
|
total_bytes,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,2 +1,13 @@
|
||||||
pub mod processes;
|
mod processes;
|
||||||
pub mod temperature;
|
mod temperature;
|
||||||
|
|
||||||
|
pub(crate) use processes::*;
|
||||||
|
pub(crate) use temperature::*;
|
||||||
|
|
||||||
|
// For now we only use a Linux-specific implementation for zfs ARC usage.
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(feature = "zfs")] {
|
||||||
|
mod memory;
|
||||||
|
pub(crate) use memory::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -272,13 +272,13 @@ fn read_proc(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct PrevProc {
|
pub struct PrevProc {
|
||||||
pub prev_idle: f64,
|
pub prev_idle: f64,
|
||||||
pub prev_non_idle: f64,
|
pub prev_non_idle: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub(crate) struct ProcHarvestOptions {
|
pub struct ProcHarvestOptions {
|
||||||
pub use_current_cpu_total: bool,
|
pub use_current_cpu_total: bool,
|
||||||
pub unnormalized_cpu: bool,
|
pub unnormalized_cpu: bool,
|
||||||
}
|
}
|
||||||
|
@ -289,13 +289,13 @@ fn is_str_numeric(s: &str) -> bool {
|
||||||
|
|
||||||
/// General args to keep around for reading proc data.
|
/// General args to keep around for reading proc data.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub(crate) struct ReadProcArgs {
|
pub struct ReadProcArgs {
|
||||||
pub(crate) use_current_cpu_total: bool,
|
pub use_current_cpu_total: bool,
|
||||||
pub(crate) cpu_usage: f64,
|
pub cpu_usage: f64,
|
||||||
pub(crate) cpu_fraction: f64,
|
pub cpu_fraction: f64,
|
||||||
pub(crate) total_memory: u64,
|
pub total_memory: u64,
|
||||||
pub(crate) time_difference_in_secs: u64,
|
pub time_difference_in_secs: u64,
|
||||||
pub(crate) uptime: u64,
|
pub uptime: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ProcessCollector {
|
pub struct ProcessCollector {
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod linux;
|
pub mod linux;
|
||||||
pub mod macos;
|
// pub mod macos;
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
pub mod nvidia;
|
pub mod nvidia;
|
||||||
|
pub mod starship;
|
||||||
pub mod sysinfo;
|
pub mod sysinfo;
|
||||||
pub mod unix;
|
pub mod unix;
|
||||||
pub mod windows;
|
// pub mod windows;
|
||||||
|
|
||||||
|
pub use common::*;
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(target_family = "windows")] {
|
if #[cfg(target_family = "unix")] {
|
||||||
pub use windows::processes::Pid as Pid;
|
|
||||||
} else if #[cfg(target_family = "unix")] {
|
|
||||||
pub use unix::processes::Pid as Pid;
|
pub use unix::processes::Pid as Pid;
|
||||||
}
|
}
|
||||||
|
// else if #[cfg(target_family = "windows")] {
|
||||||
|
// pub use windows::processes::Pid as Pid;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
32
src/new_data_collection/sources/nvidia/memory.rs
Normal file
32
src/new_data_collection/sources/nvidia/memory.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
use nvml_wrapper::Nvml;
|
||||||
|
|
||||||
|
use crate::new_data_collection::sources::memory::MemData;
|
||||||
|
|
||||||
|
/// Returns GPU memory usage per device name.
|
||||||
|
pub(crate) fn get_gpu_memory_usage(nvml: &Nvml) -> Vec<(String, MemData)> {
|
||||||
|
let Ok(num_gpu) = nvml.device_count() else {
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
|
||||||
|
(0..num_gpu)
|
||||||
|
.filter_map(|i| nvml.device_by_index(i).ok())
|
||||||
|
.filter_map(|device| match device.name() {
|
||||||
|
Ok(name) => {
|
||||||
|
match device.memory_info() {
|
||||||
|
Ok(mem_info) => Some((
|
||||||
|
name,
|
||||||
|
MemData {
|
||||||
|
used_bytes: mem_info.used,
|
||||||
|
total_bytes: mem_info.total,
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
Err(_) => {
|
||||||
|
// TODO: Maybe we should still return something here if it errors out.
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => None,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
mod memory;
|
||||||
|
pub(crate) use memory::*;
|
|
@ -10,17 +10,21 @@
|
||||||
|
|
||||||
use starship_battery::{
|
use starship_battery::{
|
||||||
units::{power::watt, ratio::percent, time::second},
|
units::{power::watt, ratio::percent, time::second},
|
||||||
Battery, Manager, State,
|
Battery, Manager,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
use super::battery::{BatteryHarvest, State};
|
||||||
pub struct BatteryHarvest {
|
|
||||||
pub charge_percent: f64,
|
impl From<starship_battery::State> for State {
|
||||||
pub secs_until_full: Option<i64>,
|
fn from(value: starship_battery::State) -> Self {
|
||||||
pub secs_until_empty: Option<i64>,
|
match value {
|
||||||
pub power_consumption_rate_watts: f64,
|
starship_battery::State::Unknown => State::Unknown,
|
||||||
pub health_percent: f64,
|
starship_battery::State::Charging => State::Charging,
|
||||||
pub state: State,
|
starship_battery::State::Discharging => State::Discharging,
|
||||||
|
starship_battery::State::Empty => State::Empty,
|
||||||
|
starship_battery::State::Full => State::Full,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh_batteries(manager: &Manager, batteries: &mut [Battery]) -> Vec<BatteryHarvest> {
|
pub fn refresh_batteries(manager: &Manager, batteries: &mut [Battery]) -> Vec<BatteryHarvest> {
|
||||||
|
@ -40,7 +44,7 @@ pub fn refresh_batteries(manager: &Manager, batteries: &mut [Battery]) -> Vec<Ba
|
||||||
charge_percent: f64::from(battery.state_of_charge().get::<percent>()),
|
charge_percent: f64::from(battery.state_of_charge().get::<percent>()),
|
||||||
power_consumption_rate_watts: f64::from(battery.energy_rate().get::<watt>()),
|
power_consumption_rate_watts: f64::from(battery.energy_rate().get::<watt>()),
|
||||||
health_percent: f64::from(battery.state_of_health().get::<percent>()),
|
health_percent: f64::from(battery.state_of_health().get::<percent>()),
|
||||||
state: battery.state(),
|
state: battery.state().into(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
40
src/new_data_collection/sources/sysinfo/cpu.rs
Normal file
40
src/new_data_collection/sources/sysinfo/cpu.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use sysinfo::{LoadAvg, System};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
data_collection::cpu::LoadAvgHarvest,
|
||||||
|
new_data_collection::sources::cpu::{CpuData, CpuDataType},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) fn get_cpu_data_list(sys: &System, show_average_cpu: bool) -> Vec<CpuData> {
|
||||||
|
let mut cpu_deque: VecDeque<_> = sys
|
||||||
|
.cpus()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, cpu)| CpuData {
|
||||||
|
entry_type: CpuDataType::Cpu(i),
|
||||||
|
usage: cpu.cpu_usage() as f64,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if show_average_cpu {
|
||||||
|
let cpu = sys.global_cpu_info();
|
||||||
|
|
||||||
|
cpu_deque.push_front(CpuData {
|
||||||
|
entry_type: CpuDataType::Avg,
|
||||||
|
usage: cpu.cpu_usage() as f64,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec::from(cpu_deque)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
pub(crate) fn get_load_avg() -> LoadAvgHarvest {
|
||||||
|
// The API for sysinfo apparently wants you to call it like this, rather than
|
||||||
|
// using a &System.
|
||||||
|
let LoadAvg { one, five, fifteen } = sysinfo::System::load_average();
|
||||||
|
|
||||||
|
[one as f32, five as f32, fifteen as f32]
|
||||||
|
}
|
47
src/new_data_collection/sources/sysinfo/memory.rs
Normal file
47
src/new_data_collection/sources/sysinfo/memory.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//! Collecting memory data using sysinfo.
|
||||||
|
|
||||||
|
use sysinfo::System;
|
||||||
|
|
||||||
|
use crate::new_data_collection::sources::memory::MemData;
|
||||||
|
|
||||||
|
/// Returns RAM usage.
|
||||||
|
pub(crate) fn get_ram_usage(sys: &System) -> MemData {
|
||||||
|
let mem_used = sys.used_memory();
|
||||||
|
let mem_total = sys.total_memory();
|
||||||
|
|
||||||
|
MemData {
|
||||||
|
used_bytes: mem_used,
|
||||||
|
total_bytes: mem_total,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns SWAP usage.
|
||||||
|
pub(crate) fn get_swap_usage(sys: &System) -> MemData {
|
||||||
|
let mem_used = sys.used_swap();
|
||||||
|
let mem_total = sys.total_swap();
|
||||||
|
|
||||||
|
MemData {
|
||||||
|
used_bytes: mem_used,
|
||||||
|
total_bytes: mem_total,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns cache usage. sysinfo has no way to do this directly but it should
|
||||||
|
/// equal the difference between the available and free memory. Free memory is
|
||||||
|
/// defined as memory not containing any data, which means cache and buffer
|
||||||
|
/// memory are not "free". Available memory is defined as memory able
|
||||||
|
/// to be allocated by processes, which includes cache and buffer memory. On
|
||||||
|
/// Windows, this will always be 0 - as such, we do not use this on Windows.
|
||||||
|
///
|
||||||
|
/// For more information, see [docs](https://docs.rs/sysinfo/latest/sysinfo/struct.System.html#method.available_memory)
|
||||||
|
/// and [memory explanation](https://askubuntu.com/questions/867068/what-is-available-memory-while-using-free-command)
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
pub(crate) fn get_cache_usage(sys: &System) -> MemData {
|
||||||
|
let mem_used = sys.available_memory().saturating_sub(sys.free_memory());
|
||||||
|
let mem_total = sys.total_memory();
|
||||||
|
|
||||||
|
MemData {
|
||||||
|
total_bytes: mem_total,
|
||||||
|
used_bytes: mem_used,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,4 @@
|
||||||
pub mod temperature;
|
pub mod temperature;
|
||||||
|
pub mod cpu;
|
||||||
|
pub mod disk;
|
||||||
|
pub mod memory;
|
|
@ -1 +0,0 @@
|
||||||
pub mod processes;
|
|
|
@ -1,3 +0,0 @@
|
||||||
/// A Windows process ID.
|
|
||||||
#[cfg(target_family = "windows")]
|
|
||||||
pub type Pid = usize;
|
|
Loading…
Reference in a new issue