From b20158820b243af5c929efd913e460956c1aed1a Mon Sep 17 00:00:00 2001 From: electria Date: Tue, 18 Aug 2026 14:05:36 -0700 Subject: [PATCH] feat: process list this is unnacceptably unperformant; I think I've reached the point at which sysinfo doesn't do what I want, which is keep track of a table of processes efficiently enough. --- src/app/info_stream.rs | 9 +++--- src/app/mod.rs | 61 +++++++++++++++++++++++++++++++---------- src/server/mod.rs | 6 ++-- src/sysinfo_snapshot.rs | 51 ++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 22 deletions(-) diff --git a/src/app/info_stream.rs b/src/app/info_stream.rs index ed21a87..787e459 100644 --- a/src/app/info_stream.rs +++ b/src/app/info_stream.rs @@ -8,7 +8,10 @@ use iced::{ use ron::de; use smol::{Timer, unblock}; -use crate::{app, sysinfo_snapshot::SysinfoSnapshot}; +use crate::{ + app, + sysinfo_snapshot::{self, SysinfoSnapshot}, +}; pub fn create() -> Task { if env::args() @@ -43,9 +46,7 @@ pub fn create() -> Task { 1, async |mut sender: futures::channel::mpsc::Sender| { let mut sys = sysinfo::System::new(); - let refreshes = sysinfo::RefreshKind::nothing() - .with_memory(sysinfo::MemoryRefreshKind::everything()) - .with_cpu(sysinfo::CpuRefreshKind::nothing().with_cpu_usage()); + let refreshes = sysinfo_snapshot::refreshes(); sys.refresh_specifics(refreshes); Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await; diff --git a/src/app/mod.rs b/src/app/mod.rs index 8725669..c636868 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -8,7 +8,7 @@ use iced::{ use crate::{ app::{graph::Graph, utils::maybe_widget}, - sysinfo_snapshot::SysinfoSnapshot, + sysinfo_snapshot::{ProcessSnapshot, SysinfoSnapshot}, utils::Toggle, }; @@ -22,6 +22,7 @@ enum Message { ToggleMain, ToggleCpu, ToggleMemory, + ToggleProcesses, Info(SysinfoSnapshot), } @@ -39,6 +40,9 @@ struct State { is_memory_section_shown: bool, memory_graph: Graph, swap_graph: Graph, + + is_processes_section_shown: bool, + processes: Vec, } impl State { @@ -65,6 +69,10 @@ impl State { self.is_memory_section_shown.toggle(); self.check_borders(); } + Message::ToggleProcesses => { + self.is_processes_section_shown.toggle(); + self.check_borders(); + } Message::Info(snapshot) => { self.uptime = snapshot.uptime(); @@ -89,6 +97,10 @@ impl State { .zip(snapshot.cpus.into_iter()) .for_each(|(graph, value)| graph.update(value)); } + + self.processes = snapshot.processes; + self.processes + .sort_by(|a, b| b.cpu_usage.total_cmp(&a.cpu_usage)); } } @@ -115,19 +127,39 @@ impl State { }), ]) .into(), - maybe_widget(self.is_memory_section_shown, || { - widget::row([ - widget::canvas(&self.memory_graph) - .width(Length::Fill) - .height(Length::Fill) - .into(), - widget::canvas(&self.swap_graph) - .width(Length::Fill) - .height(Length::Fill) - .into(), - ]) - .into() - }), + widget::row([ + maybe_widget(self.is_memory_section_shown, || { + widget::row([ + widget::canvas(&self.memory_graph) + .width(Length::Fill) + .height(Length::Fill) + .into(), + widget::canvas(&self.swap_graph) + .width(Length::Fill) + .height(Length::Fill) + .into(), + ]) + .into() + }), + maybe_widget(self.is_processes_section_shown, || { + widget::scrollable( + widget::grid(self.processes.iter().flat_map(|process| { + [ + widget::text(&process.name).into(), + widget::text(process.cpu_usage).into(), + widget::text(format!("{:?}", process.cpu_time())).into(), + widget::text(process.pid).into(), + ] + })) + .height(Length::Shrink) + .columns(4), + ) + .width(Length::Fill) + .height(Length::Fill) + .into() + }), + ]) + .into(), ]) .into() } @@ -139,6 +171,7 @@ impl State { Key::Character("M" | "1") => Some(Message::ToggleMain), Key::Character("c" | "2") => Some(Message::ToggleCpu), Key::Character("m" | "3") => Some(Message::ToggleMemory), + Key::Character("p" | "4") => Some(Message::ToggleProcesses), _ => None, } } diff --git a/src/server/mod.rs b/src/server/mod.rs index 445f393..5777c84 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -2,13 +2,11 @@ use std::{io::Write, thread}; use ron::{Error, ser}; -use crate::sysinfo_snapshot::SysinfoSnapshot; +use crate::sysinfo_snapshot::{self, SysinfoSnapshot}; pub fn run() -> Result<(), Error> { let mut sys = sysinfo::System::new(); - let refreshes = sysinfo::RefreshKind::nothing() - .with_memory(sysinfo::MemoryRefreshKind::everything()) - .with_cpu(sysinfo::CpuRefreshKind::nothing().with_cpu_usage()); + let refreshes = sysinfo_snapshot::refreshes(); sys.refresh_specifics(refreshes); thread::sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL); diff --git a/src/sysinfo_snapshot.rs b/src/sysinfo_snapshot.rs index 917b2f6..ef0f36e 100644 --- a/src/sysinfo_snapshot.rs +++ b/src/sysinfo_snapshot.rs @@ -1,7 +1,20 @@ use std::time::Duration; +pub fn refreshes() -> sysinfo::RefreshKind { + sysinfo::RefreshKind::nothing() + .with_memory(sysinfo::MemoryRefreshKind::everything()) + .with_cpu(sysinfo::CpuRefreshKind::nothing().with_cpu_usage()) + .with_processes( + sysinfo::ProcessRefreshKind::nothing() + .with_cpu() + .with_cmd(sysinfo::UpdateKind::OnlyIfNotSet) + .with_exe(sysinfo::UpdateKind::OnlyIfNotSet), + ) +} + #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct SysinfoSnapshot { + /// in seconds uptime: u64, pub cpu_total: f32, @@ -11,6 +24,8 @@ pub struct SysinfoSnapshot { pub memory_total: u64, pub swap: u64, pub swap_total: u64, + + pub processes: Vec, } impl SysinfoSnapshot { #[cfg_attr(not(feature = "app"), allow(dead_code))] @@ -31,6 +46,42 @@ impl From<&sysinfo::System> for SysinfoSnapshot { memory_total: value.total_memory(), swap: value.used_swap(), swap_total: value.total_swap(), + + processes: value + .processes() + .iter() + .map(|(_pid, process)| ProcessSnapshot::from(process)) + .collect(), + } + } +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct ProcessSnapshot { + pub pid: usize, + + /// in CPU-milliseconds + accumulated_cpu_time: u64, + + /// note: it's by core, and can be up to eg 1600 on a 16-core device + pub cpu_usage: f32, + + pub name: String, +} +impl ProcessSnapshot { + pub fn cpu_time(&self) -> Duration { + Duration::from_millis(self.accumulated_cpu_time) + } +} + +impl From<&sysinfo::Process> for ProcessSnapshot { + fn from(value: &sysinfo::Process) -> Self { + Self { + pid: value.pid().into(), + accumulated_cpu_time: value.accumulated_cpu_time(), + cpu_usage: value.cpu_usage(), + + name: value.name().to_string_lossy().into_owned(), } } }