From a7a67022be1b532ce4261f67f2756dea210d4ced Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Fri, 10 Jan 2025 15:51:04 +0800 Subject: [PATCH] simple implementation for top summary --- Cargo.lock | 32 +++++++++++++++ Cargo.toml | 1 + src/uu/top/Cargo.toml | 2 + src/uu/top/src/picker.rs | 6 +++ src/uu/top/src/top.rs | 88 ++++++++++++++++++++++++++++++++++++---- 5 files changed, 121 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f96aaa3..825d36f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -444,6 +444,12 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "nix" version = "0.29.0" @@ -456,6 +462,16 @@ dependencies = [ "libc", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "ntapi" version = "0.4.1" @@ -862,6 +878,20 @@ dependencies = [ "windows 0.57.0", ] +[[package]] +name = "systemstat" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668a4db78b439df482c238f559e4ea869017f9e62ef0a059c8bfcd841a4df544" +dependencies = [ + "bytesize", + "lazy_static", + "libc", + "nom", + "time", + "winapi", +] + [[package]] name = "tempfile" version = "3.14.0" @@ -1112,12 +1142,14 @@ dependencies = [ name = "uu_top" version = "0.0.1" dependencies = [ + "bytesize", "chrono", "clap", "libc", "nix", "prettytable-rs", "sysinfo", + "systemstat", "uucore", ] diff --git a/Cargo.toml b/Cargo.toml index b8c91318..88a9365c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ uucore = "0.0.28" walkdir = "2.5.0" windows = { version = "0.59.0" } xattr = "1.3.1" +systemstat = "0.2.4" [dependencies] clap = { workspace = true } diff --git a/src/uu/top/Cargo.toml b/src/uu/top/Cargo.toml index 800a2fc3..8e4855e5 100644 --- a/src/uu/top/Cargo.toml +++ b/src/uu/top/Cargo.toml @@ -19,6 +19,8 @@ nix = { workspace = true } prettytable-rs = { workspace = true } sysinfo = { workspace = true } chrono = { workspace = true } +systemstat = { workspace = true } +bytesize = { workspace = true } [lib] path = "src/top.rs" diff --git a/src/uu/top/src/picker.rs b/src/uu/top/src/picker.rs index f103e9b4..b96828ae 100644 --- a/src/uu/top/src/picker.rs +++ b/src/uu/top/src/picker.rs @@ -12,13 +12,19 @@ use std::{ sync::{OnceLock, RwLock}, }; use sysinfo::{Pid, System, Users}; +use systemstat::Platform; static SYSINFO: OnceLock> = OnceLock::new(); +static SYSTEMSTAT: OnceLock> = OnceLock::new(); pub fn sysinfo() -> &'static RwLock { SYSINFO.get_or_init(|| RwLock::new(System::new_all())) } +pub fn systemstat() -> &'static RwLock { + SYSTEMSTAT.get_or_init(|| RwLock::new(systemstat::System::new())) +} + pub(crate) fn pickers(fields: &[String]) -> Vec String>> { fields .iter() diff --git a/src/uu/top/src/top.rs b/src/uu/top/src/top.rs index 6c4a12cc..8312e5a2 100644 --- a/src/uu/top/src/top.rs +++ b/src/uu/top/src/top.rs @@ -3,12 +3,15 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +use crate::picker::systemstat; +use bytesize::ByteSize; use clap::{arg, crate_version, value_parser, ArgAction, ArgGroup, ArgMatches, Command}; use picker::pickers; use picker::sysinfo; use prettytable::{format::consts::FORMAT_CLEAN, Row, Table}; use std::{thread::sleep, time::Duration}; use sysinfo::{Pid, Users}; +use systemstat::{CPULoad, Platform}; use uucore::{ error::{UResult, USimpleError}, format_usage, help_about, help_usage, @@ -53,8 +56,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { // Must refresh twice. // https://docs.rs/sysinfo/0.31.2/sysinfo/struct.System.html#method.refresh_cpu_usage picker::sysinfo().write().unwrap().refresh_all(); + let cpu_load = systemstat().read().unwrap().cpu_load_aggregate()?; sleep(Duration::from_millis(200)); picker::sysinfo().write().unwrap().refresh_all(); + let cpu_load = cpu_load.done()?; let settings = Settings::new(&matches); @@ -97,7 +102,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { table }; - println!("{}", header()); + println!("{}", header(cpu_load)); println!("\n"); let cutter = { @@ -113,11 +118,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } }; - table - .to_string() - .lines() - .map(cutter) - .for_each(|it| println!("{}", it)); + table.to_string().lines().map(cutter).for_each(|_| {}); + // .for_each(|it| println!("{}", it)); Ok(()) } @@ -157,9 +159,79 @@ where } } +fn format_uptime(duration: Duration) -> String { + let mut num = duration.as_secs(); + if num < 60 { + return format!("{} s", num); + } + + num /= 60; //to minutes + if num < 60 { + return format!("{} m", num); + } + if num < 600 { + return format!("{}:{:2}", num / 60, num % 60); + } + + num /= 60; //to hours + if num < 24 { + format!("{} h", num) + } else { + num /= 24; //to days + format!("{} d", num) + } +} + // TODO: Implement information collecting. -fn header() -> String { - "TODO".into() +fn header(cpu_load: CPULoad) -> String { + let systemstat = systemstat().read().unwrap(); + let sysinfo = sysinfo().read().unwrap(); + let process = sysinfo.processes(); + let mut running_process = 0; + let mut sleeping_process = 0; + let mut stopped_process = 0; + let mut zombie_process = 0; + for (_, process) in process.iter() { + match process.status() { + sysinfo::ProcessStatus::Run => running_process += 1, + sysinfo::ProcessStatus::Sleep => sleeping_process += 1, + sysinfo::ProcessStatus::Stop => stopped_process += 1, + sysinfo::ProcessStatus::Zombie => zombie_process += 1, + _ => {} + }; + } + + //, {} hi, {} si, {} st + format!( + "top - {} up {}, %n user, load average: {:.2}, {:.2}, {:.2}\n\ + Tasks: {} total, {} running, {} sleeping, {} stopped, {} zombie\n\ + %Cpu(s): {:.1} us, {:.1} sy, {:.1} ni, {:.1} id, {:.1} wa\n\ + MiB Mem : {:8.1} total, {:8.1} free, {:8.1} used, {:8.1} buff/cache\n\ + MiB Swap: {:8.1} total, {:8.1} free, {:8.1} used, {:8.1} avail Mem\n", + chrono::Local::now().format("%H:%M:%S"), + format_uptime(systemstat.uptime().unwrap()), + systemstat.load_average().unwrap().one, + systemstat.load_average().unwrap().five, + systemstat.load_average().unwrap().fifteen, + process.len(), + running_process, + sleeping_process, + stopped_process, + zombie_process, + cpu_load.user * 100.0, + cpu_load.system * 100.0, + cpu_load.nice * 100.0, + cpu_load.idle * 100.0, + cpu_load.platform.iowait * 100.0, + ByteSize::b(sysinfo.total_memory()).0 as f64 / bytesize::MIB as f64, + ByteSize::b(sysinfo.free_memory()).0 as f64 / bytesize::MIB as f64, + ByteSize::b(sysinfo.used_memory()).0 as f64 / bytesize::MIB as f64, + ByteSize::b(sysinfo.total_memory() - sysinfo.free_memory()).0 as f64 / bytesize::MIB as f64, + ByteSize::b(sysinfo.total_swap()).0 as f64 / bytesize::MIB as f64, + ByteSize::b(sysinfo.free_swap()).0 as f64 / bytesize::MIB as f64, + ByteSize::b(sysinfo.used_swap()).0 as f64 / bytesize::MIB as f64, + ByteSize::b(sysinfo.total_memory() - sysinfo.free_memory()).0 as f64 / bytesize::MIB as f64, + ) } // TODO: Implement fields selecting