From 89c55bbbe3b87eb278e6056eda24c996c182def5 Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Sat, 28 Jun 2025 16:58:26 +0800 Subject: [PATCH 01/40] w: truncate long username --- src/uu/w/src/w.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/uu/w/src/w.rs b/src/uu/w/src/w.rs index 8b13e35..1d4b9af 100644 --- a/src/uu/w/src/w.rs +++ b/src/uu/w/src/w.rs @@ -255,6 +255,10 @@ fn fetch_user_info() -> Result, std::io::Error> { Ok(Vec::new()) } +fn truncate_username(user: &str) -> String { + user.chars().take(8).collect::() +} + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args)?; @@ -281,7 +285,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { if short { println!( "{:<9}{:<9}{:<7}{:<}", - user.user, + truncate_username(&user.user), user.terminal, format_time_elapsed(user.idle_time, old_style).unwrap_or_default(), user.command @@ -289,7 +293,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } else { println!( "{:<9}{:<10}{:<9}{:<6} {:<7}{:<6}{:<}", - user.user, + truncate_username(&user.user), user.terminal, user.login_time, format_time_elapsed(user.idle_time, old_style).unwrap_or_default(), From e6261faad16489dc2d3713c8e41a716d56172def Mon Sep 17 00:00:00 2001 From: estodi <83288835+estodi@users.noreply.github.com> Date: Thu, 3 Jul 2025 07:08:41 -0700 Subject: [PATCH 02/40] pmap: implemented rc options (#456) * pmap: implemented rc options * pmap: added tests for rc options * fixed lint errors * pmap: updated test_default_rc to run only in CI * Update src/uu/pmap/src/pmap.rs Co-authored-by: Daniel Hofstetter * Update src/uu/pmap/src/pmap.rs Co-authored-by: Daniel Hofstetter --------- Co-authored-by: Daniel Hofstetter --- Cargo.toml | 1 + src/uu/pmap/Cargo.toml | 1 + src/uu/pmap/src/pmap.rs | 116 +++++++++++++++++++++++++++++---- src/uu/pmap/src/pmap_config.rs | 95 +++++++++++++++++++++++++++ tests/by-util/test_pmap.rs | 72 ++++++++++++++++++++ 5 files changed, 274 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 293b924..fe4a31d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ clap_complete = "4.5.2" clap_mangen = "0.2.20" crossterm = "0.29.0" ctor = "0.4.1" +dirs = "6.0.0" libc = "0.2.154" nix = { version = "0.30", default-features = false, features = ["process"] } phf = "0.12.1" diff --git a/src/uu/pmap/Cargo.toml b/src/uu/pmap/Cargo.toml index dd17918..60a42ab 100644 --- a/src/uu/pmap/Cargo.toml +++ b/src/uu/pmap/Cargo.toml @@ -13,6 +13,7 @@ version.workspace = true [dependencies] uucore = { workspace = true } clap = { workspace = true } +dirs = { workspace = true } [lib] path = "src/pmap.rs" diff --git a/src/uu/pmap/src/pmap.rs b/src/uu/pmap/src/pmap.rs index 34073b4..bc05802 100644 --- a/src/uu/pmap/src/pmap.rs +++ b/src/uu/pmap/src/pmap.rs @@ -5,7 +5,7 @@ use clap::{crate_version, Arg, ArgAction, Command}; use maps_format_parser::{parse_map_line, MapLine}; -use pmap_config::{pmap_field_name, PmapConfig}; +use pmap_config::{create_rc, pmap_field_name, PmapConfig}; use smaps_format_parser::{parse_smaps, SmapTable}; use std::env; use std::fs; @@ -39,12 +39,61 @@ mod options { pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args)?; + if matches.get_flag(options::CREATE_RC) { + let path = pmap_config::get_rc_default_path(); + if std::fs::exists(&path)? { + eprintln!("pmap: the file already exists - delete or rename it first"); + eprintln!( + "pmap: couldn't create {}", + pmap_config::get_rc_default_path_str() + ); + set_exit_code(1); + } else { + create_rc(&path)?; + eprintln!( + "pmap: {} file successfully created, feel free to edit the content", + pmap_config::get_rc_default_path_str() + ); + } + return Ok(()); + } else if let Some(path_str) = matches.get_one::(options::CREATE_RC_TO) { + let path = std::path::PathBuf::from(path_str); + if std::fs::exists(&path)? { + eprintln!("pmap: the file already exists - delete or rename it first"); + eprintln!("pmap: couldn't create the rc file"); + set_exit_code(1); + } else { + create_rc(&path)?; + eprintln!("pmap: rc file successfully created, feel free to edit the content"); + } + return Ok(()); + } + let mut pmap_config = PmapConfig::default(); if matches.get_flag(options::MORE_EXTENDED) { pmap_config.set_more_extended(); } else if matches.get_flag(options::MOST_EXTENDED) { pmap_config.set_most_extended(); + } else if matches.get_flag(options::READ_RC) { + let path = pmap_config::get_rc_default_path(); + if !std::fs::exists(&path)? { + eprintln!( + "pmap: couldn't read {}", + pmap_config::get_rc_default_path_str() + ); + set_exit_code(1); + return Ok(()); + } + pmap_config.read_rc(&path)?; + } else if let Some(path) = matches.get_one::(options::READ_RC_FROM) { + let path = std::fs::canonicalize(path)?; + if !std::fs::exists(&path)? { + eprintln!("pmap: couldn't read the rc file"); + set_exit_code(1); + return Ok(()); + } + pmap_config.read_rc(&path)?; } // Options independent with field selection: @@ -405,36 +454,81 @@ pub fn uu_app() -> Command { .short('c') .long("read-rc") .help("read the default rc") - .action(ArgAction::SetTrue), - ) + .action(ArgAction::SetTrue) + .conflicts_with_all([ + "read-rc-from", + "device", + "create-rc", + "create-rc-to", + "extended", + "more-extended", + "most-extended", + ]), + ) // pmap: options -c, -C, -d, -n, -N, -x, -X are mutually exclusive .arg( Arg::new(options::READ_RC_FROM) .short('C') .long("read-rc-from") .num_args(1) - .help("read the rc from file"), - ) + .help("read the rc from file") + .conflicts_with_all([ + "read-rc", + "device", + "create-rc", + "create-rc-to", + "extended", + "more-extended", + "most-extended", + ]), + ) // pmap: options -c, -C, -d, -n, -N, -x, -X are mutually exclusive .arg( Arg::new(options::CREATE_RC) .short('n') .long("create-rc") .help("create new default rc") - .action(ArgAction::SetTrue), - ) + .action(ArgAction::SetTrue) + .conflicts_with_all([ + "read-rc", + "read-rc-from", + "device", + "create-rc-to", + "extended", + "more-extended", + "most-extended", + ]), + ) // pmap: options -c, -C, -d, -n, -N, -x, -X are mutually exclusive .arg( Arg::new(options::CREATE_RC_TO) .short('N') .long("create-rc-to") .num_args(1) - .help("create new rc to file"), - ) + .help("create new rc to file") + .conflicts_with_all([ + "read-rc", + "read-rc-from", + "device", + "create-rc", + "extended", + "more-extended", + "most-extended", + ]), + ) // pmap: options -c, -C, -d, -n, -N, -x, -X are mutually exclusive .arg( Arg::new(options::DEVICE) .short('d') .long("device") .help("show the device format") - .action(ArgAction::SetTrue), - ) + .action(ArgAction::SetTrue) + .conflicts_with_all([ + "read-rc", + "read-rc-from", + "create-rc", + "create-rc-to", + "extended", + "more-extended", + "most-extended", + ]), + ) // pmap: options -c, -C, -d, -n, -N, -x, -X are mutually exclusive .arg( Arg::new(options::QUIET) .short('q') diff --git a/src/uu/pmap/src/pmap_config.rs b/src/uu/pmap/src/pmap_config.rs index 5f544c8..073aa6c 100644 --- a/src/uu/pmap/src/pmap_config.rs +++ b/src/uu/pmap/src/pmap_config.rs @@ -3,6 +3,10 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +use dirs::home_dir; +use std::io::Error; +use std::path::PathBuf; + pub mod pmap_field_name { pub const ADDRESS: &str = "Address"; pub const PERM: &str = "Perm"; @@ -200,6 +204,10 @@ impl PmapConfig { } } + pub fn enable_field(&mut self, field_name: &str) { + self.set_field(field_name, true); + } + pub fn disable_field(&mut self, field_name: &str) { self.set_field(field_name, false); } @@ -244,4 +252,91 @@ impl PmapConfig { self.anon_huge_pages = true; self.vmflags = true; } + + pub fn read_rc(&mut self, path: &PathBuf) -> Result<(), Error> { + self.custom_format_enabled = true; + + let contents = std::fs::read_to_string(path)?; + + let mut in_field_display = false; + let mut in_mapping = false; + + for line in contents.lines() { + let line = line.trim_ascii(); + if line.starts_with("#") || line.is_empty() { + continue; + } + + // The leftmost category on the line is recoginized. + if line.starts_with("[Fields Display]") { + in_field_display = true; + in_mapping = false; + continue; + } else if line.starts_with("[Mapping]") { + in_field_display = false; + in_mapping = true; + continue; + } + + if in_field_display { + self.enable_field(line); + } else if in_mapping && line == "ShowPath" { + self.show_path = true; + } + } + + Ok(()) + } +} + +pub fn create_rc(path: &PathBuf) -> Result<(), Error> { + let contents = "# pmap's Config File\n".to_string() + + "\n" + + "# All the entries are case sensitive.\n" + + "# Unsupported entries are ignored!\n" + + "\n" + + "[Fields Display]\n" + + "\n" + + "# To enable a field uncomment its entry\n" + + "\n" + + "#Perm\n" + + "#Offset\n" + + "#Device\n" + + "#Inode\n" + + "#Size\n" + + "#Rss\n" + + "#Pss\n" + + "#Shared_Clean\n" + + "#Shared_Dirty\n" + + "#Private_Clean\n" + + "#Private_Dirty\n" + + "#Referenced\n" + + "#Anonymous\n" + + "#AnonHugePages\n" + + "#Swap\n" + + "#KernelPageSize\n" + + "#MMUPageSize\n" + + "#Locked\n" + + "#VmFlags\n" + + "#Mapping\n" + + "\n" + + "[Mapping]\n" + + "\n" + + "# to show paths in the mapping column uncomment the following line\n" + + "#ShowPath\n" + + "\n"; + + std::fs::write(path, contents)?; + + Ok(()) +} + +pub fn get_rc_default_path() -> PathBuf { + let mut path = home_dir().expect("home directory should not be None"); + path.push(".pmaprc"); + path +} + +pub fn get_rc_default_path_str() -> &'static str { + "~/.pmaprc" } diff --git a/tests/by-util/test_pmap.rs b/tests/by-util/test_pmap.rs index fab26be..e289f06 100644 --- a/tests/by-util/test_pmap.rs +++ b/tests/by-util/test_pmap.rs @@ -20,6 +20,78 @@ fn test_no_args() { new_ucmd!().fails().code_is(1); } +#[test] +#[cfg(target_os = "linux")] +fn test_default_rc() { + if !uutests::util::is_ci() { + return; + } + + let pid = process::id(); + let ts = TestScenario::new(util_name!()); + + // Fails to read before creating rc file + for arg in ["-c", "--read-rc"] { + ts.ucmd().arg(arg).arg(pid.to_string()).fails().code_is(1); + } + + // Create rc file + ts.ucmd().arg("-n").succeeds(); + + // Fails to create because rc file already exists + for arg in ["-n", "--create-rc"] { + ts.ucmd().arg(arg).fails().code_is(1); + } + + // Succeeds to read now + for arg in ["-c", "--read-rc"] { + ts.ucmd().arg(arg).arg(pid.to_string()).succeeds(); + } +} + +#[test] +#[cfg(target_os = "linux")] +fn test_create_rc_to() { + let ts = TestScenario::new(util_name!()); + + ts.ucmd().args(&["-N", "pmap_rc_file_name"]).succeeds(); + + // Fails to create because rc file already exists + for arg in ["-N", "--create-rc-to"] { + ts.ucmd() + .args(&[arg, "pmap_rc_file_name"]) + .fails() + .code_is(1); + } +} + +#[test] +#[cfg(target_os = "linux")] +fn test_read_rc_from() { + let pid = process::id(); + let ts = TestScenario::new(util_name!()); + + // Fails to read before creating rc file + for arg in ["-C", "--read-rc-from"] { + ts.ucmd() + .args(&[arg, "pmap_rc_file_name"]) + .arg(pid.to_string()) + .fails() + .code_is(1); + } + + // Create rc file + ts.ucmd().args(&["-N", "pmap_rc_file_name"]).succeeds(); + + // Succeeds to read now + for arg in ["-C", "--read-rc-from"] { + ts.ucmd() + .args(&[arg, "pmap_rc_file_name"]) + .arg(pid.to_string()) + .succeeds(); + } +} + #[test] #[cfg(target_os = "linux")] fn test_existing_pid() { From 8f14e7b03e07aaaef8d1ebd8bb9e36f911433021 Mon Sep 17 00:00:00 2001 From: estodi <83288835+estodi@users.noreply.github.com> Date: Sun, 6 Jul 2025 23:55:57 +0900 Subject: [PATCH 03/40] pmap: fixed padding of Address, Offset, and Device fields (#458) * pmap: fixed padding of Address, Offset, and Device fields * pmap: fixed tests for more and most extended formats --- src/uu/pmap/src/maps_format_parser.rs | 129 ++++++++++++++++++++----- src/uu/pmap/src/pmap.rs | 10 +- src/uu/pmap/src/smaps_format_parser.rs | 64 ++++++++---- tests/by-util/test_pmap.rs | 40 ++++---- 4 files changed, 172 insertions(+), 71 deletions(-) diff --git a/src/uu/pmap/src/maps_format_parser.rs b/src/uu/pmap/src/maps_format_parser.rs index 6516363..0e07760 100644 --- a/src/uu/pmap/src/maps_format_parser.rs +++ b/src/uu/pmap/src/maps_format_parser.rs @@ -10,15 +10,37 @@ use std::io::{Error, ErrorKind}; // Represents a parsed single line from /proc//maps. #[derive(Debug, Clone, Default, PartialEq)] pub struct MapLine { - pub address: String, + pub address: Address, pub size_in_kb: u64, pub perms: Perms, pub offset: String, - pub device: String, + pub device: Device, pub inode: u64, pub mapping: String, } +#[derive(Debug, Clone, Default, PartialEq)] +pub struct Address { + pub start: String, + pub low: u64, + pub high: u64, +} + +impl fmt::Display for Address { + // By default, pads with white spaces. + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{: >16}", self.start) + } +} + +impl Address { + // Format for default, extended option, and device option. + // Pads the start address with zero. + pub fn zero_pad(&self) -> String { + format!("{:0>16}", self.start) + } +} + // Represents a set of permissions from the "perms" column of /proc//maps. #[derive(Clone, Copy, Debug, Default, PartialEq)] pub struct Perms { @@ -69,6 +91,28 @@ impl Perms { } } +#[derive(Debug, Clone, Default, PartialEq)] +pub struct Device { + pub major: String, + pub minor: String, + pub width: usize, +} + +impl fmt::Display for Device { + // By default, does not pad. + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}:{}", self.major, self.minor) + } +} + +impl Device { + // Format for device option. + // Pads the device info from /proc//maps with zeros and turns AB:CD into 0AB:000CD. + pub fn device(&self) -> String { + format!("{:0>3}:{:0>5}", self.major, self.minor) + } +} + // Parses a single line from /proc//maps. See // https://www.kernel.org/doc/html/latest/filesystems/proc.html for details about the expected // format. @@ -90,7 +134,7 @@ pub fn parse_map_line(line: &str) -> Result { let (offset, rest) = rest .split_once(' ') .ok_or_else(|| Error::from(ErrorKind::InvalidData))?; - let offset = format!("{offset:0>16}"); + let offset = format!("{offset:0>8}"); let (device, rest) = rest .split_once(' ') @@ -116,9 +160,8 @@ pub fn parse_map_line(line: &str) -> Result { }) } -// Returns the start address and the size of the provided memory range. The start address is always -// 16-digits and padded with 0, if necessary. The size is in KB. -fn parse_address(memory_range: &str) -> Result<(String, u64), Error> { +// Returns Address instance and the size of the provided memory range. The size is in KB. +fn parse_address(memory_range: &str) -> Result<(Address, u64), Error> { let (start, end) = memory_range .split_once('-') .ok_or_else(|| Error::from(ErrorKind::InvalidData))?; @@ -127,15 +170,26 @@ fn parse_address(memory_range: &str) -> Result<(String, u64), Error> { let high = u64::from_str_radix(end, 16).map_err(|_| Error::from(ErrorKind::InvalidData))?; let size_in_kb = (high - low) / 1024; - Ok((format!("{start:0>16}"), size_in_kb)) + Ok(( + Address { + start: start.to_string(), + low, + high, + }, + size_in_kb, + )) } -// Pads the device info from /proc//maps with zeros and turns AB:CD into 0AB:000CD. -fn parse_device(device: &str) -> Result { +// Returns Device instance. +fn parse_device(device: &str) -> Result { let (major, minor) = device .split_once(':') .ok_or_else(|| Error::from(ErrorKind::InvalidData))?; - Ok(format!("{major:0>3}:{minor:0>5}")) + Ok(Device { + major: major.to_string(), + minor: minor.to_string(), + width: device.len(), + }) } impl MapLine { @@ -174,19 +228,31 @@ mod test { fn create_map_line( address: &str, + low: u64, + high: u64, size_in_kb: u64, perms: Perms, offset: &str, - device: &str, + major: &str, + minor: &str, + width: usize, inode: u64, mapping: &str, ) -> MapLine { MapLine { - address: address.to_string(), + address: Address { + start: address.to_string(), + low, + high, + }, size_in_kb, perms, offset: offset.to_string(), - device: device.to_string(), + device: Device { + major: major.to_string(), + minor: minor.to_string(), + width, + }, inode, mapping: mapping.to_string(), } @@ -210,31 +276,31 @@ mod test { fn test_parse_map_line() { let data = [ ( - create_map_line("000062442eb9e000", 16, Perms::from("r--p"), "0000000000000000", "008:00008", 10813151, "/usr/bin/konsole"), + create_map_line("62442eb9e000", 0x62442eb9e000, 0x62442eba2000, 16, Perms::from("r--p"), "00000000", "08", "08", 5, 10813151, "/usr/bin/konsole"), "62442eb9e000-62442eba2000 r--p 00000000 08:08 10813151 /usr/bin/konsole" ), ( - create_map_line("000071af50000000", 132, Perms::from("rw-p"), "0000000000000000", "000:00000", 0, ""), + create_map_line("71af50000000", 0x71af50000000, 0x71af50021000, 132, Perms::from("rw-p"), "00000000", "00", "00", 5, 0, ""), "71af50000000-71af50021000 rw-p 00000000 00:00 0 " ), ( - create_map_line("00007ffc3f8df000", 132, Perms::from("rw-p"), "0000000000000000", "000:00000", 0, "[stack]"), + create_map_line("7ffc3f8df000", 0x7ffc3f8df000, 0x7ffc3f900000, 132, Perms::from("rw-p"), "00000000", "00", "00", 5, 0, "[stack]"), "7ffc3f8df000-7ffc3f900000 rw-p 00000000 00:00 0 [stack]" ), ( - create_map_line("000071af8c9e6000", 16, Perms::from("rw-s"), "0000000105830000", "000:00010", 1075, "anon_inode:i915.gem"), + create_map_line("71af8c9e6000", 0x71af8c9e6000, 0x71af8c9ea000, 16, Perms::from("rw-s"), "105830000", "00", "10", 5, 1075, "anon_inode:i915.gem"), "71af8c9e6000-71af8c9ea000 rw-s 105830000 00:10 1075 anon_inode:i915.gem" ), ( - create_map_line("000071af6cf0c000", 3560, Perms::from("rw-s"), "0000000000000000", "000:00001", 256481, "/memfd:wayland-shm (deleted)"), + create_map_line("71af6cf0c000", 0x71af6cf0c000, 0x71af6d286000, 3560, Perms::from("rw-s"), "00000000", "00", "01", 5, 256481, "/memfd:wayland-shm (deleted)"), "71af6cf0c000-71af6d286000 rw-s 00000000 00:01 256481 /memfd:wayland-shm (deleted)" ), ( - create_map_line("ffffffffff600000", 4, Perms::from("--xp"), "0000000000000000", "000:00000", 0, "[vsyscall]"), + create_map_line("ffffffffff600000", 0xffffffffff600000, 0xffffffffff601000, 4, Perms::from("--xp"), "00000000", "00", "00", 5, 0, "[vsyscall]"), "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]" ), ( - create_map_line("00005e8187da8000", 24, Perms::from("r--p"), "0000000000000000", "008:00008", 9524160, "/usr/bin/hello world"), + create_map_line("5e8187da8000", 0x5e8187da8000, 0x5e8187dae000, 24, Perms::from("r--p"), "00000000", "08", "08", 5, 9524160, "/usr/bin/hello world"), "5e8187da8000-5e8187dae000 r--p 00000000 08:08 9524160 /usr/bin/hello world" ), ]; @@ -251,12 +317,16 @@ mod test { #[test] fn test_parse_address() { - let (start, size) = parse_address("ffffffffff600000-ffffffffff601000").unwrap(); - assert_eq!(start, "ffffffffff600000"); + let (address, size) = parse_address("ffffffffff600000-ffffffffff601000").unwrap(); + assert_eq!(address.start, "ffffffffff600000"); + assert_eq!(address.low, 0xffffffffff600000); + assert_eq!(address.high, 0xffffffffff601000); assert_eq!(size, 4); - let (start, size) = parse_address("7ffc4f0c2000-7ffc4f0e3000").unwrap(); - assert_eq!(start, "00007ffc4f0c2000"); + let (address, size) = parse_address("7ffc4f0c2000-7ffc4f0e3000").unwrap(); + assert_eq!(address.start, "7ffc4f0c2000"); + assert_eq!(address.low, 0x7ffc4f0c2000); + assert_eq!(address.high, 0x7ffc4f0e3000); assert_eq!(size, 132); } @@ -273,8 +343,15 @@ mod test { #[test] fn test_parse_device() { - assert_eq!("012:00034", parse_device("12:34").unwrap()); - assert_eq!("000:00000", parse_device("00:00").unwrap()); + assert_eq!("12:34", parse_device("12:34").unwrap().to_string()); + assert_eq!("00:00", parse_device("00:00").unwrap().to_string()); + assert_eq!("fe:01", parse_device("fe:01").unwrap().to_string()); + assert_eq!("103:100", parse_device("103:100").unwrap().to_string()); + + assert_eq!("012:00034", parse_device("12:34").unwrap().device()); + assert_eq!("000:00000", parse_device("00:00").unwrap().device()); + assert_eq!("0fe:00001", parse_device("fe:01").unwrap().device()); + assert_eq!("103:00100", parse_device("103:100").unwrap().device()); } #[test] diff --git a/src/uu/pmap/src/pmap.rs b/src/uu/pmap/src/pmap.rs index bc05802..aa5bb94 100644 --- a/src/uu/pmap/src/pmap.rs +++ b/src/uu/pmap/src/pmap.rs @@ -184,7 +184,7 @@ fn output_default_format(pid: &str, pmap_config: &PmapConfig) -> Result<(), Erro process_maps(pid, None, |map_line| { println!( "{} {:>6}K {} {}", - map_line.address, + map_line.address.zero_pad(), map_line.size_in_kb, map_line.perms.mode(), map_line.parse_mapping(pmap_config) @@ -209,7 +209,7 @@ fn output_extended_format(pid: &str, pmap_config: &PmapConfig) -> Result<(), Err for smap_entry in smap_table.entries { println!( "{} {:>7} {:>7} {:>7} {} {}", - smap_entry.map_line.address, + smap_entry.map_line.address.zero_pad(), smap_entry.map_line.size_in_kb, smap_entry.rss_in_kb, smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb, @@ -361,12 +361,12 @@ fn output_device_format(pid: &str, pmap_config: &PmapConfig) -> Result<(), Error }, |map_line| { println!( - "{} {:>7} {} {} {} {}", - map_line.address, + "{} {:>7} {} {:0>16} {} {}", + map_line.address.zero_pad(), map_line.size_in_kb, map_line.perms.mode(), map_line.offset, - map_line.device, + map_line.device.device(), map_line.parse_mapping(pmap_config) ); total_mapped += map_line.size_in_kb; diff --git a/src/uu/pmap/src/smaps_format_parser.rs b/src/uu/pmap/src/smaps_format_parser.rs index 779a2b8..f478916 100644 --- a/src/uu/pmap/src/smaps_format_parser.rs +++ b/src/uu/pmap/src/smaps_format_parser.rs @@ -40,10 +40,10 @@ pub struct SmapEntry { impl SmapEntry { pub fn get_field(&self, field_name: &str) -> String { match field_name { - pmap_field_name::ADDRESS => self.map_line.address.clone(), + pmap_field_name::ADDRESS => self.map_line.address.to_string(), pmap_field_name::PERM => self.map_line.perms.to_string(), pmap_field_name::OFFSET => self.map_line.offset.clone(), - pmap_field_name::DEVICE => self.map_line.device.clone(), + pmap_field_name::DEVICE => self.map_line.device.to_string(), pmap_field_name::INODE => self.map_line.inode.to_string(), pmap_field_name::SIZE => self.map_line.size_in_kb.to_string(), pmap_field_name::KERNEL_PAGE_SIZE => self.kernel_page_size_in_kb.to_string(), @@ -107,6 +107,9 @@ pub struct SmapTableInfo { pub total_thp_eligible: u64, pub total_protection_key: u64, // Width + pub offset_width: usize, + pub device_width: usize, + pub inode_width: usize, pub size_in_kb_width: usize, pub kernel_page_size_in_kb_width: usize, pub mmu_page_size_in_kb_width: usize, @@ -165,6 +168,9 @@ impl Default for SmapTableInfo { total_thp_eligible: 0, total_protection_key: 0, + device_width: pmap_field_name::DEVICE.len(), + offset_width: pmap_field_name::OFFSET.len(), + inode_width: pmap_field_name::INODE.len(), size_in_kb_width: pmap_field_name::SIZE.len(), kernel_page_size_in_kb_width: pmap_field_name::KERNEL_PAGE_SIZE.len(), mmu_page_size_in_kb_width: pmap_field_name::MMU_PAGE_SIZE.len(), @@ -200,9 +206,9 @@ impl SmapTableInfo { match field_name { pmap_field_name::ADDRESS => 16, // See maps_format_parser.rs pmap_field_name::PERM => 4, // See maps_format_parser.rs - pmap_field_name::OFFSET => 16, // See maps_format_parser.rs - pmap_field_name::DEVICE => 9, // See maps_format_parser.rs - pmap_field_name::INODE => 10, // See maps_format_parser.rs + pmap_field_name::OFFSET => self.offset_width, + pmap_field_name::DEVICE => self.device_width, + pmap_field_name::INODE => self.inode_width, pmap_field_name::SIZE => self.size_in_kb_width, pmap_field_name::KERNEL_PAGE_SIZE => self.kernel_page_size_in_kb_width, pmap_field_name::MMU_PAGE_SIZE => self.mmu_page_size_in_kb_width, @@ -289,6 +295,12 @@ pub fn parse_smaps(contents: &str) -> Result { smap_entry = SmapEntry::default(); } smap_table.info.total_size_in_kb += map_line.size_in_kb; + smap_table.info.offset_width = smap_table.info.offset_width.max(map_line.offset.len()); + smap_table.info.device_width = smap_table.info.device_width.max(map_line.device.width); + smap_table.info.inode_width = smap_table + .info + .inode_width + .max(map_line.inode.to_string().len()); smap_entry.map_line = map_line; } else { let (key, val) = line @@ -544,9 +556,13 @@ mod test { #[allow(clippy::too_many_arguments)] fn create_smap_entry( address: &str, + low: u64, + high: u64, perms: Perms, offset: &str, - device: &str, + major: &str, + minor: &str, + width: usize, inode: u64, mapping: &str, size_in_kb: u64, @@ -577,11 +593,19 @@ mod test { ) -> SmapEntry { SmapEntry { map_line: MapLine { - address: address.to_string(), + address: crate::maps_format_parser::Address { + start: address.to_string(), + low, + high, + }, size_in_kb, perms, offset: offset.to_string(), - device: device.to_string(), + device: crate::maps_format_parser::Device { + major: major.to_string(), + minor: minor.to_string(), + width, + }, inode, mapping: mapping.to_string(), }, @@ -617,7 +641,7 @@ mod test { let data = [ ( vec![create_smap_entry( - "0000560880413000", Perms::from("r--p"), "0000000000000000", "008:00008", 10813151, "/usr/bin/konsole", + "560880413000", 0x560880413000, 0x560880440000, Perms::from("r--p"), "00000000", "08", "08", 5, 10813151, "/usr/bin/konsole", 180, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, "rd mr mw me dw sd")], concat!( @@ -651,7 +675,7 @@ mod test { ), ( vec![create_smap_entry( - "000071af50000000", Perms::from("rw-p"), "0000000000000000", "000:00000", 0, "", + "71af50000000", 0x71af50000000, 0x71af50021000, Perms::from("rw-p"), "00000000", "00", "00", 5, 0, "", 132, 4, 4, 128, 9, 9, 128, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, "rd mr mw me sd")], concat!( @@ -684,7 +708,7 @@ mod test { ), ( vec![create_smap_entry( - "00007ffc3f8df000", Perms::from("rw-p"), "0000000000000000", "000:00000", 0, "[stack]", + "7ffc3f8df000", 0x7ffc3f8df000, 0x7ffc3f900000, Perms::from("rw-p"), "00000000", "00", "00", 5, 0, "[stack]", 132, 4, 4, 108, 108, 108, 0, 0, 0, 108, 108, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, "rd wr mr mw me gd ac")], concat!( @@ -717,7 +741,7 @@ mod test { ), ( vec![create_smap_entry( - "000071af8c9e6000", Perms::from("rw-s"), "0000000105830000", "000:00010", 1075, "anon_inode:i915.gem", + "71af8c9e6000", 0x71af8c9e6000, 0x71af8c9ea000, Perms::from("rw-s"), "105830000", "00", "10", 5, 1075, "anon_inode:i915.gem", 16, 4, 4, 16, 16, 16, 0, 0, 0, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rd wr mr mw me ac sd")], concat!( @@ -749,7 +773,7 @@ mod test { ), ( vec![create_smap_entry( - "000071af6cf0c000", Perms::from("rw-s"), "0000000000000000", "000:00001", 256481, "/memfd:wayland-shm (deleted)", + "71af6cf0c000", 0x71af6cf0c000, 0x71af6d286000, Perms::from("rw-s"), "00000000", "00", "01", 5, 256481, "/memfd:wayland-shm (deleted)", 3560, 4, 4, 532, 108, 0, 524, 0, 8, 0, 532, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rd mr mw me sd")], concat!( @@ -781,7 +805,7 @@ mod test { ), ( vec![create_smap_entry( - "ffffffffff600000", Perms::from("--xp"), "0000000000000000", "000:00000", 0, "[vsyscall]", + "ffffffffff600000", 0xffffffffff600000, 0xffffffffff601000, Perms::from("--xp"), "00000000", "00", "00", 5, 0, "[vsyscall]", 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rd wr mr mw me ac sd")], concat!( @@ -813,7 +837,7 @@ mod test { ), ( vec![create_smap_entry( - "00005e8187da8000", Perms::from("r--p"), "0000000000000000", "008:00008", 9524160, "/usr/bin/hello world", + "5e8187da8000", 0x5e8187da8000, 0x5e8187dae000, Perms::from("r--p"), "00000000", "08", "08", 5, 9524160, "/usr/bin/hello world", 24, 4, 4, 24, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rd ex mr mw me sd")], concat!( @@ -846,11 +870,11 @@ mod test { ( vec![ create_smap_entry( - "000071af8c9e6000", Perms::from("rw-s"), "0000000105830000", "000:00010", 1075, "anon_inode:i915.gem", + "71af8c9e6000", 0x71af8c9e6000, 0x71af8c9ea000, Perms::from("rw-s"), "105830000", "00", "10", 5, 1075, "anon_inode:i915.gem", 16, 4, 4, 16, 16, 16, 0, 0, 0, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rd wr mr mw me ac sd"), create_smap_entry( - "000071af6cf0c000", Perms::from("rw-s"), "0000000000000000", "000:00001", 256481, "/memfd:wayland-shm (deleted)", + "71af6cf0c000", 0x71af6cf0c000, 0x71af6d286000, Perms::from("rw-s"), "00000000", "00", "01", 5, 256481, "/memfd:wayland-shm (deleted)", 3560, 4, 4, 532, 108, 0, 524, 0, 8, 0, 532, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rd mr mw me sd"), ], @@ -909,15 +933,15 @@ mod test { ( vec![ create_smap_entry( - "000071af8c9e6000", Perms::from("rw-s"), "0000000105830000", "000:00010", 1075, "anon_inode:i915.gem", + "71af8c9e6000", 0x71af8c9e6000, 0x71af8c9ea000, Perms::from("rw-s"), "105830000", "00", "10", 5, 1075, "anon_inode:i915.gem", 16, 4, 4, 16, 16, 16, 0, 0, 0, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, "rd wr mr mw me ac sd"), create_smap_entry( - "0000560880413000", Perms::from("r--p"), "0000000000000000", "008:00008", 10813151, "/usr/bin/konsole", + "560880413000", 0x560880413000, 0x560880440000, Perms::from("r--p"), "00000000", "08", "08", 5, 10813151, "/usr/bin/konsole", 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ""), create_smap_entry( - "000071af6cf0c000", Perms::from("rw-s"), "0000000000000000", "000:00001", 256481, "/memfd:wayland-shm (deleted)", + "71af6cf0c000", 0x71af6cf0c000, 0x71af6d286000, Perms::from("rw-s"), "00000000", "00", "01", 5, 256481, "/memfd:wayland-shm (deleted)", 3560, 4, 4, 532, 108, 0, 524, 0, 8, 0, 532, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rd mr mw me sd"), ], diff --git a/tests/by-util/test_pmap.rs b/tests/by-util/test_pmap.rs index e289f06..e4b9f8c 100644 --- a/tests/by-util/test_pmap.rs +++ b/tests/by-util/test_pmap.rs @@ -450,13 +450,13 @@ fn assert_extended_format(pid: u32, s: &str, quiet: bool, show_path: bool) { // Ensure `s` has the following more extended format (-X): // // 1234: /some/path -// Address Perm Offset Device Inode Size Rss Pss Pss_Dirty Referenced Anonymous LazyFree ShmemPmdMapped FilePmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked THPeligible Mapping -// 000073eb5f4c7000 r-xp 0000000000036000 008:00008 2274176 1284 1148 1148 0 1148 0 0 0 0 0 0 0 0 0 0 ld-linux-x86-64.so.2 -// 00007ffd588fc000 r--p 0000000000000000 000:00000 2274176 20 20 20 20 20 20 0 0 0 0 0 0 0 0 0 [stack] -// ffffffffff600000 rw-p 0000000000000000 000:00000 2274176 36 36 36 36 36 36 0 0 0 0 0 0 0 0 0 (one intentional trailing space) +// Address Perm Offset Device Inode Size Rss Pss Pss_Dirty Referenced Anonymous LazyFree ShmemPmdMapped FilePmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked THPeligible Mapping +// 73eb5f4c7000 r-xp 00036000 08:08 2274176 1284 1148 1148 0 1148 0 0 0 0 0 0 0 0 0 0 ld-linux-x86-64.so.2 +// 7ffd588fc000 r--p 00000000 00:00 2274176 20 20 20 20 20 20 0 0 0 0 0 0 0 0 0 [stack] +// ffffffffff600000 rw-p 00000000 00:00 2274176 36 36 36 36 36 36 0 0 0 0 0 0 0 0 0 (one intentional trailing space) // ... -// ==== ==== ==== ========= ========== ========= ======== ============== ============= ============== =============== ==== ======= ====== =========== (one intentional trailing space) -// 4164 3448 2826 552 3448 552 0 0 0 0 0 0 0 0 0 KB (one intentional trailing space) +// ==== ==== ==== ========= ========== ========= ======== ============== ============= ============== =============== ==== ======= ====== =========== (one intentional trailing space) +// 4164 3448 2826 552 3448 552 0 0 0 0 0 0 0 0 0 KB (one intentional trailing space) #[cfg(target_os = "linux")] fn assert_more_extended_format(pid: u32, s: &str, quiet: bool, show_path: bool) { let lines: Vec<_> = s.lines().collect(); @@ -466,11 +466,11 @@ fn assert_more_extended_format(pid: u32, s: &str, quiet: bool, show_path: bool) assert!(re.is_match(lines[0])); if !quiet { - let re = Regex::new(r"^ Address Perm Offset Device Inode +Size +Rss +Pss +Pss_Dirty +Referenced +Anonymous( +KSM)? +LazyFree +ShmemPmdMapped +FilePmdMapped +Shared_Hugetlb +Private_Hugetlb +Swap +SwapPss +Locked +THPeligible( +ProtectionKey)? +Mapping$").unwrap(); + let re = Regex::new(r"^ Address Perm Offset +Device +Inode +Size +Rss +Pss +Pss_Dirty +Referenced +Anonymous( +KSM)? +LazyFree +ShmemPmdMapped +FilePmdMapped +Shared_Hugetlb +Private_Hugetlb +Swap +SwapPss +Locked +THPeligible( +ProtectionKey)? +Mapping$").unwrap(); assert!(re.is_match(lines[1]), "failing line: '{}'", lines[1]); } - let base_pattern = r"^[0-9a-f]{16} (-|r)(-|w)(-|x)(p|s) [0-9a-f]{16} [0-9a-f]{3}:[0-9a-f]{5} +\d+ +[1-9][0-9]* +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)?"; + let base_pattern = r"^[ 0-9a-f]{16} (-|r)(-|w)(-|x)(p|s) [0-9a-f]{8} +[0-9a-f]+:[0-9a-f]+ +\d+ +[1-9][0-9]* +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)?"; let mapping_pattern = if show_path { r" (|\[[a-zA-Z_ ]+\]|/[/a-zA-Z0-9._-]+)$" } else { @@ -487,14 +487,14 @@ fn assert_more_extended_format(pid: u32, s: &str, quiet: bool, show_path: bool) assert!(re.is_match(line), "failing line: '{line}'"); } - let re = Regex::new(r"^ +=+ =+ =+ =+ =+ =+( =+)? =+ =+ =+ =+ =+ =+ =+ =+ =+( =+)? $").unwrap(); + let re = Regex::new(r"^ +=+ =+ =+ =+ =+ =+( =+)? =+ =+ =+ =+ =+ =+ =+ =+ =+( =+)? $").unwrap(); assert!( re.is_match(lines[line_count - 2]), "failing line: '{}'", lines[line_count - 2] ); - let re = Regex::new(r"^ +[1-9][0-9]* +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? KB $").unwrap(); + let re = Regex::new(r"^ +[1-9][0-9]* +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? KB $").unwrap(); assert!( re.is_match(lines[line_count - 1]), "failing line: '{}'", @@ -506,13 +506,13 @@ fn assert_more_extended_format(pid: u32, s: &str, quiet: bool, show_path: bool) // Ensure `s` has the following most extended format (--XX): // // 1234: /some/path -// Address Perm Offset Device Inode Size KernelPageSize MMUPageSize Rss Pss Pss_Dirty Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous LazyFree AnonHugePages ShmemPmdMapped FilePmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked THPeligible VmFlags Mapping -// 000073eb5f4c7000 r-xp 0000000000036000 008:00008 2274176 1284 4 4 1148 1148 0 0 0 1148 0 1148 0 0 0 0 0 0 0 0 0 0 0 rd ex mr mw me ld-linux-x86-64.so.2 -// 00007ffd588fc000 r--p 0000000000000000 000:00000 2274176 20 4 4 20 20 20 0 0 0 20 20 20 0 0 0 0 0 0 0 0 0 0 rd mr mw me ac [stack] -// ffffffffff600000 rw-p 0000000000000000 000:00000 2274176 36 4 4 36 36 36 0 0 0 36 36 36 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me ac (one intentional trailing space) +// Address Perm Offset Device Inode Size KernelPageSize MMUPageSize Rss Pss Pss_Dirty Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous LazyFree AnonHugePages ShmemPmdMapped FilePmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked THPeligible VmFlags Mapping +// 73eb5f4c7000 r-xp 00036000 08:08 2274176 1284 4 4 1148 1148 0 0 0 1148 0 1148 0 0 0 0 0 0 0 0 0 0 0 rd ex mr mw me ld-linux-x86-64.so.2 +// 7ffd588fc000 r--p 00000000 00:00 2274176 20 4 4 20 20 20 0 0 0 20 20 20 0 0 0 0 0 0 0 0 0 0 rd mr mw me ac [stack] +// ffffffffff600000 rw-p 00000000 00:00 2274176 36 4 4 36 36 36 0 0 0 36 36 36 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me ac (one intentional trailing space) // ... -// ==== ============== =========== ==== ==== ========= ============ ============ ============= ============= ========== ========= ======== ============= ============== ============= ============== =============== ==== ======= ====== =========== (one intentional trailing space) -// 4164 92 92 3448 2880 552 1132 0 1764 552 3448 552 0 0 0 0 0 0 0 0 0 0 KB (one intentional trailing space) +// ==== ============== =========== ==== ==== ========= ============ ============ ============= ============= ========== ========= ======== ============= ============== ============= ============== =============== ==== ======= ====== =========== (one intentional trailing space) +// 4164 92 92 3448 2880 552 1132 0 1764 552 3448 552 0 0 0 0 0 0 0 0 0 0 KB (one intentional trailing space) #[cfg(target_os = "linux")] fn assert_most_extended_format(pid: u32, s: &str, quiet: bool, show_path: bool) { let lines: Vec<_> = s.lines().collect(); @@ -522,11 +522,11 @@ fn assert_most_extended_format(pid: u32, s: &str, quiet: bool, show_path: bool) assert!(re.is_match(lines[0])); if !quiet { - let re = Regex::new(r"^ Address Perm Offset Device Inode +Size +KernelPageSize +MMUPageSize +Rss +Pss +Pss_Dirty +Shared_Clean +Shared_Dirty +Private_Clean +Private_Dirty +Referenced +Anonymous( +KSM)? +LazyFree +AnonHugePages +ShmemPmdMapped +FilePmdMapped +Shared_Hugetlb +Private_Hugetlb +Swap +SwapPss +Locked +THPeligible( +ProtectionKey)? +VmFlags +Mapping$").unwrap(); + let re = Regex::new(r"^ Address Perm Offset +Device +Inode +Size +KernelPageSize +MMUPageSize +Rss +Pss +Pss_Dirty +Shared_Clean +Shared_Dirty +Private_Clean +Private_Dirty +Referenced +Anonymous( +KSM)? +LazyFree +AnonHugePages +ShmemPmdMapped +FilePmdMapped +Shared_Hugetlb +Private_Hugetlb +Swap +SwapPss +Locked +THPeligible( +ProtectionKey)? +VmFlags +Mapping$").unwrap(); assert!(re.is_match(lines[1]), "failing line: '{}'", lines[1]); } - let base_pattern = r"^[0-9a-f]{16} (-|r)(-|w)(-|x)(p|s) [0-9a-f]{16} [0-9a-f]{3}:[0-9a-f]{5} +\d+ +[1-9][0-9]* +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? +([a-z][a-z] )*"; + let base_pattern = r"^[ 0-9a-f]{16} (-|r)(-|w)(-|x)(p|s) [0-9a-f]{8} +[0-9a-f]+:[0-9a-f]+ +\d+ +[1-9][0-9]* +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? +([a-z][a-z] )*"; let mapping_pattern = if show_path { r"(|\[[a-zA-Z_ ]+\]|/[/a-zA-Z0-9._-]+)$" } else { @@ -543,14 +543,14 @@ fn assert_most_extended_format(pid: u32, s: &str, quiet: bool, show_path: bool) assert!(re.is_match(line), "failing line: '{line}'"); } - let re = Regex::new(r"^ +=+ =+ =+ =+ =+ =+ =+ =+ =+ =+ =+ =+( =+)? =+ =+ =+ =+ =+ =+ =+ =+ =+ =+( =+)? $").unwrap(); + let re = Regex::new(r"^ +=+ =+ =+ =+ =+ =+ =+ =+ =+ =+ =+ =+( =+)? =+ =+ =+ =+ =+ =+ =+ =+ =+ =+( =+)? $").unwrap(); assert!( re.is_match(lines[line_count - 2]), "failing line: '{}'", lines[line_count - 2] ); - let re = Regex::new(r"^ +[1-9][0-9]* +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? KB $").unwrap(); + let re = Regex::new(r"^ +[1-9][0-9]* +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+( +\d+)? KB $").unwrap(); assert!( re.is_match(lines[line_count - 1]), "failing line: '{}'", From 41080642666fb4cad5068a023259c43457c6b3e2 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sun, 6 Jul 2025 17:07:46 +0200 Subject: [PATCH 04/40] Cargo.lock: update with changes from #456 --- Cargo.lock | 53 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc45fbb..d36f0a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -414,6 +414,15 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -424,6 +433,18 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.0", + "windows-sys 0.60.2", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -431,7 +452,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] @@ -507,7 +528,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -701,7 +722,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -894,6 +915,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "os_display" version = "0.1.4" @@ -1212,6 +1239,17 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror 2.0.12", +] + [[package]] name = "regex" version = "1.11.1" @@ -1272,7 +1310,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1285,7 +1323,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1474,7 +1512,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1749,6 +1787,7 @@ name = "uu_pmap" version = "0.0.1" dependencies = [ "clap", + "dirs", "uucore", ] @@ -2040,7 +2079,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] From 10059608e5e1f059ccb6ab904a9ec75ada13ed87 Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Mon, 7 Jul 2025 12:30:39 +0800 Subject: [PATCH 05/40] vmstat: implement `--slabs` --- Cargo.lock | 1 + src/uu/slabtop/src/parse.rs | 6 ++--- src/uu/slabtop/src/slabtop.rs | 4 ++- src/uu/vmstat/Cargo.toml | 2 ++ src/uu/vmstat/src/vmstat.rs | 50 ++++++++++++++++++++++++++++++----- 5 files changed, 53 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d36f0a8..fce8cb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1881,6 +1881,7 @@ dependencies = [ "chrono", "clap", "terminal_size", + "uu_slabtop", "uucore", ] diff --git a/src/uu/slabtop/src/parse.rs b/src/uu/slabtop/src/parse.rs index 0730379..b16d0be 100644 --- a/src/uu/slabtop/src/parse.rs +++ b/src/uu/slabtop/src/parse.rs @@ -10,9 +10,9 @@ use std::{ }; #[derive(Debug, Default)] -pub(crate) struct SlabInfo { - pub(crate) meta: Vec, - pub(crate) data: Vec<(String, Vec)>, +pub struct SlabInfo { + pub meta: Vec, + pub data: Vec<(String, Vec)>, } impl SlabInfo { diff --git a/src/uu/slabtop/src/slabtop.rs b/src/uu/slabtop/src/slabtop.rs index 3fc0165..1dc5c91 100644 --- a/src/uu/slabtop/src/slabtop.rs +++ b/src/uu/slabtop/src/slabtop.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::parse::SlabInfo; +pub use crate::parse::SlabInfo; use clap::{arg, crate_version, ArgAction, Command}; use uucore::{error::UResult, format_usage, help_about, help_section, help_usage}; @@ -25,6 +25,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let slabinfo = SlabInfo::new()?.sort(*sort_flag, false); + println!("{slabinfo:?}"); + if matches.get_flag("once") { output_header(&slabinfo); println!(); diff --git a/src/uu/vmstat/Cargo.toml b/src/uu/vmstat/Cargo.toml index 6538ed4..f912261 100644 --- a/src/uu/vmstat/Cargo.toml +++ b/src/uu/vmstat/Cargo.toml @@ -17,6 +17,8 @@ clap = { workspace = true } terminal_size = { workspace = true } uucore = { workspace = true, features = ["custom-tz-fmt"] } +uu_slabtop = {path = "../slabtop"} + [lib] path = "src/vmstat.rs" diff --git a/src/uu/vmstat/src/vmstat.rs b/src/uu/vmstat/src/vmstat.rs index 93996af..8b943f0 100644 --- a/src/uu/vmstat/src/vmstat.rs +++ b/src/uu/vmstat/src/vmstat.rs @@ -38,6 +38,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let one_header = matches.get_flag("one-header"); let no_first = matches.get_flag("no-first"); + let term_height = terminal_size::terminal_size() + .map(|size| size.1 .0) + .unwrap_or(0); + + if matches.get_flag("slabs") { + return print_slabs(one_header, term_height); + } let delay = matches.get_one::("delay"); let count = matches.get_one::("count"); @@ -57,14 +64,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { line_count += 1; } - let term_height = terminal_size::terminal_size() - .map(|size| size.1 .0) - .unwrap_or(0); - while count.is_none() || line_count < count.unwrap() { std::thread::sleep(std::time::Duration::from_secs(delay)); let proc_data_now = ProcData::new(); - if !one_header && term_height > 0 && ((line_count + 3) % term_height as u64 == 0) { + if needs_header(one_header, term_height, line_count) { print_header(&pickers); } print_data(&pickers, &proc_data_now, Some(&proc_data), &matches); @@ -76,6 +79,41 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { Ok(()) } +#[cfg(target_os = "linux")] +fn print_slabs(one_header: bool, term_height: u16) -> UResult<()> { + let mut slab_data = uu_slabtop::SlabInfo::new()?.data; + + slab_data.sort_by_key(|k| k.0.to_lowercase()); + + print_slab_header(); + + for (line_count, slab_item) in slab_data.into_iter().enumerate() { + if needs_header(one_header, term_height, line_count as u64) { + print_slab_header(); + } + + println!( + "{:<24} {:>6} {:>6} {:>6} {:>6}", + slab_item.0, slab_item.1[0], slab_item.1[1], slab_item.1[2], slab_item.1[3] + ); + } + + Ok(()) +} + +#[cfg(target_os = "linux")] +fn needs_header(one_header: bool, term_height: u16, line_count: u64) -> bool { + !one_header && term_height > 0 && ((line_count + 3) % term_height as u64 == 0) +} + +#[cfg(target_os = "linux")] +fn print_slab_header() { + println!( + "{:<24} {:>6} {:>6} {:>6} {:>6}", + "Cache", "Num", "Total", "Size", "Pages" + ); +} + #[cfg(target_os = "linux")] fn print_header(pickers: &[Picker]) { let mut section: Vec<&str> = vec![]; @@ -126,7 +164,7 @@ pub fn uu_app() -> Command { .value_parser(value_parser!(u64)), arg!(-a --active "Display active and inactive memory"), // arg!(-f --forks "switch displays the number of forks since boot"), - // arg!(-m --slabs "Display slabinfo"), + 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!(-d --disk "Report disk statistics"), From 8bd9a1f35184fe5b699e5ec02ba988392d04c164 Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Wed, 9 Jul 2025 21:36:05 +0800 Subject: [PATCH 06/40] vmstat&slabtop: fix error description --- src/uu/slabtop/src/parse.rs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/uu/slabtop/src/parse.rs b/src/uu/slabtop/src/parse.rs index b16d0be..eb01646 100644 --- a/src/uu/slabtop/src/parse.rs +++ b/src/uu/slabtop/src/parse.rs @@ -3,11 +3,9 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use std::{ - cmp::Ordering, - fs, - io::{Error, ErrorKind}, -}; +use std::io::Error; +use std::{cmp::Ordering, fs, io::ErrorKind}; +use uucore::error::{UError, UResult, USimpleError}; #[derive(Debug, Default)] pub struct SlabInfo { @@ -18,10 +16,22 @@ pub struct SlabInfo { impl SlabInfo { // parse slabinfo from /proc/slabinfo // need root permission - pub fn new() -> Result { - let content = fs::read_to_string("/proc/slabinfo")?; + pub fn new() -> UResult { + let error_wrapper = |e: Error| { + USimpleError::new( + 1, + format!( + "Unable to create slabinfo structure: {}", + Box::::from(e) // We need Display impl of UError + ), + ) + }; - Self::parse(&content).ok_or(ErrorKind::Unsupported.into()) + let content = fs::read_to_string("/proc/slabinfo").map_err(error_wrapper)?; + + Self::parse(&content) + .ok_or(ErrorKind::Unsupported.into()) + .map_err(error_wrapper) } pub fn parse(content: &str) -> Option { From 10d30d6a1e5fc1647007161f2bd2c81324373ed6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 03:51:43 +0000 Subject: [PATCH 07/40] chore(deps): update rust crate clap to v4.5.41 --- Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d36f0a8..ec9e223 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -183,18 +183,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.40" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.40" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" dependencies = [ "anstream", "anstyle", @@ -528,7 +528,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -722,7 +722,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1310,7 +1310,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1323,7 +1323,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1512,7 +1512,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2079,7 +2079,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] From 6e8b63db04a8f16c649d0ad5861259e76f91e224 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 03:51:49 +0000 Subject: [PATCH 08/40] chore(deps): update rust crate clap_complete to v4.5.55 --- Cargo.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d36f0a8..494715f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,9 +205,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.54" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aad5b1b4de04fead402672b48897030eec1f3bfe1550776322f59f6d6e6a5677" +checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a" dependencies = [ "clap", ] @@ -528,7 +528,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -722,7 +722,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1310,7 +1310,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1323,7 +1323,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1512,7 +1512,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2079,7 +2079,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] From f9873ce41e1f4aa39ac43631f638b1813d1d33fa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 06:03:13 +0000 Subject: [PATCH 09/40] chore(deps): update rust crate clap_mangen to v0.2.28 --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6ebe5d..81e1a51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -220,9 +220,9 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clap_mangen" -version = "0.2.27" +version = "0.2.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc33c849748320656a90832f54a5eeecaa598e92557fb5dedebc3355746d31e4" +checksum = "e2fb6d3f935bbb9819391528b0e7cf655e78a0bc7a7c3d227211a1d24fc11db1" dependencies = [ "clap", "roff", @@ -528,7 +528,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -722,7 +722,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1310,7 +1310,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1323,7 +1323,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1512,7 +1512,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] From ac9e17a7d0186c113f9bd9f56bfbe8669a4aed6e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 19:52:04 +0000 Subject: [PATCH 10/40] chore(deps): update rust crate sysinfo to 0.36.0 --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81e1a51..b688cc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1490,9 +1490,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.35.2" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3ffa3e4ff2b324a57f7aeb3c349656c7b127c3c189520251a648102a92496e" +checksum = "aab138f5c1bb35231de19049060a87977ad23e04f2303e953bc5c2947ac7dec4" dependencies = [ "libc", "memchr", @@ -2079,7 +2079,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fe4a31d..b1621df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,7 @@ prettytable-rs = "0.10.0" rand = { version = "0.9.0", features = ["small_rng"] } ratatui = "0.29.0" regex = "1.10.4" -sysinfo = "0.35.0" +sysinfo = "0.36.0" tempfile = "3.10.1" terminal_size = "0.4.2" textwrap = { version = "0.16.1", features = ["terminal_size"] } From 7ec4a414ad3c704f8d664ae880d33ff24f986463 Mon Sep 17 00:00:00 2001 From: estodi <83288835+estodi@users.noreply.github.com> Date: Tue, 15 Jul 2025 06:11:03 -0700 Subject: [PATCH 11/40] pmap: implemented `--range` option (#461) * pmap: implemented range option * pmap: added tests for range option * fixed lint errors --------- Co-authored-by: Krysztal Huang --- src/uu/pmap/src/maps_format_parser.rs | 91 ++++++++++++++++++ src/uu/pmap/src/pmap.rs | 133 ++++++++++++++++++-------- src/uu/pmap/src/pmap_config.rs | 2 + tests/by-util/test_pmap.rs | 89 ++++++++++++++--- 4 files changed, 261 insertions(+), 54 deletions(-) diff --git a/src/uu/pmap/src/maps_format_parser.rs b/src/uu/pmap/src/maps_format_parser.rs index 0e07760..c1418ca 100644 --- a/src/uu/pmap/src/maps_format_parser.rs +++ b/src/uu/pmap/src/maps_format_parser.rs @@ -39,6 +39,13 @@ impl Address { pub fn zero_pad(&self) -> String { format!("{:0>16}", self.start) } + + // Checks whether an entry's address range overlaps the limits specified by the range option. + // Note: Even if a reversed range (high < low) is given, an entry still hits + // only if the specified range lies entirely within the entry's address range. + pub fn is_within_range(&self, pmap_config: &PmapConfig) -> bool { + pmap_config.range_low < self.high && self.low <= pmap_config.range_high + } } // Represents a set of permissions from the "perms" column of /proc//maps. @@ -341,6 +348,90 @@ mod test { assert!(parse_address("ffffffffff600000-zfffffffff601000").is_err()); } + fn limit_address_range_and_assert(address: &Address, low: u64, high: u64, expected: bool) { + let mut pmap_config = PmapConfig::default(); + (pmap_config.range_low, pmap_config.range_high) = (low, high); + assert_eq!( + address.is_within_range(&pmap_config), + expected, + "`--range 0x{low:x},0x{high:x}` expected to be {expected} for address 0x{:x},0x{:x}", + address.low, + address.high, + ); + } + + #[test] + fn test_limit_address_range() { + let low: u64 = 0x71af50000000; + let high: u64 = 0x71af50021000; + let address = Address { + start: "0x71af50000000".to_string(), + low, + high, + }; + + limit_address_range_and_assert(&address, 0x0, u64::MAX, true); + limit_address_range_and_assert(&address, 0x70000000, 0xffffffffffff, true); + + limit_address_range_and_assert(&address, 0x0, 0x0, false); + limit_address_range_and_assert(&address, 0x0, 0x70000000, false); + limit_address_range_and_assert(&address, 0x0, low - 1, false); + limit_address_range_and_assert(&address, low - 1, low - 1, false); + + limit_address_range_and_assert(&address, 0x0, low + 0, true); + limit_address_range_and_assert(&address, low - 1, low + 0, true); + limit_address_range_and_assert(&address, low + 0, low + 0, true); + + limit_address_range_and_assert(&address, low - 1, high - 1, true); + limit_address_range_and_assert(&address, low - 1, high + 0, true); + limit_address_range_and_assert(&address, low - 1, high + 1, true); + limit_address_range_and_assert(&address, low + 0, high - 1, true); + limit_address_range_and_assert(&address, low + 0, high + 0, true); + limit_address_range_and_assert(&address, low + 0, high + 1, true); + limit_address_range_and_assert(&address, low + 1, high - 1, true); + limit_address_range_and_assert(&address, low + 1, high + 0, true); + limit_address_range_and_assert(&address, low + 1, high + 1, true); + + limit_address_range_and_assert(&address, high - 1, high - 1, true); + limit_address_range_and_assert(&address, high - 1, high + 0, true); + limit_address_range_and_assert(&address, high - 1, u64::MAX, true); + + limit_address_range_and_assert(&address, high + 0, high + 0, false); + limit_address_range_and_assert(&address, high + 0, high + 1, false); + limit_address_range_and_assert(&address, high + 0, u64::MAX, false); + limit_address_range_and_assert(&address, 0xffffffffffff, u64::MAX, false); + limit_address_range_and_assert(&address, u64::MAX, u64::MAX, false); + + // Reversed range + + limit_address_range_and_assert(&address, u64::MAX, 0, false); + limit_address_range_and_assert(&address, 0xffffffffffff, 0x70000000, false); + + limit_address_range_and_assert(&address, 0x70000000, 0x0, false); + limit_address_range_and_assert(&address, low - 1, 0x0, false); + limit_address_range_and_assert(&address, low - 1, low - 1, false); + + limit_address_range_and_assert(&address, low + 0, 0x0, false); + limit_address_range_and_assert(&address, low + 0, low - 1, false); + + limit_address_range_and_assert(&address, high - 1, low - 1, false); + limit_address_range_and_assert(&address, high + 0, low - 1, false); + limit_address_range_and_assert(&address, high + 1, low - 1, false); + limit_address_range_and_assert(&address, high - 1, low + 0, true); // true + limit_address_range_and_assert(&address, high + 0, low + 0, false); + limit_address_range_and_assert(&address, high + 1, low + 0, false); + limit_address_range_and_assert(&address, high - 1, low + 1, true); // true + limit_address_range_and_assert(&address, high + 0, low + 1, false); + limit_address_range_and_assert(&address, high + 1, low + 1, false); + + limit_address_range_and_assert(&address, high + 0, high - 1, false); + limit_address_range_and_assert(&address, u64::MAX, high - 1, false); + + limit_address_range_and_assert(&address, high + 1, high + 0, false); + limit_address_range_and_assert(&address, u64::MAX, high + 0, false); + limit_address_range_and_assert(&address, u64::MAX, 0xffffffffffff, false); + } + #[test] fn test_parse_device() { assert_eq!("12:34", parse_device("12:34").unwrap().to_string()); diff --git a/src/uu/pmap/src/pmap.rs b/src/uu/pmap/src/pmap.rs index aa5bb94..05a046e 100644 --- a/src/uu/pmap/src/pmap.rs +++ b/src/uu/pmap/src/pmap.rs @@ -9,8 +9,8 @@ use pmap_config::{create_rc, pmap_field_name, PmapConfig}; use smaps_format_parser::{parse_smaps, SmapTable}; use std::env; use std::fs; -use std::io::Error; -use uucore::error::{set_exit_code, UResult}; +use std::io::{Error, ErrorKind}; +use uucore::error::{set_exit_code, UResult, USimpleError}; use uucore::{format_usage, help_about, help_usage}; mod maps_format_parser; @@ -98,8 +98,45 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { // Options independent with field selection: pmap_config.quiet = matches.get_flag(options::QUIET); - if matches.get_flag(options::SHOW_PATH) { - pmap_config.show_path = true; + pmap_config.show_path = matches.get_flag(options::SHOW_PATH); + + if let Some(range) = matches.get_one::(options::RANGE) { + match range.matches(',').count() { + 0 => { + let address = u64::from_str_radix(range, 16).map_err(|_| { + USimpleError::new(1, format!("failed to parse argument: '{range}'")) + })?; + pmap_config.range_low = address; + pmap_config.range_high = address; + } + 1 => { + let (low, high) = range + .split_once(',') + .ok_or_else(|| Error::from(ErrorKind::InvalidData))?; + pmap_config.range_low = if low.is_empty() { + 0 + } else { + u64::from_str_radix(low, 16).map_err(|_| { + USimpleError::new(1, format!("failed to parse argument: '{range}'")) + })? + }; + pmap_config.range_high = if high.is_empty() { + u64::MAX + } else { + u64::from_str_radix(high, 16).map_err(|_| { + USimpleError::new(1, format!("failed to parse argument: '{range}'")) + })? + }; + } + _ => { + eprintln!("pmap: failed to parse argument: '{range}'"); + set_exit_code(1); + return Ok(()); + } + } + } else { + pmap_config.range_low = 0; + pmap_config.range_high = u64::MAX; } let pids = matches @@ -182,14 +219,16 @@ fn output_default_format(pid: &str, pmap_config: &PmapConfig) -> Result<(), Erro let mut total = 0; process_maps(pid, None, |map_line| { - println!( - "{} {:>6}K {} {}", - map_line.address.zero_pad(), - map_line.size_in_kb, - map_line.perms.mode(), - map_line.parse_mapping(pmap_config) - ); - total += map_line.size_in_kb; + if map_line.address.is_within_range(pmap_config) { + println!( + "{} {:>6}K {} {}", + map_line.address.zero_pad(), + map_line.size_in_kb, + map_line.perms.mode(), + map_line.parse_mapping(pmap_config) + ); + total += map_line.size_in_kb; + } })?; if !pmap_config.quiet { @@ -206,25 +245,31 @@ fn output_extended_format(pid: &str, pmap_config: &PmapConfig) -> Result<(), Err println!("Address Kbytes RSS Dirty Mode Mapping"); } + let mut total_size_in_kb = 0; + let mut total_rss_in_kb = 0; + let mut total_dirty_in_kb = 0; + for smap_entry in smap_table.entries { - println!( - "{} {:>7} {:>7} {:>7} {} {}", - smap_entry.map_line.address.zero_pad(), - smap_entry.map_line.size_in_kb, - smap_entry.rss_in_kb, - smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb, - smap_entry.map_line.perms.mode(), - smap_entry.map_line.parse_mapping(pmap_config) - ); + if smap_entry.map_line.address.is_within_range(pmap_config) { + println!( + "{} {:>7} {:>7} {:>7} {} {}", + smap_entry.map_line.address.zero_pad(), + smap_entry.map_line.size_in_kb, + smap_entry.rss_in_kb, + smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb, + smap_entry.map_line.perms.mode(), + smap_entry.map_line.parse_mapping(pmap_config) + ); + total_size_in_kb += smap_entry.map_line.size_in_kb; + total_rss_in_kb += smap_entry.rss_in_kb; + total_dirty_in_kb += smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb; + } } if !pmap_config.quiet { println!("---------------- ------- ------- ------- "); println!( - "total kB {:>7} {:>7} {:>7}", - smap_table.info.total_size_in_kb, - smap_table.info.total_rss_in_kb, - smap_table.info.total_shared_dirty_in_kb + smap_table.info.total_private_dirty_in_kb, + "total kB {total_size_in_kb:>7} {total_rss_in_kb:>7} {total_dirty_in_kb:>7}" ); } @@ -360,23 +405,25 @@ fn output_device_format(pid: &str, pmap_config: &PmapConfig) -> Result<(), Error None }, |map_line| { - println!( - "{} {:>7} {} {:0>16} {} {}", - map_line.address.zero_pad(), - map_line.size_in_kb, - map_line.perms.mode(), - map_line.offset, - map_line.device.device(), - map_line.parse_mapping(pmap_config) - ); - total_mapped += map_line.size_in_kb; + if map_line.address.is_within_range(pmap_config) { + println!( + "{} {:>7} {} {:0>16} {} {}", + map_line.address.zero_pad(), + map_line.size_in_kb, + map_line.perms.mode(), + map_line.offset, + map_line.device.device(), + map_line.parse_mapping(pmap_config) + ); + total_mapped += map_line.size_in_kb; - if map_line.perms.writable && !map_line.perms.shared { - total_writeable_private += map_line.size_in_kb; - } + if map_line.perms.writable && !map_line.perms.shared { + total_writeable_private += map_line.size_in_kb; + } - if map_line.perms.shared { - total_shared += map_line.size_in_kb; + if map_line.perms.shared { + total_shared += map_line.size_in_kb; + } } }, )?; @@ -547,7 +594,9 @@ pub fn uu_app() -> Command { Arg::new(options::RANGE) .short('A') .long("range") - .num_args(1..=2) - .help("limit results to the given range"), + .num_args(1) + .help("limit results to the given range [,]"), + // This option applies only to the default, extended, or device formats, + // yet it will not raise an error in any other case. ) } diff --git a/src/uu/pmap/src/pmap_config.rs b/src/uu/pmap/src/pmap_config.rs index 073aa6c..d399eb6 100644 --- a/src/uu/pmap/src/pmap_config.rs +++ b/src/uu/pmap/src/pmap_config.rs @@ -81,6 +81,8 @@ pub struct PmapConfig { // Misc pub quiet: bool, pub custom_format_enabled: bool, + pub range_low: u64, + pub range_high: u64, } impl PmapConfig { diff --git a/tests/by-util/test_pmap.rs b/tests/by-util/test_pmap.rs index e4b9f8c..472078c 100644 --- a/tests/by-util/test_pmap.rs +++ b/tests/by-util/test_pmap.rs @@ -264,8 +264,8 @@ fn test_device_permission_denied() { fn test_quiet() { let pid = process::id(); - for arg in ["-q", "--quiet"] { - _test_multiple_formats(pid, arg, true, false); + for args in [&["-q"], &["--quiet"]] { + _test_multiple_formats(pid, args, true, false); } } @@ -274,8 +274,8 @@ fn test_quiet() { fn test_showpath() { let pid = process::id(); - for arg in ["-p", "--show-path"] { - _test_multiple_formats(pid, arg, false, true); + for args in [&["-p"], &["--show-path"]] { + _test_multiple_formats(pid, args, false, true); } } @@ -284,16 +284,26 @@ fn test_showpath() { fn test_quiet_showpath() { let pid = process::id(); - for arg in ["-qp", "-pq"] { - _test_multiple_formats(pid, arg, true, true); + for args in [&["-qp"], &["-pq"]] { + _test_multiple_formats(pid, args, true, true); + } +} + +#[test] +#[cfg(target_os = "linux")] +fn test_range() { + let pid = process::id(); + + for args in [&["-A", ","], &["--range", ","]] { + _test_multiple_formats(pid, args, false, false); } } #[cfg(target_os = "linux")] -fn _test_multiple_formats(pid: u32, arg: &str, quiet: bool, show_path: bool) { +fn _test_multiple_formats(pid: u32, args: &[&str], quiet: bool, show_path: bool) { // default format let result = new_ucmd!() - .arg(arg) + .args(args) .arg(pid.to_string()) .succeeds() .stdout_move_str(); @@ -302,7 +312,7 @@ fn _test_multiple_formats(pid: u32, arg: &str, quiet: bool, show_path: bool) { // extended format let result = new_ucmd!() - .arg(arg) + .args(args) .arg("--extended") .arg(pid.to_string()) .succeeds() @@ -312,7 +322,7 @@ fn _test_multiple_formats(pid: u32, arg: &str, quiet: bool, show_path: bool) { // more-extended format let result = new_ucmd!() - .arg(arg) + .args(args) .arg("-X") .arg(pid.to_string()) .succeeds() @@ -322,7 +332,7 @@ fn _test_multiple_formats(pid: u32, arg: &str, quiet: bool, show_path: bool) { // most-extended format let result = new_ucmd!() - .arg(arg) + .args(args) .arg("--XX") .arg(pid.to_string()) .succeeds() @@ -332,7 +342,7 @@ fn _test_multiple_formats(pid: u32, arg: &str, quiet: bool, show_path: bool) { // device format let result = new_ucmd!() - .arg(arg) + .args(args) .arg("--device") .arg(pid.to_string()) .succeeds() @@ -341,6 +351,61 @@ fn _test_multiple_formats(pid: u32, arg: &str, quiet: bool, show_path: bool) { assert_device_format(pid, &result, quiet, show_path); } +#[test] +#[cfg(target_os = "linux")] +fn test_range_arg() { + let pid_s = process::id().to_string(); + + for opt in ["-A", "--range"] { + // option without an argument + new_ucmd!().arg(&pid_s).arg(opt).fails().code_is(1); + + // valid arguments + for arg in [ + ",", + "c00fee", + "c00fee,", + ",c00fee", + "c00,fee", + "0", + "0,", + ",0", + "0,0", + "ffffffffffffffff", + "ffffffffffffffff,", + ",ffffffffffffffff", + "ffffffffffffffff,ffffffffffffffff", + ] { + new_ucmd!().arg(&pid_s).arg(opt).arg(arg).succeeds(); + } + + // invalid arguments + for arg in [ + // white spaces + ", ", + " ,", + " , ", + "bad ", + " bad", + // multiple commas + ",,", + ",bad,", + // underscore separator + "bad_beef", + // non-numeric value + "someinvalidtext", + "someinvalidtext,", + ",someinvalidtext", + "someinvalidtext,someinvalidtext", + // too large value (> u64) + "f0000000000000000", + "f0000000000000000,f0000000000000000", + ] { + new_ucmd!().arg(&pid_s).arg(opt).arg(arg).fails().code_is(1); + } + } +} + #[test] fn test_invalid_arg() { new_ucmd!().arg("--definitely-invalid").fails().code_is(1); From d9af1afd6110a9b4ccbdd6f80586aea3a1d1d89c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 18 Jul 2025 17:15:27 +0000 Subject: [PATCH 12/40] chore(deps): update rust crate sysinfo to v0.36.1 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae79620..8f97cf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1490,9 +1490,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.36.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aab138f5c1bb35231de19049060a87977ad23e04f2303e953bc5c2947ac7dec4" +checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d" dependencies = [ "libc", "memchr", From c81d2c28e7858a16566227f308d4d6c8313868f4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 20 Jul 2025 18:39:29 +0000 Subject: [PATCH 13/40] chore(deps): update rust crate rand to v0.9.2 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8f97cf5..6419f87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1112,7 +1112,7 @@ dependencies = [ "phf 0.12.1", "phf_codegen 0.12.1", "pretty_assertions", - "rand 0.9.1", + "rand 0.9.2", "regex", "rlimit", "sysinfo", @@ -1165,9 +1165,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", "rand_core 0.9.3", @@ -1957,7 +1957,7 @@ dependencies = [ "libc", "nix", "pretty_assertions", - "rand 0.9.1", + "rand 0.9.2", "regex", "rlimit", "tempfile", From 56b9c9f11fdc4c7412c0ab26a5749e8e196f554a Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 15 Jul 2025 14:41:15 +0200 Subject: [PATCH 14/40] Bump chrono-tz from 0.10.3 to 0.10.4 --- Cargo.lock | 99 +++++++----------------------------------------------- 1 file changed, 13 insertions(+), 86 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6419f87..4b1ba69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -162,23 +162,12 @@ dependencies = [ [[package]] name = "chrono-tz" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efdce149c370f133a071ca8ef6ea340b7b88748ab0810097a9e2976eaa34b4f3" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" dependencies = [ "chrono", - "chrono-tz-build", - "phf 0.11.3", -] - -[[package]] -name = "chrono-tz-build" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f10f8c9340e31fc120ff885fcdb54a0b48e474bbd77cab557f0c30a3e569402" -dependencies = [ - "parse-zoneinfo", - "phf_codegen 0.11.3", + "phf", ] [[package]] @@ -953,68 +942,30 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "parse-zoneinfo" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" -dependencies = [ - "regex", -] - [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_shared 0.11.3", -] - [[package]] name = "phf" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" dependencies = [ - "phf_shared 0.12.1", + "phf_shared", "serde", ] -[[package]] -name = "phf_codegen" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" -dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", -] - [[package]] name = "phf_codegen" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efbdcb6f01d193b17f0b9c3360fa7e0e620991b193ff08702f78b3ce365d7e61" dependencies = [ - "phf_generator 0.12.1", - "phf_shared 0.12.1", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared 0.11.3", - "rand 0.8.5", + "phf_generator", + "phf_shared", ] [[package]] @@ -1024,16 +975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cbb1126afed61dd6368748dae63b1ee7dc480191c6262a3b4ff1e29d86a6c5b" dependencies = [ "fastrand", - "phf_shared 0.12.1", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", + "phf_shared", ] [[package]] @@ -1109,10 +1051,10 @@ dependencies = [ "clap_mangen", "ctor", "libc", - "phf 0.12.1", - "phf_codegen 0.12.1", + "phf", + "phf_codegen", "pretty_assertions", - "rand 0.9.2", + "rand", "regex", "rlimit", "sysinfo", @@ -1154,15 +1096,6 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core 0.6.4", -] - [[package]] name = "rand" version = "0.9.2" @@ -1170,7 +1103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", - "rand_core 0.9.3", + "rand_core", ] [[package]] @@ -1180,15 +1113,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - [[package]] name = "rand_core" version = "0.9.3" @@ -1957,7 +1884,7 @@ dependencies = [ "libc", "nix", "pretty_assertions", - "rand 0.9.2", + "rand", "regex", "rlimit", "tempfile", From 05127ea2ec1cbc66dafb159066f667b4bc8b4937 Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Tue, 22 Jul 2025 18:57:39 +0800 Subject: [PATCH 15/40] vmstat: implement `--stats` --- src/uu/vmstat/src/parser.rs | 113 ++++++++++++++++++++++--------- src/uu/vmstat/src/picker.rs | 129 +++++++++++++++++++++++++++++++++++- src/uu/vmstat/src/vmstat.rs | 34 +++++++--- 3 files changed, 234 insertions(+), 42 deletions(-) diff --git a/src/uu/vmstat/src/parser.rs b/src/uu/vmstat/src/parser.rs index 6cff6ea..831023e 100644 --- a/src/uu/vmstat/src/parser.rs +++ b/src/uu/vmstat/src/parser.rs @@ -61,6 +61,30 @@ impl ProcData { let idle_time = parts.next().unwrap().parse::().unwrap(); (uptime, idle_time) } + + pub fn get_one(table: &HashMap, 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::>(); - let user = load[0].parse::().unwrap(); - let nice = load[1].parse::().unwrap(); - let system = load[2].parse::().unwrap(); - let idle = load[3].parse::().unwrap_or_default(); // since 2.5.41 - let io_wait = load[4].parse::().unwrap_or_default(); // since 2.5.41 - let hardware_interrupt = load[5].parse::().unwrap_or_default(); // since 2.6.0 - let software_interrupt = load[6].parse::().unwrap_or_default(); // since 2.6.0 - let steal_time = load[7].parse::().unwrap_or_default(); // since 2.6.11 - let guest = load[8].parse::().unwrap_or_default(); // since 2.6.24 - let guest_nice = load[9].parse::().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::().unwrap(); + let nice = load[1].parse::().unwrap(); + let system = load[2].parse::().unwrap(); + let idle = load[3].parse::().unwrap_or_default(); // since 2.5.41 + let io_wait = load[4].parse::().unwrap_or_default(); // since 2.5.41 + let hardware_interrupt = load[5].parse::().unwrap_or_default(); // since 2.6.0 + let software_interrupt = load[6].parse::().unwrap_or_default(); // since 2.6.0 + let steal_time = load[7].parse::().unwrap_or_default(); // since 2.6.11 + let guest = load[8].parse::().unwrap_or_default(); // since 2.6.24 + let guest_nice = load[9].parse::().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) -> 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, } } } diff --git a/src/uu/vmstat/src/picker.rs b/src/uu/vmstat/src/picker.rs index 5e5362b..e2b4026 100644 --- a/src/uu/vmstat/src/picker.rs +++ b/src/uu/vmstat/src/picker.rs @@ -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 { 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::() + .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::("unit") { diff --git a/src/uu/vmstat/src/vmstat.rs b/src/uu/vmstat/src/vmstat.rs index 8b943f0..38619a7 100644 --- a/src/uu/vmstat/src/vmstat.rs +++ b/src/uu/vmstat/src/vmstat.rs @@ -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::("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::("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::("delay"); let count = matches.get_one::("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 "Detailed statistics about partition"), From ad8598e11687aa1cb5b3d1a9a8d805fc29c63e07 Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Thu, 24 Jul 2025 15:48:37 +0800 Subject: [PATCH 16/40] vmstat: add test for `--stats` --- tests/by-util/test_vmstat.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/by-util/test_vmstat.rs b/tests/by-util/test_vmstat.rs index a37a790..b48c240 100644 --- a/tests/by-util/test_vmstat.rs +++ b/tests/by-util/test_vmstat.rs @@ -81,3 +81,9 @@ fn test_timestamp() { .unwrap() .contains("timestamp")); } + +#[test] +#[cfg(target_os = "linux")] +fn test_stats() { + new_ucmd!().arg("-s").succeeds(); +} From b6327b8730cd0e95ec9093ba3713569d743d6127 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 27 Jul 2025 13:30:23 +0000 Subject: [PATCH 17/40] chore(deps): update rust crate ctor to v0.4.3 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6419f87..7d0258a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,9 +329,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4735f265ba6a1188052ca32d461028a7d1125868be18e287e756019da7607b5" +checksum = "ec09e802f5081de6157da9a75701d6c713d8dc3ba52571fd4bd25f412644e8a6" dependencies = [ "ctor-proc-macro", "dtor", @@ -339,9 +339,9 @@ dependencies = [ [[package]] name = "ctor-proc-macro" -version = "0.0.5" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f211af61d8efdd104f96e57adf5e426ba1bc3ed7a4ead616e15e5881fd79c4d" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" [[package]] name = "darling" From 88e57efa441dae0dbcf7d201c4fe51d95502df3c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 02:49:35 +0000 Subject: [PATCH 18/40] chore(deps): update rust crate clap_mangen to v0.2.29 --- Cargo.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d0258a..b9fe06f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -220,9 +220,9 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clap_mangen" -version = "0.2.28" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2fb6d3f935bbb9819391528b0e7cf655e78a0bc7a7c3d227211a1d24fc11db1" +checksum = "27b4c3c54b30f0d9adcb47f25f61fcce35c4dd8916638c6b82fbd5f4fb4179e2" dependencies = [ "clap", "roff", @@ -528,7 +528,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -722,7 +722,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1310,7 +1310,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1323,7 +1323,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1512,7 +1512,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2080,7 +2080,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] From 5805b655fd22a00daa1eeb19aec9c1022c3c538a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 05:13:37 +0000 Subject: [PATCH 19/40] chore(deps): update rust crate clap to v4.5.42 --- Cargo.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af3d732..294cd98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,18 +172,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.41" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" +checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.41" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" +checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" dependencies = [ "anstream", "anstyle", @@ -517,7 +517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -711,7 +711,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1237,7 +1237,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1250,7 +1250,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1439,7 +1439,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] From 6a64f1bc82abafa58272e9bfc0a39cbe007fc556 Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Tue, 29 Jul 2025 19:09:10 +0800 Subject: [PATCH 20/40] vmstat: implement `--forks` --- src/uu/vmstat/src/vmstat.rs | 22 +++++++++++++++++++--- tests/by-util/test_vmstat.rs | 5 +++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/uu/vmstat/src/vmstat.rs b/src/uu/vmstat/src/vmstat.rs index 38619a7..75d11c8 100644 --- a/src/uu/vmstat/src/vmstat.rs +++ b/src/uu/vmstat/src/vmstat.rs @@ -26,6 +26,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args)?; #[cfg(target_os = "linux")] { + if matches.get_flag("forks") { + return print_forks(); + } if matches.get_flag("stats") { return print_stats(); } @@ -83,6 +86,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { Ok(()) } +#[cfg(target_os = "linux")] +fn print_forks() -> UResult<()> { + let data = get_stats(); + + let fork_data = data.last().unwrap(); + println!("{:>13} {}", fork_data.1, fork_data.0); + + Ok(()) +} + #[cfg(target_os = "linux")] fn print_stats() -> UResult<()> { let data = get_stats(); @@ -177,10 +190,13 @@ pub fn uu_app() -> Command { .required(false) .value_parser(value_parser!(u64)), arg!(-a --active "Display active and inactive memory"), - // arg!(-f --forks "switch displays the number of forks since boot"), - arg!(-m --slabs "Display slabinfo"), + arg!(-f --forks "switch displays the number of forks since boot") + .conflicts_with_all(["slabs", "stats", /*"disk", "disk-sum", "partition"*/]), + arg!(-m --slabs "Display slabinfo") + .conflicts_with_all(["forks", "stats", /*"disk", "disk-sum", "partition"*/]), 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") + .conflicts_with_all(["forks", "slabs", /*"disk", "disk-sum", "partition"*/]), // arg!(-d --disk "Report disk statistics"), // arg!(-D --"disk-sum" "Report some summary statistics about disk activity"), // arg!(-p --partition "Detailed statistics about partition"), diff --git a/tests/by-util/test_vmstat.rs b/tests/by-util/test_vmstat.rs index b48c240..6ff26c3 100644 --- a/tests/by-util/test_vmstat.rs +++ b/tests/by-util/test_vmstat.rs @@ -19,6 +19,11 @@ fn test_invalid_arg() { new_ucmd!().arg("--definitely-invalid").fails().code_is(1); } +#[test] +fn test_conflict_arg() { + new_ucmd!().args(&["-s", "-m"]).fails().code_is(1); +} + #[test] fn test_invalid_number() { new_ucmd!().arg("-1").fails().code_is(1); From 19a1c37236fd7829e006ec326dfa884fd79d1706 Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Thu, 31 Jul 2025 17:41:39 +0800 Subject: [PATCH 21/40] vmstat: implement `--disk` `--disk-sum` `--partition` --- src/uu/vmstat/src/parser.rs | 110 ++++++++++++++++++++++++++ src/uu/vmstat/src/picker.rs | 59 +++++++++++++- src/uu/vmstat/src/vmstat.rs | 144 +++++++++++++++++++++++++++++++---- tests/by-util/test_vmstat.rs | 12 +++ 4 files changed, 310 insertions(+), 15 deletions(-) diff --git a/src/uu/vmstat/src/parser.rs b/src/uu/vmstat/src/parser.rs index 831023e..a6441de 100644 --- a/src/uu/vmstat/src/parser.rs +++ b/src/uu/vmstat/src/parser.rs @@ -5,6 +5,8 @@ #[cfg(target_os = "linux")] use std::collections::HashMap; +#[cfg(target_os = "linux")] +use std::fmt::{Debug, Display, Formatter}; #[cfg(target_os = "linux")] pub fn parse_proc_file(path: &str) -> HashMap { @@ -31,6 +33,7 @@ pub struct ProcData { pub stat: HashMap, pub meminfo: HashMap, pub vmstat: HashMap, + pub diskstat: Vec, } #[cfg(target_os = "linux")] impl Default for ProcData { @@ -45,11 +48,17 @@ impl ProcData { let stat = parse_proc_file("/proc/stat"); let meminfo = parse_proc_file("/proc/meminfo"); let vmstat = parse_proc_file("/proc/vmstat"); + let diskstat = std::fs::read_to_string("/proc/diskstats") + .unwrap() + .lines() + .map(|line| line.to_string()) + .collect(); Self { uptime, stat, meminfo, vmstat, + diskstat, } } @@ -228,3 +237,104 @@ impl Meminfo { } } } + +#[cfg(target_os = "linux")] +#[derive(Debug)] +pub struct DiskStatParseError; + +#[cfg(target_os = "linux")] +impl Display for DiskStatParseError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt("Failed to parse diskstat line", f) + } +} + +#[cfg(target_os = "linux")] +impl std::error::Error for DiskStatParseError {} + +#[cfg(target_os = "linux")] +pub struct DiskStat { + // Name from https://www.kernel.org/doc/html/latest/admin-guide/iostats.html + pub major: u64, + pub minor: u64, + pub device: String, + pub reads_completed: u64, + pub reads_merged: u64, + pub sectors_read: u64, + pub milliseconds_spent_reading: u64, + pub writes_completed: u64, + pub writes_merged: u64, + pub sectors_written: u64, + pub milliseconds_spent_writing: u64, + pub ios_currently_in_progress: u64, + pub milliseconds_spent_doing_ios: u64, + pub weighted_milliseconds_spent_doing_ios: u64, + pub discards_completed: u64, + pub discards_merged: u64, + pub sectors_discarded: u64, + pub milliseconds_spent_discarding: u64, + pub flush_requests_completed: u64, + pub milliseconds_spent_flushing: u64, +} + +#[cfg(target_os = "linux")] +impl std::str::FromStr for DiskStat { + type Err = DiskStatParseError; + + fn from_str(line: &str) -> Result { + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() < 14 { + Err(DiskStatParseError)?; + } + + let parse_value = |s: &str| s.parse::().map_err(|_| DiskStatParseError); + let parse_optional_value = |s: Option<&&str>| match s { + None => Ok(0), + Some(value) => value.parse::().map_err(|_| DiskStatParseError), + }; + + Ok(Self { + major: parse_value(parts[0])?, + minor: parse_value(parts[1])?, + device: parts[2].to_string(), + reads_completed: parse_value(parts[3])?, + reads_merged: parse_value(parts[4])?, + sectors_read: parse_value(parts[5])?, + milliseconds_spent_reading: parse_value(parts[6])?, + writes_completed: parse_value(parts[7])?, + writes_merged: parse_value(parts[8])?, + sectors_written: parse_value(parts[9])?, + milliseconds_spent_writing: parse_value(parts[10])?, + ios_currently_in_progress: parse_value(parts[11])?, + milliseconds_spent_doing_ios: parse_value(parts[12])?, + weighted_milliseconds_spent_doing_ios: parse_optional_value(parts.get(13))?, + discards_completed: parse_optional_value(parts.get(14))?, + discards_merged: parse_optional_value(parts.get(15))?, + sectors_discarded: parse_optional_value(parts.get(16))?, + milliseconds_spent_discarding: parse_optional_value(parts.get(17))?, + flush_requests_completed: parse_optional_value(parts.get(18))?, + milliseconds_spent_flushing: parse_optional_value(parts.get(19))?, + }) + } +} + +#[cfg(target_os = "linux")] +impl DiskStat { + pub fn is_disk(&self) -> bool { + std::path::Path::new(&format!("/sys/block/{}", self.device)).exists() + } + + pub fn current() -> Result, DiskStatParseError> { + let diskstats = + std::fs::read_to_string("/proc/diskstats").map_err(|_| DiskStatParseError)?; + let lines = diskstats.lines(); + Self::from_proc_vec(&lines.map(|line| line.to_string()).collect::>()) + } + + pub fn from_proc_vec(proc_vec: &[String]) -> Result, DiskStatParseError> { + proc_vec + .iter() + .map(|line| line.parse::()) + .collect() + } +} diff --git a/src/uu/vmstat/src/picker.rs b/src/uu/vmstat/src/picker.rs index e2b4026..8f69cd4 100644 --- a/src/uu/vmstat/src/picker.rs +++ b/src/uu/vmstat/src/picker.rs @@ -4,9 +4,11 @@ // file that was distributed with this source code. #[cfg(target_os = "linux")] -use crate::{CpuLoad, CpuLoadRaw, Meminfo, ProcData}; +use crate::{CpuLoad, CpuLoadRaw, DiskStat, Meminfo, ProcData}; #[cfg(target_os = "linux")] use clap::ArgMatches; +#[cfg(target_os = "linux")] +use uucore::error::{UResult, USimpleError}; #[cfg(target_os = "linux")] pub type Picker = ( @@ -203,6 +205,61 @@ pub fn get_stats() -> Vec<(String, u64)> { ] } +#[cfg(target_os = "linux")] +pub fn get_disk_sum() -> UResult> { + let disk_data = DiskStat::current() + .map_err(|_| USimpleError::new(1, "Unable to retrieve disk statistics"))?; + + let mut disks = 0; + let mut partitions = 0; + let mut total_reads = 0; + let mut merged_reads = 0; + let mut read_sectors = 0; + let mut milli_reading = 0; + let mut writes = 0; + let mut merged_writes = 0; + let mut written_sectors = 0; + let mut milli_writing = 0; + let mut inprogress_io = 0; + let mut milli_spent_io = 0; + let mut milli_weighted_io = 0; + + for disk in disk_data.iter() { + if disk.is_disk() { + disks += 1; + total_reads += disk.reads_completed; + merged_reads += disk.reads_merged; + read_sectors += disk.sectors_read; + milli_reading += disk.milliseconds_spent_reading; + writes += disk.writes_completed; + merged_writes += disk.writes_merged; + written_sectors += disk.sectors_written; + milli_writing += disk.milliseconds_spent_writing; + inprogress_io += disk.ios_currently_in_progress; + milli_spent_io += disk.milliseconds_spent_doing_ios / 1000; + milli_weighted_io += disk.weighted_milliseconds_spent_doing_ios / 1000; + } else { + partitions += 1; + } + } + + Ok(vec![ + ("disks".to_string(), disks), + ("partitions".to_string(), partitions), + ("total reads".to_string(), total_reads), + ("merged reads".to_string(), merged_reads), + ("read sectors".to_string(), read_sectors), + ("milli reading".to_string(), milli_reading), + ("writes".to_string(), writes), + ("merged writes".to_string(), merged_writes), + ("written sectors".to_string(), written_sectors), + ("milli writing".to_string(), milli_writing), + ("in progress IO".to_string(), inprogress_io), + ("milli spent IO".to_string(), milli_spent_io), + ("milli weighted IO".to_string(), milli_weighted_io), + ]) +} + #[cfg(target_os = "linux")] fn with_unit(x: u64, arg: &ArgMatches) -> u64 { if let Some(unit) = arg.get_one::("unit") { diff --git a/src/uu/vmstat/src/vmstat.rs b/src/uu/vmstat/src/vmstat.rs index 75d11c8..5be6006 100644 --- a/src/uu/vmstat/src/vmstat.rs +++ b/src/uu/vmstat/src/vmstat.rs @@ -7,7 +7,7 @@ mod parser; mod picker; #[cfg(target_os = "linux")] -use crate::picker::{get_pickers, get_stats, Picker}; +use crate::picker::{get_disk_sum, get_pickers, get_stats, Picker}; use clap::value_parser; #[allow(unused_imports)] use clap::{arg, crate_version, ArgMatches, Command}; @@ -26,22 +26,31 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args)?; #[cfg(target_os = "linux")] { - if matches.get_flag("forks") { - return print_forks(); - } - if matches.get_flag("stats") { - return print_stats(); - } - + let wide = matches.get_flag("wide"); let one_header = matches.get_flag("one-header"); let no_first = matches.get_flag("no-first"); let term_height = terminal_size::terminal_size() .map(|size| size.1 .0) .unwrap_or(0); + if matches.get_flag("forks") { + return print_forks(); + } if matches.get_flag("slabs") { return print_slabs(one_header, term_height); } + if matches.get_flag("stats") { + return print_stats(); + } + if matches.get_flag("disk") { + return print_disk(wide, one_header, term_height); + } + if matches.get_flag("disk-sum") { + return print_disk_sum(); + } + if let Some(device) = matches.get_one::("partition") { + return print_partition(device); + } // validate unit if let Some(unit) = matches.get_one::("unit") { @@ -141,6 +150,110 @@ fn print_slab_header() { ); } +#[cfg(target_os = "linux")] +fn print_disk_header(wide: bool) { + if wide { + println!("disk- -------------------reads------------------- -------------------writes------------------ ------IO-------"); + println!( + "{:>15} {:>9} {:>11} {:>11} {:>9} {:>9} {:>11} {:>11} {:>7} {:>7}", + "total", "merged", "sectors", "ms", "total", "merged", "sectors", "ms", "cur", "sec" + ); + } else { + println!("disk- ------------reads------------ ------------writes----------- -----IO------"); + println!( + "{:>12} {:>6} {:>7} {:>7} {:>6} {:>6} {:>7} {:>7} {:>6} {:>6}", + "total", "merged", "sectors", "ms", "total", "merged", "sectors", "ms", "cur", "sec" + ); + } +} + +#[cfg(target_os = "linux")] +fn print_disk(wide: bool, one_header: bool, term_height: u16) -> UResult<()> { + let disk_data = DiskStat::current() + .map_err(|_| USimpleError::new(1, "Unable to retrieve disk statistics"))?; + + let mut line_count = 0; + + print_disk_header(wide); + + for disk in disk_data { + if !disk.is_disk() { + continue; + } + + if needs_header(one_header, term_height, line_count) { + print_disk_header(wide); + } + line_count += 1; + + if wide { + println!( + "{:<5} {:>9} {:>9} {:>11} {:>11} {:>9} {:>9} {:>11} {:>11} {:>7} {:>7}", + disk.device, + disk.reads_completed, + disk.reads_merged, + disk.sectors_read, + disk.milliseconds_spent_reading, + disk.writes_completed, + disk.writes_merged, + disk.sectors_written, + disk.milliseconds_spent_writing, + disk.ios_currently_in_progress / 1000, + disk.milliseconds_spent_doing_ios / 1000 + ); + } else { + println!( + "{:<5} {:>6} {:>6} {:>7} {:>7} {:>6} {:>6} {:>7} {:>7} {:>6} {:>6}", + disk.device, + disk.reads_completed, + disk.reads_merged, + disk.sectors_read, + disk.milliseconds_spent_reading, + disk.writes_completed, + disk.writes_merged, + disk.sectors_written, + disk.milliseconds_spent_writing, + disk.ios_currently_in_progress / 1000, + disk.milliseconds_spent_doing_ios / 1000 + ); + } + } + + Ok(()) +} + +#[cfg(target_os = "linux")] +fn print_disk_sum() -> UResult<()> { + let data = get_disk_sum()?; + + data.iter() + .for_each(|(name, value)| println!("{value:>13} {name}")); + + Ok(()) +} + +#[cfg(target_os = "linux")] +fn print_partition(device: &str) -> UResult<()> { + let disk_data = DiskStat::current() + .map_err(|_| USimpleError::new(1, "Unable to retrieve disk statistics"))?; + + let disk = disk_data + .iter() + .find(|disk| disk.device == device) + .ok_or_else(|| USimpleError::new(1, format!("Disk/Partition {device} not found")))?; + + println!( + "{device:<9} {:>11} {:>17} {:>11} {:>17}", + "reads", "read sectors", "writes", "requested writes" + ); + println!( + "{:>21} {:>17} {:>11} {:>17}", + disk.reads_completed, disk.sectors_read, disk.writes_completed, disk.sectors_written + ); + + Ok(()) +} + #[cfg(target_os = "linux")] fn print_header(pickers: &[Picker]) { let mut section: Vec<&str> = vec![]; @@ -191,15 +304,18 @@ pub fn uu_app() -> Command { .value_parser(value_parser!(u64)), arg!(-a --active "Display active and inactive memory"), arg!(-f --forks "switch displays the number of forks since boot") - .conflicts_with_all(["slabs", "stats", /*"disk", "disk-sum", "partition"*/]), + .conflicts_with_all(["slabs", "stats", "disk", "disk-sum", "partition"]), arg!(-m --slabs "Display slabinfo") - .conflicts_with_all(["forks", "stats", /*"disk", "disk-sum", "partition"*/]), + .conflicts_with_all(["forks", "stats", "disk", "disk-sum", "partition"]), 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") - .conflicts_with_all(["forks", "slabs", /*"disk", "disk-sum", "partition"*/]), - // arg!(-d --disk "Report disk statistics"), - // arg!(-D --"disk-sum" "Report some summary statistics about disk activity"), - // arg!(-p --partition "Detailed statistics about partition"), + .conflicts_with_all(["forks", "slabs", "disk", "disk-sum", "partition"]), + arg!(-d --disk "Report disk statistics") + .conflicts_with_all(["forks", "slabs", "stats", "disk-sum", "partition"]), + arg!(-D --"disk-sum" "Report some summary statistics about disk activity") + .conflicts_with_all(["forks", "slabs", "stats", "disk", "partition"]), + arg!(-p --partition "Detailed statistics about partition") + .conflicts_with_all(["forks", "slabs", "stats", "disk", "disk-sum"]), arg!(-S --unit "Switches outputs between 1000 (k), 1024 (K), 1000000 (m), or 1048576 (M) bytes"), arg!(-t --timestamp "Append timestamp to each line"), arg!(-w --wide "Wide output mode"), diff --git a/tests/by-util/test_vmstat.rs b/tests/by-util/test_vmstat.rs index 6ff26c3..544f087 100644 --- a/tests/by-util/test_vmstat.rs +++ b/tests/by-util/test_vmstat.rs @@ -92,3 +92,15 @@ fn test_timestamp() { fn test_stats() { new_ucmd!().arg("-s").succeeds(); } + +#[test] +#[cfg(target_os = "linux")] +fn test_disk() { + new_ucmd!().arg("-d").succeeds(); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_disk_sum() { + new_ucmd!().arg("-D").succeeds(); +} From fd9344f5c660de9252cacd7f71c039aa39230d89 Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Tue, 29 Jul 2025 19:19:15 +0800 Subject: [PATCH 22/40] snice: implement `--no-action` --- src/uu/snice/src/action.rs | 16 ++++++++++++---- src/uu/snice/src/snice.rs | 7 +++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/uu/snice/src/action.rs b/src/uu/snice/src/action.rs index 3fac9ad..0a25d16 100644 --- a/src/uu/snice/src/action.rs +++ b/src/uu/snice/src/action.rs @@ -111,7 +111,7 @@ impl Display for ActionResult { /// /// But we don't know if the process of pid are exist, if [None], the process doesn't exist #[cfg(target_os = "linux")] -fn set_priority(pid: u32, prio: &Priority) -> Option { +fn set_priority(pid: u32, prio: &Priority, take_action: bool) -> Option { use libc::{getpriority, setpriority, PRIO_PROCESS}; use nix::errno::Errno; @@ -136,6 +136,10 @@ fn set_priority(pid: u32, prio: &Priority) -> Option { prio }; + if !take_action { + return Some(ActionResult::Success); + } + let prio = match prio { Priority::Increase(prio) => current_priority + *prio as i32, Priority::Decrease(prio) => current_priority - *prio as i32, @@ -159,11 +163,15 @@ fn set_priority(pid: u32, prio: &Priority) -> Option { // TODO: Implemented this on other platform #[cfg(not(target_os = "linux"))] -fn set_priority(_pid: u32, _prio: &Priority) -> Option { +fn set_priority(_pid: u32, _prio: &Priority, _take_action: bool) -> Option { None } -pub(crate) fn perform_action(pids: &[u32], prio: &Priority) -> Vec> { - let f = |pid: &u32| set_priority(*pid, prio); +pub(crate) fn perform_action( + pids: &[u32], + prio: &Priority, + take_action: bool, +) -> Vec> { + let f = |pid: &u32| set_priority(*pid, prio, take_action); pids.iter().map(f).collect() } diff --git a/src/uu/snice/src/snice.rs b/src/uu/snice/src/snice.rs index cebf5df..caa7c9e 100644 --- a/src/uu/snice/src/snice.rs +++ b/src/uu/snice/src/snice.rs @@ -173,9 +173,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } // Case1: Perform priority + let take_action = !matches.get_flag("no-action"); if let Some(targets) = settings.expressions { let pids = collect_pids(&targets); - let results = perform_action(&pids, &settings.priority); + let results = perform_action(&pids, &settings.priority, take_action); if results.iter().all(|it| it.is_none()) || results.is_empty() { return Err(USimpleError::new(1, "no process selection criteria")); @@ -184,6 +185,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { if settings.verbose { let output = construct_verbose_result(&pids, &results).trim().to_owned(); println!("{output}"); + } else if !take_action { + pids.iter().for_each(|pid| println!("{pid}")); } } @@ -255,7 +258,7 @@ pub fn uu_app() -> Command { // arg!(-i --interactive "interactive"), arg!(-l --list "list all signal names"), arg!(-L --table "list all signal names in a nice table"), - // arg!(-n --"no-action" "do not actually kill processes; just print what would happen"), + arg!(-n --"no-action" "do not actually kill processes; just print what would happen"), arg!(-v --verbose "explain what is being done"), // arg!(-w --warnings "enable warnings (not implemented)"), // Expressions From 71db1f31957c60a7bd81918a5bb696c676b3fccb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 21:36:29 +0000 Subject: [PATCH 23/40] chore(deps): update rust crate clap to v4.5.43 --- Cargo.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 294cd98..358b870 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,18 +172,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" dependencies = [ "anstream", "anstyle", @@ -517,7 +517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -711,7 +711,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1237,7 +1237,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1250,7 +1250,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1439,7 +1439,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] From 33573cfa1982c49aa17f1eda731a7f1ab0818c22 Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Sat, 5 Jul 2025 17:23:45 +0800 Subject: [PATCH 24/40] pkill: implement `--queue` --- src/uu/pkill/src/pkill.rs | 58 +++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/src/uu/pkill/src/pkill.rs b/src/uu/pkill/src/pkill.rs index d4c96a7..8659433 100644 --- a/src/uu/pkill/src/pkill.rs +++ b/src/uu/pkill/src/pkill.rs @@ -4,7 +4,7 @@ // file that was distributed with this source code. // Pid utils -use clap::{arg, crate_version, Command}; +use clap::{arg, crate_version, value_parser, Command}; #[cfg(unix)] use nix::{ sys::signal::{self, Signal}, @@ -48,7 +48,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } else { let sig = (settings.signal as i32) .try_into() - .map_err(|e| std::io::Error::from_raw_os_error(e as i32))?; + .map_err(|e| Error::from_raw_os_error(e as i32))?; Some(sig) }; @@ -56,11 +56,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let pids = process_matcher::find_matching_pids(&settings)?; // Send signal - // TODO: Implement -q #[cfg(unix)] - let echo = matches.get_flag("echo"); - #[cfg(unix)] - kill(&pids, sig, echo); + { + let echo = matches.get_flag("echo"); + let queue = matches.get_one::("queue").cloned(); + + kill(&pids, sig, queue, echo); + } if matches.get_flag("count") { println!("{}", pids.len()); @@ -71,25 +73,50 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { #[cfg(unix)] fn handle_obsolete(args: &mut [String]) { - // Sanity check - if args.len() > 2 { - // Old signal can only be in the first argument position - let slice = args[1].as_str(); - if let Some(signal) = slice.strip_prefix('-') { + for arg in &mut args[1..] { + if let Some(signal) = arg.strip_prefix('-') { // Check if it is a valid signal let opt_signal = signal_by_name_or_value(signal); if opt_signal.is_some() { // Replace with long option that clap can parse - args[1] = format!("--signal={signal}"); + *arg = format!("--signal={signal}"); } } } } +// Not contains in libc +#[cfg(target_os = "linux")] +extern "C" { + fn sigqueue( + pid: uucore::libc::pid_t, + sig: uucore::libc::c_int, + val: uucore::libc::sigval, + ) -> uucore::libc::c_int; +} + #[cfg(unix)] -fn kill(pids: &Vec, sig: Option, echo: bool) { +#[allow(unused_variables)] +fn kill(pids: &Vec, sig: Option, queue: Option, echo: bool) { for pid in pids { - if let Err(e) = signal::kill(Pid::from_raw(pid.pid as i32), sig) { + #[cfg(target_os = "linux")] + let result = if let Some(queue) = queue { + let v = unsafe { + sigqueue( + pid.pid as i32, + sig.map_or(0, |s| s as uucore::libc::c_int), + uucore::libc::sigval { + sival_ptr: queue as usize as *mut uucore::libc::c_void, + }, + ) + }; + nix::errno::Errno::result(v).map(drop) + } else { + signal::kill(Pid::from_raw(pid.pid as i32), sig) + }; + #[cfg(not(target_os = "linux"))] + let result = signal::kill(Pid::from_raw(pid.pid as i32), sig); + if let Err(e) = result { show!(Error::from_raw_os_error(e as i32) .map_err_context(|| format!("killing pid {} failed", pid.pid))); } else if echo { @@ -111,7 +138,8 @@ pub fn uu_app() -> Command { .args_override_self(true) .args([ // arg!(- "signal to send (either number or name)"), - // arg!(-q --queue "integer value to be sent with the signal"), + arg!(-q --queue "integer value to be sent with the signal") + .value_parser(value_parser!(u32)), arg!(-e --echo "display what is killed"), ]) .args(process_matcher::clap_args( From 3d5bcda86b9be12b876e87bdf7920b27018e719c Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Fri, 1 Aug 2025 16:58:01 +0800 Subject: [PATCH 25/40] pkill: add test for `--queue` --- tests/by-util/test_pkill.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/by-util/test_pkill.rs b/tests/by-util/test_pkill.rs index 4828e90..54a4c17 100644 --- a/tests/by-util/test_pkill.rs +++ b/tests/by-util/test_pkill.rs @@ -74,3 +74,9 @@ fn test_too_long_pattern() { .code_is(1) .stderr_contains("pattern that searches for process name longer than 15 characters will result in zero matches"); } + +#[test] +#[cfg(target_os = "linux")] +fn test_invalid_queue() { + new_ucmd!().args(&["-q"]).fails().code_is(1); +} From a0e61a90c709d449777d2afd5c84fa3dde7a1ccc Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Mon, 14 Jul 2025 11:51:50 +0800 Subject: [PATCH 26/40] skill: add basic implementation --- Cargo.lock | 11 +++ Cargo.toml | 2 + src/uu/skill/Cargo.toml | 26 +++++ src/uu/skill/skill.md | 7 ++ src/uu/skill/src/main.rs | 1 + src/uu/skill/src/skill.rs | 106 ++++++++++++++++++++ src/uu/snice/src/action.rs | 4 +- src/uu/snice/src/priority.rs | 2 +- src/uu/snice/src/process_matcher.rs | 101 +++++++++++++++++++ src/uu/snice/src/snice.rs | 148 ++++++---------------------- tests/by-util/test_skill.rs | 13 +++ tests/tests.rs | 4 + 12 files changed, 304 insertions(+), 121 deletions(-) create mode 100644 src/uu/skill/Cargo.toml create mode 100644 src/uu/skill/skill.md create mode 100644 src/uu/skill/src/main.rs create mode 100644 src/uu/skill/src/skill.rs create mode 100644 src/uu/snice/src/process_matcher.rs create mode 100644 tests/by-util/test_skill.rs diff --git a/Cargo.lock b/Cargo.lock index 294cd98..7aa8e01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1068,6 +1068,7 @@ dependencies = [ "uu_pmap", "uu_ps", "uu_pwdx", + "uu_skill", "uu_slabtop", "uu_snice", "uu_sysctl", @@ -1740,6 +1741,16 @@ dependencies = [ "uucore", ] +[[package]] +name = "uu_skill" +version = "0.0.1" +dependencies = [ + "clap", + "nix", + "uu_snice", + "uucore", +] + [[package]] name = "uu_slabtop" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index b1621df..cdec2b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ feat_common_core = [ "pmap", "ps", "pwdx", + "skill", "slabtop", "snice", "sysctl", @@ -98,6 +99,7 @@ pkill = { optional = true, version = "0.0.1", package = "uu_pkill", path = "src/ pmap = { optional = true, version = "0.0.1", package = "uu_pmap", path = "src/uu/pmap" } ps = { optional = true, version = "0.0.1", package = "uu_ps", path = "src/uu/ps" } pwdx = { optional = true, version = "0.0.1", package = "uu_pwdx", path = "src/uu/pwdx" } +skill = { optional = true, version = "0.0.1", package = "uu_skill", path = "src/uu/skill" } slabtop = { optional = true, version = "0.0.1", package = "uu_slabtop", path = "src/uu/slabtop" } snice = { optional = true, version = "0.0.1", package = "uu_snice", path = "src/uu/snice" } sysctl = { optional = true, version = "0.0.1", package = "uu_sysctl", path = "src/uu/sysctl" } diff --git a/src/uu/skill/Cargo.toml b/src/uu/skill/Cargo.toml new file mode 100644 index 0000000..dedc7bf --- /dev/null +++ b/src/uu/skill/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "uu_skill" +description = "skill - (uutils) send a signal or report process status" +repository = "https://github.com/uutils/procps/tree/main/src/uu/skill" +authors.workspace = true +categories.workspace = true +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +version.workspace = true + +[dependencies] +uucore = { workspace = true, features = ["signals"] } +clap = { workspace = true } +nix = { workspace = true } + +uu_snice = { path = "../snice" } + + +[lib] +path = "src/skill.rs" + +[[bin]] +name = "skill" +path = "src/main.rs" diff --git a/src/uu/skill/skill.md b/src/uu/skill/skill.md new file mode 100644 index 0000000..8fdb454 --- /dev/null +++ b/src/uu/skill/skill.md @@ -0,0 +1,7 @@ +# skill + +``` +skill [signal] [options] +``` + +Report processes matching an expression and send a signal to them. \ No newline at end of file diff --git a/src/uu/skill/src/main.rs b/src/uu/skill/src/main.rs new file mode 100644 index 0000000..2e827bc --- /dev/null +++ b/src/uu/skill/src/main.rs @@ -0,0 +1 @@ +uucore::bin!(uu_skill); diff --git a/src/uu/skill/src/skill.rs b/src/uu/skill/src/skill.rs new file mode 100644 index 0000000..e1c47c6 --- /dev/null +++ b/src/uu/skill/src/skill.rs @@ -0,0 +1,106 @@ +// This file is part of the uutils procps package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use clap::{arg, crate_version, value_parser, Arg, Command}; +#[cfg(unix)] +use nix::{sys::signal, sys::signal::Signal, unistd::Pid}; +use uu_snice::{ + collect_pids, construct_verbose_result, print_signals, process_matcher, ActionResult, +}; +use uucore::error::USimpleError; +#[cfg(unix)] +use uucore::signals::signal_by_name_or_value; +use uucore::{error::UResult, format_usage, help_about, help_usage}; + +const ABOUT: &str = help_about!("skill.md"); +const USAGE: &str = help_usage!("skill.md"); + +#[uucore::main] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let matches = uu_app().try_get_matches_from(args)?; + let settings = process_matcher::Settings::try_new(&matches)?; + + // Case0: Print SIGNALS + if let Some(display) = &settings.display { + print_signals(display); + return Ok(()); + } + + // Case1: Send signal + if let Some(targets) = settings.expressions { + let pids = collect_pids(&targets); + + #[cfg(unix)] + let signal_str = matches.get_one::("signal").cloned(); + + #[cfg(unix)] + let signal = if let Some(sig) = signal_str { + (signal_by_name_or_value(sig.strip_prefix('-').unwrap()).unwrap() as i32).try_into()? + } else { + Signal::SIGTERM + }; + + #[cfg(unix)] + let results = perform_action(&pids, &signal); + #[cfg(not(unix))] + let results: Vec> = Vec::new(); + + if results.iter().all(|it| it.is_none()) || results.is_empty() { + return Err(USimpleError::new(1, "no process selection criteria")); + } + + if settings.verbose { + let output = construct_verbose_result(&pids, &results).trim().to_owned(); + println!("{output}"); + } + } + + Ok(()) +} + +#[cfg(unix)] +fn perform_action(pids: &[u32], signal: &Signal) -> Vec> { + pids.iter() + .map(|pid| { + { + Some(match signal::kill(Pid::from_raw(*pid as i32), *signal) { + Ok(_) => ActionResult::Success, + + Err(_) => ActionResult::PermissionDenied, + }) + } + }) + .collect() +} + +#[allow(clippy::cognitive_complexity)] +pub fn uu_app() -> Command { + Command::new(uucore::util_name()) + .version(crate_version!()) + .about(ABOUT) + .override_usage(format_usage(USAGE)) + .infer_long_args(true) + .arg_required_else_help(true) + .arg(Arg::new("signal")) + .args([ + // arg!(-f --fast "fast mode (not implemented)"), + // arg!(-i --interactive "interactive"), + arg!(-l --list "list all signal names"), + arg!(-L --table "list all signal names in a nice table"), + // arg!(-n --"no-action" "do not actually kill processes; just print what would happen"), + arg!(-v --verbose "explain what is being done"), + // arg!(-w --warnings "enable warnings (not implemented)"), + // Expressions + arg!(-c --command ... "expression is a command name"), + arg!(-p --pid ... "expression is a process id number") + .value_parser(value_parser!(u32)), + arg!(-t --tty ... "expression is a terminal"), + arg!(-u --user ... "expression is a username"), + // arg!(--ns "match the processes that belong to the same namespace as "), + // arg!(--nslist "list which namespaces will be considered for the --ns option.") + // .value_delimiter(',') + // .value_parser(["ipc", "mnt", "net", "pid", "user", "uts"]), + ]) +} diff --git a/src/uu/snice/src/action.rs b/src/uu/snice/src/action.rs index 0a25d16..dff7bd3 100644 --- a/src/uu/snice/src/action.rs +++ b/src/uu/snice/src/action.rs @@ -24,7 +24,7 @@ pub(crate) fn users() -> &'static Users { } #[derive(Debug)] -pub(crate) enum SelectedTarget { +pub enum SelectedTarget { Command(String), Pid(u32), Tty(Teletype), @@ -93,7 +93,7 @@ impl SelectedTarget { #[allow(unused)] #[derive(Debug, Clone)] -pub(crate) enum ActionResult { +pub enum ActionResult { PermissionDenied, Success, } diff --git a/src/uu/snice/src/priority.rs b/src/uu/snice/src/priority.rs index d87dd9a..889ebdb 100644 --- a/src/uu/snice/src/priority.rs +++ b/src/uu/snice/src/priority.rs @@ -13,7 +13,7 @@ pub enum Error { } #[derive(Debug, PartialEq, Eq)] -pub(crate) enum Priority { +pub enum Priority { // The default priority is +4. (snice +4 ...) Increase(u32), Decrease(u32), diff --git a/src/uu/snice/src/process_matcher.rs b/src/uu/snice/src/process_matcher.rs new file mode 100644 index 0000000..939d326 --- /dev/null +++ b/src/uu/snice/src/process_matcher.rs @@ -0,0 +1,101 @@ +// This file is part of the uutils procps package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use crate::action::SelectedTarget; +use crate::SignalDisplay; +use clap::{arg, value_parser, Arg, ArgMatches}; +use uu_pgrep::process::Teletype; +use uucore::error::UResult; + +#[derive(Debug)] +pub struct Settings { + pub display: Option, + pub expressions: Option>, + pub verbose: bool, +} + +impl Settings { + pub fn try_new(matches: &ArgMatches) -> UResult { + let display = if matches.get_flag("table") { + Some(SignalDisplay::Table) + } else if matches.get_flag("list") { + Some(SignalDisplay::List) + } else { + None + }; + + Ok(Self { + display, + expressions: Self::targets(matches), + verbose: matches.get_flag("verbose"), + }) + } + + fn targets(matches: &ArgMatches) -> Option> { + let cmd = matches + .get_many::("command") + .unwrap_or_default() + .map(Into::into) + .map(SelectedTarget::Command) + .collect::>(); + + let pid = matches + .get_many::("pid") + .unwrap_or_default() + .map(Clone::clone) + .map(SelectedTarget::Pid) + .collect::>(); + + let tty = matches + .get_many::("tty") + .unwrap_or_default() + .flat_map(|it| Teletype::try_from(it.as_str())) + .map(SelectedTarget::Tty) + .collect::>(); + + let user = matches + .get_many::("user") + .unwrap_or_default() + .map(Into::into) + .map(SelectedTarget::User) + .collect::>(); + + let collected = cmd + .into_iter() + .chain(pid) + .chain(tty) + .chain(user) + .collect::>(); + + if collected.is_empty() { + None + } else { + Some(collected) + } + } +} + +#[allow(clippy::cognitive_complexity)] +pub fn clap_args() -> Vec { + vec![ + // arg!(-f --fast "fast mode (not implemented)"), + // arg!(-i --interactive "interactive"), + arg!(-l --list "list all signal names"), + arg!(-L --table "list all signal names in a nice table"), + arg!(-n --"no-action" "do not actually kill processes; just print what would happen"), + arg!(-v --verbose "explain what is being done"), + // arg!(-w --warnings "enable warnings (not implemented)"), + // Expressions + arg!(-c --command ... "expression is a command name"), + arg!(-p --pid ... "expression is a process id number") + .value_parser(value_parser!(u32)), + arg!(-t --tty ... "expression is a terminal"), + arg!(-u --user ... "expression is a username"), + // arg!(--ns "match the processes that belong to the same namespace as "), + // arg!(--nslist "list which namespaces will be considered for the --ns option.") + // .value_delimiter(',') + // .value_parser(["ipc", "mnt", "net", "pid", "user", "uts"]), + ] +} diff --git a/src/uu/snice/src/snice.rs b/src/uu/snice/src/snice.rs index caa7c9e..5bb0c06 100644 --- a/src/uu/snice/src/snice.rs +++ b/src/uu/snice/src/snice.rs @@ -5,12 +5,14 @@ use std::{collections::HashSet, path::PathBuf, str::FromStr}; -use action::{perform_action, process_snapshot, users, ActionResult, SelectedTarget}; -use clap::{arg, crate_version, value_parser, Arg, ArgMatches, Command}; +use crate::priority::Priority; +pub use action::ActionResult; +use action::{perform_action, process_snapshot, users, SelectedTarget}; +use clap::{crate_version, Arg, Command}; use prettytable::{format::consts::FORMAT_CLEAN, row, Table}; -use priority::Priority; +use process_matcher::*; use sysinfo::Pid; -use uu_pgrep::process::{ProcessInformation, Teletype}; +use uu_pgrep::process::ProcessInformation; #[cfg(target_family = "unix")] use uucore::signals::ALL_SIGNALS; use uucore::{ @@ -23,9 +25,10 @@ const USAGE: &str = help_usage!("snice.md"); mod action; mod priority; +pub mod process_matcher; #[derive(Debug)] -enum SignalDisplay { +pub enum SignalDisplay { List, Table, } @@ -66,85 +69,13 @@ impl SignalDisplay { } } -#[derive(Debug)] -struct Settings { - display: Option, - expressions: Option>, - priority: Priority, - verbose: bool, -} +#[allow(unused)] // unused argument under non-unix targets +pub fn print_signals(display: &SignalDisplay) { + #[cfg(target_family = "unix")] + { + let result = display.display(&ALL_SIGNALS); -impl Settings { - fn try_new(matches: &ArgMatches) -> UResult { - let priority = matches - .try_get_one::("priority") - .unwrap_or(Some(&String::new())) - .cloned(); - - let expression = match priority { - Some(expr) => { - Priority::try_from(expr).map_err(|err| USimpleError::new(1, err.to_string()))? - } - None => Priority::default(), - }; - - let display = if matches.get_flag("table") { - Some(SignalDisplay::Table) - } else if matches.get_flag("list") { - Some(SignalDisplay::List) - } else { - None - }; - - Ok(Self { - display, - expressions: Self::targets(matches), - priority: expression, - verbose: matches.get_flag("verbose"), - }) - } - - fn targets(matches: &ArgMatches) -> Option> { - let cmd = matches - .get_many::("command") - .unwrap_or_default() - .map(Into::into) - .map(SelectedTarget::Command) - .collect::>(); - - let pid = matches - .get_many::("pid") - .unwrap_or_default() - .map(Clone::clone) - .map(SelectedTarget::Pid) - .collect::>(); - - let tty = matches - .get_many::("tty") - .unwrap_or_default() - .flat_map(|it| Teletype::try_from(it.as_str())) - .map(SelectedTarget::Tty) - .collect::>(); - - let user = matches - .get_many::("user") - .unwrap_or_default() - .map(Into::into) - .map(SelectedTarget::User) - .collect::>(); - - let collected = cmd - .into_iter() - .chain(pid) - .chain(tty) - .chain(user) - .collect::>(); - - if collected.is_empty() { - None - } else { - Some(collected) - } + println!("{result}"); } } @@ -155,28 +86,25 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let settings = Settings::try_new(&matches)?; // Case0: Print SIGNALS - #[cfg(target_family = "unix")] - { - if let Some(display) = settings.display { - let result = display.display(&ALL_SIGNALS); - - println!("{result}"); - return Ok(()); - } - } - - #[cfg(not(target_family = "unix"))] - { - if let Some(_display) = settings.display { - return Ok(()); - } + if let Some(display) = &settings.display { + print_signals(display); + return Ok(()); } // Case1: Perform priority let take_action = !matches.get_flag("no-action"); if let Some(targets) = settings.expressions { + let priority_str = matches.get_one::("priority").cloned(); + + let priority = match priority_str { + Some(expr) => { + Priority::try_from(expr).map_err(|err| USimpleError::new(1, err.to_string()))? + } + None => Priority::default(), + }; + let pids = collect_pids(&targets); - let results = perform_action(&pids, &settings.priority, take_action); + let results = perform_action(&pids, &priority, take_action); if results.iter().all(|it| it.is_none()) || results.is_empty() { return Err(USimpleError::new(1, "no process selection criteria")); @@ -194,7 +122,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } #[allow(unused)] -fn construct_verbose_result(pids: &[u32], action_results: &[Option]) -> String { +pub fn construct_verbose_result(pids: &[u32], action_results: &[Option]) -> String { let mut table = action_results .iter() .enumerate() @@ -232,7 +160,7 @@ fn construct_verbose_result(pids: &[u32], action_results: &[Option } /// Map and sort `SelectedTarget` to pids. -fn collect_pids(targets: &[SelectedTarget]) -> Vec { +pub fn collect_pids(targets: &[SelectedTarget]) -> Vec { let collected = targets .iter() .flat_map(SelectedTarget::to_pids) @@ -243,7 +171,6 @@ fn collect_pids(targets: &[SelectedTarget]) -> Vec { collected } -#[allow(clippy::cognitive_complexity)] pub fn uu_app() -> Command { Command::new(uucore::util_name()) .version(crate_version!()) @@ -252,22 +179,7 @@ pub fn uu_app() -> Command { .infer_long_args(true) .arg_required_else_help(true) .arg(Arg::new("priority")) - .args([ - // Options - // arg!(-f --fast "fast mode (not implemented)"), - // arg!(-i --interactive "interactive"), - arg!(-l --list "list all signal names"), - arg!(-L --table "list all signal names in a nice table"), - arg!(-n --"no-action" "do not actually kill processes; just print what would happen"), - arg!(-v --verbose "explain what is being done"), - // arg!(-w --warnings "enable warnings (not implemented)"), - // Expressions - arg!(-c --command ... "expression is a command name"), - arg!(-p --pid ... "expression is a process id number") - .value_parser(value_parser!(u32)), - arg!(-t --tty ... "expression is a terminal"), - arg!(-u --user ... "expression is a username"), - ]) + .args(clap_args()) } #[cfg(test)] diff --git a/tests/by-util/test_skill.rs b/tests/by-util/test_skill.rs new file mode 100644 index 0000000..a9e5f16 --- /dev/null +++ b/tests/by-util/test_skill.rs @@ -0,0 +1,13 @@ +// This file is part of the uutils procps package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; + +#[test] +fn test_no_args() { + new_ucmd!().fails().code_is(1); +} diff --git a/tests/tests.rs b/tests/tests.rs index 6078611..8df7027 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -63,6 +63,10 @@ mod test_top; #[path = "by-util/test_vmstat.rs"] mod test_vmstat; +#[cfg(feature = "skill")] +#[path = "by-util/test_skill.rs"] +mod test_skill; + #[cfg(feature = "snice")] #[path = "by-util/test_snice.rs"] mod test_snice; From 21ed361c65b11691035e183f55d9a6e145dd3bc0 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Thu, 7 Aug 2025 17:14:11 +0200 Subject: [PATCH 27/40] pmap: fix warnings from identity_op lint in tests --- src/uu/pmap/src/maps_format_parser.rs | 46 +++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/uu/pmap/src/maps_format_parser.rs b/src/uu/pmap/src/maps_format_parser.rs index c1418ca..7572970 100644 --- a/src/uu/pmap/src/maps_format_parser.rs +++ b/src/uu/pmap/src/maps_format_parser.rs @@ -378,27 +378,27 @@ mod test { limit_address_range_and_assert(&address, 0x0, low - 1, false); limit_address_range_and_assert(&address, low - 1, low - 1, false); - limit_address_range_and_assert(&address, 0x0, low + 0, true); - limit_address_range_and_assert(&address, low - 1, low + 0, true); - limit_address_range_and_assert(&address, low + 0, low + 0, true); + limit_address_range_and_assert(&address, 0x0, low, true); + limit_address_range_and_assert(&address, low - 1, low, true); + limit_address_range_and_assert(&address, low, low, true); limit_address_range_and_assert(&address, low - 1, high - 1, true); - limit_address_range_and_assert(&address, low - 1, high + 0, true); + limit_address_range_and_assert(&address, low - 1, high, true); limit_address_range_and_assert(&address, low - 1, high + 1, true); - limit_address_range_and_assert(&address, low + 0, high - 1, true); - limit_address_range_and_assert(&address, low + 0, high + 0, true); - limit_address_range_and_assert(&address, low + 0, high + 1, true); + limit_address_range_and_assert(&address, low, high - 1, true); + limit_address_range_and_assert(&address, low, high, true); + limit_address_range_and_assert(&address, low, high + 1, true); limit_address_range_and_assert(&address, low + 1, high - 1, true); - limit_address_range_and_assert(&address, low + 1, high + 0, true); + limit_address_range_and_assert(&address, low + 1, high, true); limit_address_range_and_assert(&address, low + 1, high + 1, true); limit_address_range_and_assert(&address, high - 1, high - 1, true); - limit_address_range_and_assert(&address, high - 1, high + 0, true); + limit_address_range_and_assert(&address, high - 1, high, true); limit_address_range_and_assert(&address, high - 1, u64::MAX, true); - limit_address_range_and_assert(&address, high + 0, high + 0, false); - limit_address_range_and_assert(&address, high + 0, high + 1, false); - limit_address_range_and_assert(&address, high + 0, u64::MAX, false); + limit_address_range_and_assert(&address, high, high, false); + limit_address_range_and_assert(&address, high, high + 1, false); + limit_address_range_and_assert(&address, high, u64::MAX, false); limit_address_range_and_assert(&address, 0xffffffffffff, u64::MAX, false); limit_address_range_and_assert(&address, u64::MAX, u64::MAX, false); @@ -411,24 +411,24 @@ mod test { limit_address_range_and_assert(&address, low - 1, 0x0, false); limit_address_range_and_assert(&address, low - 1, low - 1, false); - limit_address_range_and_assert(&address, low + 0, 0x0, false); - limit_address_range_and_assert(&address, low + 0, low - 1, false); + limit_address_range_and_assert(&address, low, 0x0, false); + limit_address_range_and_assert(&address, low, low - 1, false); limit_address_range_and_assert(&address, high - 1, low - 1, false); - limit_address_range_and_assert(&address, high + 0, low - 1, false); + limit_address_range_and_assert(&address, high, low - 1, false); limit_address_range_and_assert(&address, high + 1, low - 1, false); - limit_address_range_and_assert(&address, high - 1, low + 0, true); // true - limit_address_range_and_assert(&address, high + 0, low + 0, false); - limit_address_range_and_assert(&address, high + 1, low + 0, false); - limit_address_range_and_assert(&address, high - 1, low + 1, true); // true - limit_address_range_and_assert(&address, high + 0, low + 1, false); + limit_address_range_and_assert(&address, high - 1, low, true); + limit_address_range_and_assert(&address, high, low, false); + limit_address_range_and_assert(&address, high + 1, low, false); + limit_address_range_and_assert(&address, high - 1, low + 1, true); + limit_address_range_and_assert(&address, high, low + 1, false); limit_address_range_and_assert(&address, high + 1, low + 1, false); - limit_address_range_and_assert(&address, high + 0, high - 1, false); + limit_address_range_and_assert(&address, high, high - 1, false); limit_address_range_and_assert(&address, u64::MAX, high - 1, false); - limit_address_range_and_assert(&address, high + 1, high + 0, false); - limit_address_range_and_assert(&address, u64::MAX, high + 0, false); + limit_address_range_and_assert(&address, high + 1, high, false); + limit_address_range_and_assert(&address, u64::MAX, high, false); limit_address_range_and_assert(&address, u64::MAX, 0xffffffffffff, false); } From ab32a293ffa13890166e70ebd667ececde352612 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 01:32:32 +0000 Subject: [PATCH 28/40] chore(deps): update rust crate clap_complete to v4.5.56 --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 358b870..68e1d3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -194,9 +194,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.55" +version = "4.5.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a" +checksum = "67e4efcbb5da11a92e8a609233aa1e8a7d91e38de0be865f016d14700d45a7fd" dependencies = [ "clap", ] @@ -517,7 +517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -711,7 +711,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1237,7 +1237,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1250,7 +1250,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1439,7 +1439,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] From c6fb36ef74d2c513ae0a6e61d94c9d0d7eb24828 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 8 Aug 2025 16:28:43 +0200 Subject: [PATCH 29/40] ci: enable separate test for skill --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2dffd3a..e26cef1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,8 +52,8 @@ jobs: - name: build and test all programs separately shell: bash run: | - ## TODO: add hugetop and skill - programs="free pgrep pidof pidwait pkill pmap ps pwdx slabtop snice sysctl tload top vmstat w watch" + ## TODO: add hugetop + programs="free pgrep pidof pidwait pkill pmap ps pwdx skill slabtop snice sysctl tload top vmstat w watch" for program in $programs; do echo "Building and testing $program" cargo test -p "uu_$program" || exit 1 From bb1c7987ed5370b06267cfe65c469b1fd523cad4 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 8 Aug 2025 16:29:21 +0200 Subject: [PATCH 30/40] Readme: move skill from TODO list to Ongoing list --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 95edecb..307b0c4 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Ongoing: * `pmap`: Displays the memory map of a process. * `ps`: Displays information about active processes. * `pwdx`: Shows the current working directory of a process. +* `skill`: Sends a signal to processes based on criteria like user, terminal, etc. * `slabtop`: Displays detailed kernel slab cache information in real time. * `snice`: Changes the scheduling priority of a running process. * `sysctl`: Read or write kernel parameters at run-time. @@ -31,7 +32,6 @@ Ongoing: TODO: * `hugetop`: Report hugepage usage of processes and the system as a whole. -* `skill`: Sends a signal to processes based on criteria like user, terminal, etc. Elsewhere: From 583906987cd34a9ae9b182e7d903efd02a6cf156 Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Wed, 6 Aug 2025 15:47:54 +0800 Subject: [PATCH 31/40] top: implement `PR` `NI` The old PR impl is exactly NI impl --- src/uu/top/src/picker.rs | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/uu/top/src/picker.rs b/src/uu/top/src/picker.rs index d2c7575..448f801 100644 --- a/src/uu/top/src/picker.rs +++ b/src/uu/top/src/picker.rs @@ -26,6 +26,7 @@ pub(crate) fn pickers(fields: &[String]) -> Vec String>> { "PID" => helper(pid), "USER" => helper(user), "PR" => helper(pr), + "NI" => helper(ni), "RES" => helper(res), "SHR" => helper(shr), "S" => helper(s), @@ -76,11 +77,39 @@ fn user(pid: u32) -> String { .to_string() } -#[cfg(not(target_os = "windows"))] +#[cfg(target_os = "linux")] fn pr(pid: u32) -> String { + use uucore::libc::*; + let policy = unsafe { sched_getscheduler(pid as i32) }; + if policy == -1 { + return String::new(); + } + + // normal processes + if policy == SCHED_OTHER || policy == SCHED_BATCH || policy == SCHED_IDLE { + return (get_nice(pid) + 20).to_string(); + } + + // real-time processes + let mut param = sched_param { sched_priority: 0 }; + unsafe { sched_getparam(pid as c_int, &mut param) }; + if param.sched_priority == -1 { + return String::new(); + } + param.sched_priority.to_string() +} + +#[cfg(not(target_os = "linux"))] +fn pr(pid: u32) -> String { + todo(pid) +} + +#[cfg(not(target_os = "windows"))] +fn get_nice(pid: u32) -> i32 { use libc::{getpriority, PRIO_PROCESS}; use nix::errno::Errno; + // this is nice value, not priority value let result = unsafe { getpriority(PRIO_PROCESS, pid) }; let result = if Errno::last() == Errno::UnknownErrno { @@ -90,12 +119,17 @@ fn pr(pid: u32) -> String { 0 }; - format!("{result}") + result as i32 +} + +#[cfg(not(target_os = "windows"))] +fn ni(pid: u32) -> String { + format!("{}", get_nice(pid)) } // TODO: Implement this function for Windows #[cfg(target_os = "windows")] -fn pr(_pid: u32) -> String { +fn ni(_pid: u32) -> String { "0".into() } From 99cf7680436e12fc69d30b1362fca5836fd1dab5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 03:42:18 +0000 Subject: [PATCH 32/40] chore(deps): update rust crate sysinfo to 0.37.0 --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ee2579..a5e8208 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1418,9 +1418,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.36.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d" +checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753" dependencies = [ "libc", "memchr", @@ -2018,7 +2018,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index cdec2b3..4590e70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,7 +68,7 @@ prettytable-rs = "0.10.0" rand = { version = "0.9.0", features = ["small_rng"] } ratatui = "0.29.0" regex = "1.10.4" -sysinfo = "0.36.0" +sysinfo = "0.37.0" tempfile = "3.10.1" terminal_size = "0.4.2" textwrap = { version = "0.16.1", features = ["terminal_size"] } From 4aac4f3cf9924365a6df63c8814d9d21f60b7f3d Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 11 Aug 2025 09:44:13 +0200 Subject: [PATCH 33/40] Bump ctor from 0.4.1 to 0.5.0 --- Cargo.lock | 33 +++++++++++++++++++++++++++++---- Cargo.toml | 2 +- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5e8208..9bd1094 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -323,7 +323,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec09e802f5081de6157da9a75701d6c713d8dc3ba52571fd4bd25f412644e8a6" dependencies = [ "ctor-proc-macro", - "dtor", + "dtor 0.0.6", +] + +[[package]] +name = "ctor" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" +dependencies = [ + "ctor-proc-macro", + "dtor 0.1.0", ] [[package]] @@ -483,7 +493,16 @@ version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97cbdf2ad6846025e8e25df05171abfb30e3ababa12ee0a0e44b9bbe570633a8" dependencies = [ - "dtor-proc-macro", + "dtor-proc-macro 0.0.5", +] + +[[package]] +name = "dtor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e58a0764cddb55ab28955347b45be00ade43d4d6f3ba4bf3dc354e4ec9432934" +dependencies = [ + "dtor-proc-macro 0.0.6", ] [[package]] @@ -492,6 +511,12 @@ version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055" +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + [[package]] name = "either" version = "1.15.0" @@ -1049,7 +1074,7 @@ dependencies = [ "clap", "clap_complete", "clap_mangen", - "ctor", + "ctor 0.5.0", "libc", "phf", "phf_codegen", @@ -1890,7 +1915,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12453ee52c9cffa6bf2c74f9f35ed0c824b846cb0b4bdee829d8150332e7204c" dependencies = [ - "ctor", + "ctor 0.4.3", "glob", "libc", "nix", diff --git a/Cargo.toml b/Cargo.toml index 4590e70..b714444 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ clap = { version = "4.5.4", features = ["wrap_help", "cargo", "env"] } clap_complete = "4.5.2" clap_mangen = "0.2.20" crossterm = "0.29.0" -ctor = "0.4.1" +ctor = "0.5.0" dirs = "6.0.0" libc = "0.2.154" nix = { version = "0.30", default-features = false, features = ["process"] } From 25e50dba0da48768985aab42e4c28def8406c0c7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 10:36:40 +0000 Subject: [PATCH 34/40] chore(deps): update rust crate libc to v0.2.175 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5e8208..2098f32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -753,9 +753,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libredox" From 86ae4940980fe76a0cf27a67de6e87c300966728 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:05:06 +0000 Subject: [PATCH 35/40] chore(deps): update actions/checkout action to v5 --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/code-quality.yml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e26cef1..90f4197 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@stable - if: ${{ contains(matrix.os, 'ubuntu') }} run: | @@ -28,7 +28,7 @@ jobs: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@stable - if: ${{ contains(matrix.os, 'ubuntu') }} run: | @@ -43,7 +43,7 @@ jobs: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@stable - if: ${{ contains(matrix.os, 'ubuntu') }} run: | @@ -70,7 +70,7 @@ jobs: - { os: macos-latest , features: macos } - { os: windows-latest , features: windows } steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Initialize workflow variables id: vars shell: bash diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 89744ba..8237c54 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -25,7 +25,7 @@ jobs: name: cargo fmt --all -- --check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - run: rustup component add rustfmt @@ -45,7 +45,7 @@ jobs: - { os: macos-latest } - { os: windows-latest } steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@master with: toolchain: stable From 8a86a7eb613a516ce334deafeed5ab5b3fbf6a69 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 20:43:47 +0000 Subject: [PATCH 36/40] chore(deps): update rust crate clap to v4.5.44 --- Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2098f32..944fe91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,18 +172,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.43" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" +checksum = "1c1f056bae57e3e54c3375c41ff79619ddd13460a17d7438712bd0d83fda4ff8" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.43" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" dependencies = [ "anstream", "anstyle", @@ -517,7 +517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -711,7 +711,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1238,7 +1238,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1251,7 +1251,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1440,7 +1440,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2018,7 +2018,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] From 589288f10ccc6de38ff8c59736763970863243cd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 20:43:52 +0000 Subject: [PATCH 37/40] chore(deps): update rust crate terminal_size to v0.4.3 --- Cargo.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2098f32..a6b9e7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -517,7 +517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -711,7 +711,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1238,7 +1238,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1251,7 +1251,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1440,7 +1440,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1456,12 +1456,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" +checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -2018,7 +2018,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] From 6ec057226495a561d4dc9a30b1087852257ce79e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 04:48:24 +0000 Subject: [PATCH 38/40] chore(deps): update rust crate clap_complete to v4.5.57 --- Cargo.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2098f32..026206d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -194,9 +194,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.56" +version = "4.5.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e4efcbb5da11a92e8a609233aa1e8a7d91e38de0be865f016d14700d45a7fd" +checksum = "4d9501bd3f5f09f7bbee01da9a511073ed30a80cd7a509f1214bb74eadea71ad" dependencies = [ "clap", ] @@ -517,7 +517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -711,7 +711,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1238,7 +1238,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1251,7 +1251,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1440,7 +1440,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2018,7 +2018,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] From 758c9c32963550e3c552660e50942a3509307ae7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 04:48:29 +0000 Subject: [PATCH 39/40] chore(deps): update rust crate thiserror to v2.0.14 --- Cargo.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2098f32..f40b427 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -568,7 +568,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198" dependencies = [ "memchr", - "thiserror 2.0.12", + "thiserror 2.0.14", ] [[package]] @@ -1175,7 +1175,7 @@ checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ "getrandom 0.2.15", "libredox", - "thiserror 2.0.12", + "thiserror 2.0.14", ] [[package]] @@ -1487,11 +1487,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.14", ] [[package]] @@ -1507,9 +1507,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" dependencies = [ "proc-macro2", "quote", @@ -1768,7 +1768,7 @@ dependencies = [ "nix", "prettytable-rs", "sysinfo", - "thiserror 2.0.12", + "thiserror 2.0.14", "uu_pgrep", "uucore", ] @@ -1858,7 +1858,7 @@ dependencies = [ "nix", "number_prefix", "os_display", - "thiserror 2.0.12", + "thiserror 2.0.14", "time", "unic-langid", "utmp-classic", From 2e7502e77e4a370f929be8155d32fed826665646 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 19:26:31 +0000 Subject: [PATCH 40/40] chore(deps): update rust crate clap to v4.5.45 --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12347d6..f0a7063 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,9 +172,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.44" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1f056bae57e3e54c3375c41ff79619ddd13460a17d7438712bd0d83fda4ff8" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" dependencies = [ "clap_builder", ] @@ -517,7 +517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -711,7 +711,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1238,7 +1238,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1251,7 +1251,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1440,7 +1440,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]]