diff --git a/src/app/data_farmer.rs b/src/app/data_farmer.rs index e409957fe..a89af0a7c 100644 --- a/src/app/data_farmer.rs +++ b/src/app/data_farmer.rs @@ -43,6 +43,8 @@ pub struct TimedData { pub swap_data: Option, #[cfg(feature = "zfs")] pub arc_data: Option, + #[cfg(feature = "gpu")] + pub gpu_data: Vec>, } #[derive(Clone, Debug, Default)] @@ -133,6 +135,8 @@ pub struct DataCollection { pub battery_harvest: Vec, #[cfg(feature = "zfs")] pub arc_harvest: memory::MemHarvest, + #[cfg(feature = "gpu")] + pub gpu_harvest: Vec<(String, memory::MemHarvest)>, } impl Default for DataCollection { @@ -155,6 +159,8 @@ impl Default for DataCollection { battery_harvest: Vec::default(), #[cfg(feature = "zfs")] arc_harvest: memory::MemHarvest::default(), + #[cfg(feature = "gpu")] + gpu_harvest: Vec::default(), } } } @@ -179,6 +185,10 @@ impl DataCollection { { self.arc_harvest = memory::MemHarvest::default(); } + #[cfg(feature = "gpu")] + { + self.gpu_harvest = Vec::default(); + } } pub fn clean_data(&mut self, max_time_millis: u64) { @@ -222,6 +232,14 @@ impl DataCollection { self.eat_arc(arc, &mut new_entry); } } + + #[cfg(feature = "gpu")] + { + if let Some(gpu) = harvested_data.gpu { + self.eat_gpu(gpu, &mut new_entry); + } + } + // CPU if let Some(cpu) = harvested_data.cpu { self.eat_cpu(cpu, &mut new_entry); @@ -405,8 +423,18 @@ impl DataCollection { #[cfg(feature = "zfs")] fn eat_arc(&mut self, arc: memory::MemHarvest, new_entry: &mut TimedData) { - // Arc new_entry.arc_data = arc.use_percent; self.arc_harvest = arc; } + + #[cfg(feature = "gpu")] + fn eat_gpu(&mut self, gpu: Vec<(String, memory::MemHarvest)>, new_entry: &mut TimedData) { + // Note this only pre-calculates the data points - the names will be + // within the local copy of gpu_harvest. Since it's all sequential + // it probably doesn't matter anyways. + gpu.iter().for_each(|data| { + new_entry.gpu_data.push(data.1.use_percent); + }); + self.gpu_harvest = gpu.to_vec(); + } } diff --git a/src/app/data_harvester.rs b/src/app/data_harvester.rs index cb02bdb1a..4f163755b 100644 --- a/src/app/data_harvester.rs +++ b/src/app/data_harvester.rs @@ -17,6 +17,9 @@ use futures::join; use super::DataFilters; +#[cfg(feature = "nvidia")] +pub mod nvidia; + #[cfg(feature = "battery")] pub mod batteries; pub mod cpu; @@ -42,6 +45,8 @@ pub struct Data { pub list_of_batteries: Option>, #[cfg(feature = "zfs")] pub arc: Option, + #[cfg(feature = "gpu")] + pub gpu: Option>, } impl Default for Data { @@ -61,6 +66,8 @@ impl Default for Data { list_of_batteries: None, #[cfg(feature = "zfs")] arc: None, + #[cfg(feature = "gpu")] + gpu: None, } } } @@ -83,6 +90,10 @@ impl Data { { self.arc = None; } + #[cfg(feature = "gpu")] + { + self.gpu = None; + } } } @@ -423,19 +434,24 @@ impl DataCollector { self.data.network = net_data; } - if let Ok(memory) = mem_res.0 { + if let Ok(memory) = mem_res.ram { self.data.memory = memory; } - if let Ok(swap) = mem_res.1 { + if let Ok(swap) = mem_res.swap { self.data.swap = swap; } #[cfg(feature = "zfs")] - if let Ok(arc) = mem_res.2 { + if let Ok(arc) = mem_res.arc { self.data.arc = arc; } + #[cfg(feature = "gpu")] + if let Ok(gpu) = mem_res.gpus { + self.data.gpu = gpu; + } + if let Ok(disks) = disk_res { self.data.disks = disks; } diff --git a/src/app/data_harvester/cpu/sysinfo.rs b/src/app/data_harvester/cpu/sysinfo.rs index a5f363b8b..54318e1b0 100644 --- a/src/app/data_harvester/cpu/sysinfo.rs +++ b/src/app/data_harvester/cpu/sysinfo.rs @@ -3,9 +3,9 @@ use std::collections::VecDeque; -use sysinfo::{LoadAvg, System, SystemExt}; +use sysinfo::{CpuExt, LoadAvg, System, SystemExt}; -use super::{CpuData, CpuHarvest, PastCpuTotal, PastCpuWork}; +use super::{CpuData, CpuDataType, CpuHarvest, PastCpuTotal, PastCpuWork}; use crate::app::data_harvester::cpu::LoadAvgHarvest; pub async fn get_cpu_data_list( @@ -14,12 +14,11 @@ pub async fn get_cpu_data_list( _previous_average_cpu_time: &mut Option<(PastCpuWork, PastCpuTotal)>, ) -> crate::error::Result { let mut cpu_deque: VecDeque<_> = sys - .processors() + .cpus() .iter() .enumerate() .map(|(i, cpu)| CpuData { - cpu_prefix: "CPU".to_string(), - cpu_count: Some(i), + data_type: CpuDataType::Cpu(i), cpu_usage: cpu.cpu_usage() as f64, }) .collect(); @@ -28,8 +27,7 @@ pub async fn get_cpu_data_list( let cpu = sys.global_cpu_info(); cpu_deque.push_front(CpuData { - cpu_prefix: "AVG".to_string(), - cpu_count: None, + data_type: CpuDataType::Avg, cpu_usage: cpu.cpu_usage() as f64, }) } diff --git a/src/app/data_harvester/memory/general.rs b/src/app/data_harvester/memory/general.rs index 45fb5fddd..84ac1e0d2 100644 --- a/src/app/data_harvester/memory/general.rs +++ b/src/app/data_harvester/memory/general.rs @@ -14,3 +14,13 @@ pub struct MemHarvest { pub mem_used_in_kib: u64, pub use_percent: Option, } + +#[derive(Debug)] +pub struct MemCollect { + pub ram: crate::utils::error::Result>, + pub swap: crate::utils::error::Result>, + #[cfg(feature = "zfs")] + pub arc: crate::utils::error::Result>, + #[cfg(feature = "gpu")] + pub gpus: crate::utils::error::Result>>, +} diff --git a/src/app/data_harvester/memory/general/heim.rs b/src/app/data_harvester/memory/general/heim.rs index 71a68430f..83f78ae43 100644 --- a/src/app/data_harvester/memory/general/heim.rs +++ b/src/app/data_harvester/memory/general/heim.rs @@ -1,20 +1,26 @@ //! Data collection for memory via heim. -use crate::data_harvester::memory::MemHarvest; - -pub async fn get_mem_data( - actually_get: bool, -) -> ( - crate::utils::error::Result>, - crate::utils::error::Result>, - crate::utils::error::Result>, -) { - use futures::join; +use crate::data_harvester::memory::{MemCollect, MemHarvest}; +pub async fn get_mem_data(actually_get: bool) -> MemCollect { if !actually_get { - (Ok(None), Ok(None), Ok(None)) + MemCollect { + ram: Ok(None), + swap: Ok(None), + #[cfg(feature = "zfs")] + arc: Ok(None), + #[cfg(feature = "gpu")] + gpus: Ok(None), + } } else { - join!(get_ram_data(), get_swap_data(), get_arc_data()) + MemCollect { + ram: get_ram_data().await, + swap: get_swap_data().await, + #[cfg(feature = "zfs")] + arc: get_arc_data().await, + #[cfg(feature = "gpu")] + gpus: get_gpu_data().await, + } } } @@ -170,16 +176,15 @@ pub async fn get_swap_data() -> crate::utils::error::Result> })) } +#[cfg(feature = "zfs")] pub async fn get_arc_data() -> crate::utils::error::Result> { - #[cfg(not(feature = "zfs"))] - let (mem_total_in_kib, mem_used_in_kib) = (0, 0); - - #[cfg(feature = "zfs")] let (mem_total_in_kib, mem_used_in_kib) = { #[cfg(target_os = "linux")] { let mut mem_arc = 0; let mut mem_total = 0; + let mut zfs_keys_read: u8 = 0; + const ZFS_KEYS_NEEDED: u8 = 2; use smol::fs::read_to_string; let arcinfo = read_to_string("/proc/spl/kstat/zfs/arcstats").await?; for line in arcinfo.lines() { @@ -191,8 +196,7 @@ pub async fn get_arc_data() -> crate::utils::error::Result> { continue; } }; - let mut zfs_keys_read: u8 = 0; - const ZFS_KEYS_NEEDED: u8 = 2; + if let Some((_type, number)) = value.trim_start().rsplit_once(' ') { // Parse the value, remember it's in bytes! if let Ok(number) = number.parse::() { @@ -247,3 +251,39 @@ pub async fn get_arc_data() -> crate::utils::error::Result> { }, })) } + +#[cfg(feature = "nvidia")] +pub async fn get_gpu_data() -> crate::utils::error::Result>> { + use crate::data_harvester::nvidia::NVML_DATA; + if let Ok(nvml) = &*NVML_DATA { + if let Ok(ngpu) = nvml.device_count() { + let mut results = Vec::with_capacity(ngpu as usize); + for i in 0..ngpu { + if let Ok(device) = nvml.device_by_index(i) { + if let (Ok(name), Ok(mem)) = (device.name(), device.memory_info()) { + // add device memory in bytes + let mem_total_in_kib = mem.total / 1024; + let mem_used_in_kib = mem.used / 1024; + results.push(( + name, + MemHarvest { + mem_total_in_kib, + mem_used_in_kib, + use_percent: if mem_total_in_kib == 0 { + None + } else { + Some(mem_used_in_kib as f64 / mem_total_in_kib as f64 * 100.0) + }, + }, + )); + } + } + } + Ok(Some(results)) + } else { + Ok(None) + } + } else { + Ok(None) + } +} diff --git a/src/app/data_harvester/memory/general/sysinfo.rs b/src/app/data_harvester/memory/general/sysinfo.rs index da4bd812b..cf80c2065 100644 --- a/src/app/data_harvester/memory/general/sysinfo.rs +++ b/src/app/data_harvester/memory/general/sysinfo.rs @@ -1,26 +1,32 @@ //! Data collection for memory via sysinfo. -use crate::data_harvester::memory::MemHarvest; +use crate::data_harvester::memory::{MemCollect, MemHarvest}; use sysinfo::{System, SystemExt}; -pub async fn get_mem_data( - sys: &System, actually_get: bool, -) -> ( - crate::utils::error::Result>, - crate::utils::error::Result>, - crate::utils::error::Result>, -) { - use futures::join; - +pub async fn get_mem_data(sys: &System, actually_get: bool) -> MemCollect { if !actually_get { - (Ok(None), Ok(None), Ok(None)) + MemCollect { + ram: Ok(None), + swap: Ok(None), + #[cfg(feature = "zfs")] + arc: Ok(None), + #[cfg(feature = "gpu")] + gpus: Ok(None), + } } else { - join!(get_ram_data(sys), get_swap_data(sys), get_arc_data()) + MemCollect { + ram: get_ram_data(sys).await, + swap: get_swap_data(sys).await, + #[cfg(feature = "zfs")] + arc: get_arc_data().await, + #[cfg(feature = "gpu")] + gpus: get_gpu_data().await, + } } } pub async fn get_ram_data(sys: &System) -> crate::utils::error::Result> { - let (mem_total_in_kib, mem_used_in_kib) = (sys.total_memory() / 1024, sys.used_memory()) / 1024; + let (mem_total_in_kib, mem_used_in_kib) = (sys.total_memory() / 1024, sys.used_memory() / 1024); Ok(Some(MemHarvest { mem_total_in_kib, @@ -47,11 +53,8 @@ pub async fn get_swap_data(sys: &System) -> crate::utils::error::Result crate::utils::error::Result> { - #[cfg(not(feature = "zfs"))] - let (mem_total_in_kib, mem_used_in_kib) = (0, 0); - - #[cfg(feature = "zfs")] let (mem_total_in_kib, mem_used_in_kib) = { #[cfg(target_os = "freebsd")] { @@ -82,3 +85,39 @@ pub async fn get_arc_data() -> crate::utils::error::Result> { }, })) } + +#[cfg(feature = "nvidia")] +pub async fn get_gpu_data() -> crate::utils::error::Result>> { + use crate::data_harvester::nvidia::NVML_DATA; + if let Ok(nvml) = &*NVML_DATA { + if let Ok(ngpu) = nvml.device_count() { + let mut results = Vec::with_capacity(ngpu as usize); + for i in 0..ngpu { + if let Ok(device) = nvml.device_by_index(i) { + if let (Ok(name), Ok(mem)) = (device.name(), device.memory_info()) { + // add device memory in bytes + let mem_total_in_kib = mem.total / 1024; + let mem_used_in_kib = mem.used / 1024; + results.push(( + name, + MemHarvest { + mem_total_in_kib, + mem_used_in_kib, + use_percent: if mem_total_in_kib == 0 { + None + } else { + Some(mem_used_in_kib as f64 / mem_total_in_kib as f64 * 100.0) + }, + }, + )); + } + } + } + Ok(Some(results)) + } else { + Ok(None) + } + } else { + Ok(None) + } +} diff --git a/src/app/data_harvester/nvidia.rs b/src/app/data_harvester/nvidia.rs new file mode 100644 index 000000000..72928a7cc --- /dev/null +++ b/src/app/data_harvester/nvidia.rs @@ -0,0 +1,3 @@ +use nvml_wrapper::{error::NvmlError, NVML}; +use once_cell::sync::Lazy; +pub static NVML_DATA: Lazy> = Lazy::new(NVML::init); diff --git a/src/app/data_harvester/temperature/nvidia.rs b/src/app/data_harvester/temperature/nvidia.rs index c30345ab4..049708b74 100644 --- a/src/app/data_harvester/temperature/nvidia.rs +++ b/src/app/data_harvester/temperature/nvidia.rs @@ -5,12 +5,14 @@ use super::{ TemperatureType, }; -use nvml_wrapper::{enum_wrappers::device::TemperatureSensor, NVML}; +use nvml_wrapper::enum_wrappers::device::TemperatureSensor; + +use crate::data_harvester::nvidia::NVML_DATA; pub fn add_nvidia_data( temperature_vec: &mut Vec, temp_type: &TemperatureType, filter: &Option, ) -> crate::utils::error::Result<()> { - if let Ok(nvml) = NVML::init() { + if let Ok(nvml) = &*NVML_DATA { if let Ok(ngpu) = nvml.device_count() { for i in 0..ngpu { if let Ok(device) = nvml.device_by_index(i) { diff --git a/src/bin/main.rs b/src/bin/main.rs index 4d7a0a34e..a5473b007 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -210,6 +210,11 @@ fn main() -> Result<()> { app.converted_data.arc_data = convert_arc_data_points(&app.data_collection); } + #[cfg(feature = "gpu")] + { + app.converted_data.gpu_data = + convert_gpu_data(&app.data_collection); + } let (memory_labels, swap_labels) = convert_mem_labels(&app.data_collection); diff --git a/src/canvas.rs b/src/canvas.rs index f171bddd1..c9312700f 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -451,19 +451,29 @@ impl Painter { } }; - let mut mem_rows = 0; + let mut mem_rows = 1; + + if app_state.converted_data.swap_labels.is_some() { + mem_rows += 1; // add row for swap + } #[cfg(feature = "zfs")] { - let arc_data = &app_state.converted_data.arc_data; - if let Some(arc) = arc_data.last() { - if arc.1 != 0.0 { - mem_rows += 1; // add row for arc - } + if app_state.converted_data.arc_labels.is_some() { + mem_rows += 1; // add row for arc + } + } + + #[cfg(feature = "gpu")] + { + if let Some(gpu_data) = &app_state.converted_data.gpu_data { + mem_rows += gpu_data.len() as u16; // add row(s) for gpu } } - mem_rows += 2; // add rows for SWAP and MEM + if mem_rows == 1 { + mem_rows += 1; // need at least 2 rows for RX and TX + } let vertical_chunks = Layout::default() .direction(Direction::Vertical) diff --git a/src/canvas/canvas_colours.rs b/src/canvas/canvas_colours.rs index 9630cc42a..ab9b079e4 100644 --- a/src/canvas/canvas_colours.rs +++ b/src/canvas/canvas_colours.rs @@ -18,6 +18,7 @@ pub struct CanvasColours { pub ram_style: Style, pub swap_style: Style, pub arc_style: Style, + pub gpu_colour_styles: Vec