mirror of
https://github.com/uutils/procps.git
synced 2026-06-10 16:14:00 -07:00
vmstat: implement --stats
This commit is contained in:
+82
-31
@@ -61,6 +61,30 @@ impl ProcData {
|
||||
let idle_time = parts.next().unwrap().parse::<f64>().unwrap();
|
||||
(uptime, idle_time)
|
||||
}
|
||||
|
||||
pub fn get_one<T>(table: &HashMap<String, String>, name: &str) -> T
|
||||
where
|
||||
T: Default + std::str::FromStr,
|
||||
{
|
||||
table
|
||||
.get(name)
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub struct CpuLoadRaw {
|
||||
pub user: u64,
|
||||
pub nice: u64,
|
||||
pub system: u64,
|
||||
pub idle: u64,
|
||||
pub io_wait: u64,
|
||||
pub hardware_interrupt: u64,
|
||||
pub software_interrupt: u64,
|
||||
pub steal_time: u64,
|
||||
pub guest: u64,
|
||||
pub guest_nice: u64,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -78,7 +102,7 @@ pub struct CpuLoad {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
impl CpuLoad {
|
||||
impl CpuLoadRaw {
|
||||
pub fn current() -> Self {
|
||||
let file = std::fs::File::open(std::path::Path::new("/proc/stat")).unwrap(); // do not use `parse_proc_file` here because only one line is used
|
||||
let content = std::io::read_to_string(file).unwrap();
|
||||
@@ -93,37 +117,64 @@ impl CpuLoad {
|
||||
|
||||
fn from_str(s: &str) -> Self {
|
||||
let load = s.split(' ').filter(|s| !s.is_empty()).collect::<Vec<_>>();
|
||||
let user = load[0].parse::<f64>().unwrap();
|
||||
let nice = load[1].parse::<f64>().unwrap();
|
||||
let system = load[2].parse::<f64>().unwrap();
|
||||
let idle = load[3].parse::<f64>().unwrap_or_default(); // since 2.5.41
|
||||
let io_wait = load[4].parse::<f64>().unwrap_or_default(); // since 2.5.41
|
||||
let hardware_interrupt = load[5].parse::<f64>().unwrap_or_default(); // since 2.6.0
|
||||
let software_interrupt = load[6].parse::<f64>().unwrap_or_default(); // since 2.6.0
|
||||
let steal_time = load[7].parse::<f64>().unwrap_or_default(); // since 2.6.11
|
||||
let guest = load[8].parse::<f64>().unwrap_or_default(); // since 2.6.24
|
||||
let guest_nice = load[9].parse::<f64>().unwrap_or_default(); // since 2.6.33
|
||||
let total = user
|
||||
+ nice
|
||||
+ system
|
||||
+ idle
|
||||
+ io_wait
|
||||
+ hardware_interrupt
|
||||
+ software_interrupt
|
||||
+ steal_time
|
||||
+ guest
|
||||
+ guest_nice;
|
||||
let user = load[0].parse::<u64>().unwrap();
|
||||
let nice = load[1].parse::<u64>().unwrap();
|
||||
let system = load[2].parse::<u64>().unwrap();
|
||||
let idle = load[3].parse::<u64>().unwrap_or_default(); // since 2.5.41
|
||||
let io_wait = load[4].parse::<u64>().unwrap_or_default(); // since 2.5.41
|
||||
let hardware_interrupt = load[5].parse::<u64>().unwrap_or_default(); // since 2.6.0
|
||||
let software_interrupt = load[6].parse::<u64>().unwrap_or_default(); // since 2.6.0
|
||||
let steal_time = load[7].parse::<u64>().unwrap_or_default(); // since 2.6.11
|
||||
let guest = load[8].parse::<u64>().unwrap_or_default(); // since 2.6.24
|
||||
let guest_nice = load[9].parse::<u64>().unwrap_or_default(); // since 2.6.33
|
||||
|
||||
Self {
|
||||
user: user / total * 100.0,
|
||||
system: system / total * 100.0,
|
||||
nice: nice / total * 100.0,
|
||||
idle: idle / total * 100.0,
|
||||
io_wait: io_wait / total * 100.0,
|
||||
hardware_interrupt: hardware_interrupt / total * 100.0,
|
||||
software_interrupt: software_interrupt / total * 100.0,
|
||||
steal_time: steal_time / total * 100.0,
|
||||
guest: guest / total * 100.0,
|
||||
guest_nice: guest_nice / total * 100.0,
|
||||
user,
|
||||
system,
|
||||
nice,
|
||||
idle,
|
||||
io_wait,
|
||||
hardware_interrupt,
|
||||
software_interrupt,
|
||||
steal_time,
|
||||
guest,
|
||||
guest_nice,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
impl CpuLoad {
|
||||
pub fn current() -> Self {
|
||||
Self::from_raw(CpuLoadRaw::current())
|
||||
}
|
||||
|
||||
pub fn from_proc_map(proc_map: &HashMap<String, String>) -> Self {
|
||||
Self::from_raw(CpuLoadRaw::from_proc_map(proc_map))
|
||||
}
|
||||
|
||||
pub fn from_raw(raw_data: CpuLoadRaw) -> Self {
|
||||
let total = (raw_data.user
|
||||
+ raw_data.nice
|
||||
+ raw_data.system
|
||||
+ raw_data.idle
|
||||
+ raw_data.io_wait
|
||||
+ raw_data.hardware_interrupt
|
||||
+ raw_data.software_interrupt
|
||||
+ raw_data.steal_time
|
||||
+ raw_data.guest
|
||||
+ raw_data.guest_nice) as f64;
|
||||
Self {
|
||||
user: raw_data.user as f64 / total * 100.0,
|
||||
system: raw_data.system as f64 / total * 100.0,
|
||||
nice: raw_data.nice as f64 / total * 100.0,
|
||||
idle: raw_data.idle as f64 / total * 100.0,
|
||||
io_wait: raw_data.io_wait as f64 / total * 100.0,
|
||||
hardware_interrupt: raw_data.hardware_interrupt as f64 / total * 100.0,
|
||||
software_interrupt: raw_data.software_interrupt as f64 / total * 100.0,
|
||||
steal_time: raw_data.steal_time as f64 / total * 100.0,
|
||||
guest: raw_data.guest as f64 / total * 100.0,
|
||||
guest_nice: raw_data.guest_nice as f64 / total * 100.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+128
-1
@@ -4,7 +4,7 @@
|
||||
// file that was distributed with this source code.
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::{CpuLoad, Meminfo, ProcData};
|
||||
use crate::{CpuLoad, CpuLoadRaw, Meminfo, ProcData};
|
||||
#[cfg(target_os = "linux")]
|
||||
use clap::ArgMatches;
|
||||
|
||||
@@ -76,6 +76,133 @@ pub fn get_pickers(matches: &ArgMatches) -> Vec<Picker> {
|
||||
pickers
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn get_stats() -> Vec<(String, u64)> {
|
||||
let proc_data = ProcData::new();
|
||||
let memory_info = Meminfo::from_proc_map(&proc_data.meminfo);
|
||||
let cpu_load = CpuLoadRaw::from_proc_map(&proc_data.stat);
|
||||
|
||||
vec![
|
||||
(
|
||||
"K total memory".to_string(),
|
||||
memory_info.mem_total.0 / bytesize::KB,
|
||||
),
|
||||
(
|
||||
"K used memory".to_string(),
|
||||
(memory_info.mem_total - memory_info.mem_available).0 / bytesize::KB,
|
||||
),
|
||||
(
|
||||
"K active memory".to_string(),
|
||||
memory_info.active.0 / bytesize::KB,
|
||||
),
|
||||
(
|
||||
"K inactive memory".to_string(),
|
||||
memory_info.inactive.0 / bytesize::KB,
|
||||
),
|
||||
(
|
||||
"K free memory".to_string(),
|
||||
memory_info.mem_free.0 / bytesize::KB,
|
||||
),
|
||||
(
|
||||
"K buffer memory".to_string(),
|
||||
memory_info.buffers.0 / bytesize::KB,
|
||||
),
|
||||
(
|
||||
"K swap cache".to_string(),
|
||||
memory_info.cached.0 / bytesize::KB,
|
||||
),
|
||||
(
|
||||
"K total swap".to_string(),
|
||||
memory_info.swap_total.0 / bytesize::KB,
|
||||
),
|
||||
(
|
||||
"K used swap".to_string(),
|
||||
(memory_info.swap_total - memory_info.swap_free).0 / bytesize::KB,
|
||||
),
|
||||
(
|
||||
"K free swap".to_string(),
|
||||
memory_info.swap_free.0 / bytesize::KB,
|
||||
),
|
||||
(
|
||||
"non-nice user cpu ticks".to_string(),
|
||||
cpu_load.user - cpu_load.nice,
|
||||
),
|
||||
("nice user cpu ticks".to_string(), cpu_load.nice),
|
||||
("system cpu ticks".to_string(), cpu_load.system),
|
||||
("idle cpu ticks".to_string(), cpu_load.idle),
|
||||
("IO-wait cpu ticks".to_string(), cpu_load.io_wait),
|
||||
("IRQ cpu ticks".to_string(), cpu_load.hardware_interrupt),
|
||||
("softirq cpu ticks".to_string(), cpu_load.software_interrupt),
|
||||
("stolen cpu ticks".to_string(), cpu_load.steal_time),
|
||||
("non-nice guest cpu ticks".to_string(), cpu_load.guest),
|
||||
("nice guest cpu ticks".to_string(), cpu_load.guest_nice),
|
||||
(
|
||||
"K paged in".to_string(),
|
||||
ProcData::get_one(&proc_data.vmstat, "pgpgin"),
|
||||
),
|
||||
(
|
||||
"K paged out".to_string(),
|
||||
ProcData::get_one(&proc_data.vmstat, "pgpgout"),
|
||||
),
|
||||
(
|
||||
"pages swapped in".to_string(),
|
||||
ProcData::get_one(&proc_data.vmstat, "pswpin"),
|
||||
),
|
||||
(
|
||||
"pages swapped out".to_string(),
|
||||
ProcData::get_one(&proc_data.vmstat, "pswpout"),
|
||||
),
|
||||
(
|
||||
"pages alloc in dma".to_string(),
|
||||
ProcData::get_one(&proc_data.vmstat, "pgalloc_dma"),
|
||||
),
|
||||
(
|
||||
"pages alloc in dma32".to_string(),
|
||||
ProcData::get_one(&proc_data.vmstat, "pgalloc_dma32"),
|
||||
),
|
||||
(
|
||||
"pages alloc in high".to_string(),
|
||||
ProcData::get_one(&proc_data.vmstat, "pgalloc_high"),
|
||||
),
|
||||
(
|
||||
"pages alloc in movable".to_string(),
|
||||
ProcData::get_one(&proc_data.vmstat, "pgalloc_movable"),
|
||||
),
|
||||
(
|
||||
"pages alloc in normal".to_string(),
|
||||
ProcData::get_one(&proc_data.vmstat, "pgalloc_normal"),
|
||||
),
|
||||
(
|
||||
"pages free".to_string(),
|
||||
ProcData::get_one(&proc_data.vmstat, "pgfree"),
|
||||
),
|
||||
(
|
||||
"interrupts".to_string(),
|
||||
proc_data
|
||||
.stat
|
||||
.get("intr")
|
||||
.unwrap()
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.unwrap()
|
||||
.parse::<u64>()
|
||||
.unwrap(),
|
||||
),
|
||||
(
|
||||
"CPU context switches".to_string(),
|
||||
ProcData::get_one(&proc_data.stat, "ctxt"),
|
||||
),
|
||||
(
|
||||
"boot time".to_string(),
|
||||
ProcData::get_one(&proc_data.stat, "btime"),
|
||||
),
|
||||
(
|
||||
"forks".to_string(),
|
||||
ProcData::get_one(&proc_data.stat, "processes"),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn with_unit(x: u64, arg: &ArgMatches) -> u64 {
|
||||
if let Some(unit) = arg.get_one::<String>("unit") {
|
||||
|
||||
+24
-10
@@ -7,7 +7,7 @@ mod parser;
|
||||
mod picker;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::picker::{get_pickers, Picker};
|
||||
use crate::picker::{get_pickers, get_stats, Picker};
|
||||
use clap::value_parser;
|
||||
#[allow(unused_imports)]
|
||||
use clap::{arg, crate_version, ArgMatches, Command};
|
||||
@@ -26,14 +26,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// validate unit
|
||||
if let Some(unit) = matches.get_one::<String>("unit") {
|
||||
if !["k", "K", "m", "M"].contains(&unit.as_str()) {
|
||||
Err(USimpleError::new(
|
||||
1,
|
||||
"-S requires k, K, m or M (default is KiB)",
|
||||
))?;
|
||||
}
|
||||
if matches.get_flag("stats") {
|
||||
return print_stats();
|
||||
}
|
||||
|
||||
let one_header = matches.get_flag("one-header");
|
||||
@@ -46,6 +40,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
return print_slabs(one_header, term_height);
|
||||
}
|
||||
|
||||
// validate unit
|
||||
if let Some(unit) = matches.get_one::<String>("unit") {
|
||||
if !["k", "K", "m", "M"].contains(&unit.as_str()) {
|
||||
Err(USimpleError::new(
|
||||
1,
|
||||
"-S requires k, K, m or M (default is KiB)",
|
||||
))?;
|
||||
}
|
||||
}
|
||||
|
||||
let delay = matches.get_one::<u64>("delay");
|
||||
let count = matches.get_one::<u64>("count");
|
||||
let mut count = count.copied().map(|c| if c == 0 { 1 } else { c });
|
||||
@@ -79,6 +83,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn print_stats() -> UResult<()> {
|
||||
let data = get_stats();
|
||||
|
||||
data.iter()
|
||||
.for_each(|(name, value)| println!("{value:>13} {name}"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn print_slabs(one_header: bool, term_height: u16) -> UResult<()> {
|
||||
let mut slab_data = uu_slabtop::SlabInfo::new()?.data;
|
||||
@@ -166,7 +180,7 @@ pub fn uu_app() -> Command {
|
||||
// arg!(-f --forks "switch displays the number of forks since boot"),
|
||||
arg!(-m --slabs "Display slabinfo"),
|
||||
arg!(-n --"one-header" "Display the header only once rather than periodically"),
|
||||
// arg!(-s --stats "Displays a table of various event counters and memory statistics"),
|
||||
arg!(-s --stats "Displays a table of various event counters and memory statistics"),
|
||||
// arg!(-d --disk "Report disk statistics"),
|
||||
// arg!(-D --"disk-sum" "Report some summary statistics about disk activity"),
|
||||
// arg!(-p --partition <device> "Detailed statistics about partition"),
|
||||
|
||||
Reference in New Issue
Block a user