From cbae89f56cd4ce9f44a4af0a3405fd2549e23a90 Mon Sep 17 00:00:00 2001 From: Navid Yaghoobi Date: Sat, 17 Aug 2024 18:08:46 +1000 Subject: [PATCH 1/3] added /proc//cmdline Signed-off-by: Navid Yaghoobi --- FEATURES.md | 1 + src/proc.rs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/FEATURES.md b/FEATURES.md index f92028a..4c6854b 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -3,6 +3,7 @@ Supported Features * comm * exe * cwd + * cmdline * root * ✅ `/proc/buddyinfo` diff --git a/src/proc.rs b/src/proc.rs index 9ad09df..3146198 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -51,6 +51,23 @@ impl Proc { } } + /// returns the command line of a process + pub fn cmdline(&self) -> CollectResult> { + match utils::collect_info_string("cmdline", &self.path) { + Ok(c) => { + let proc_cmdline = c + .unwrap_or_default() + .trim_end_matches("\x00") + .split("\x00") + .map(|v| v.to_string()) + .collect::>(); + + Ok(proc_cmdline) + } + Err(err) => Err(err), + } + } + /// returns the absolute path of the executable command of a process pub fn executable(&self) -> CollectResult { let mut proc_path = self.path.clone(); @@ -194,6 +211,7 @@ mod tests { PathBuf::from("/usr/bin/vim"), ); assert_eq!(sys_single_proc.root_dir().unwrap(), PathBuf::from("/")); + assert_eq!(sys_single_proc.cmdline().unwrap(), ["vim", "test.go", "+10"] ); let sys_single_proc = collect_from(proc_path, 26232).expect("running proc 26232"); assert_eq!(sys_single_proc.cwd().is_err(), true); From 3ac6bcd60278aa891e033d08f2126e2eeb0f327a Mon Sep 17 00:00:00 2001 From: Navid Yaghoobi Date: Sat, 17 Aug 2024 18:28:01 +1000 Subject: [PATCH 2/3] added /proc//environ Signed-off-by: Navid Yaghoobi --- FEATURES.md | 1 + src/proc.rs | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/FEATURES.md b/FEATURES.md index 4c6854b..c741748 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -2,6 +2,7 @@ Supported Features * ✅ `/proc/` * comm * exe + * environ * cwd * cmdline * root diff --git a/src/proc.rs b/src/proc.rs index 3146198..e67588d 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -68,6 +68,23 @@ impl Proc { } } + /// returns the process environments from `/proc//environ` + pub fn environ(&self) -> CollectResult> { + match utils::collect_info_string("environ", &self.path) { + Ok(c) => { + let proc_cmdline = c + .unwrap_or_default() + .trim_end_matches("\x00") + .split("\x00") + .map(|v| v.to_string()) + .collect::>(); + + Ok(proc_cmdline) + } + Err(err) => Err(err), + } + } + /// returns the absolute path of the executable command of a process pub fn executable(&self) -> CollectResult { let mut proc_path = self.path.clone(); @@ -211,7 +228,14 @@ mod tests { PathBuf::from("/usr/bin/vim"), ); assert_eq!(sys_single_proc.root_dir().unwrap(), PathBuf::from("/")); - assert_eq!(sys_single_proc.cmdline().unwrap(), ["vim", "test.go", "+10"] ); + assert_eq!( + sys_single_proc.cmdline().unwrap(), + ["vim", "test.go", "+10"], + ); + assert_eq!( + sys_single_proc.environ().unwrap(), + ["PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "HOSTNAME=cd24e11f73a5", "TERM=xterm", "GOLANG_VERSION=1.12.5", "GOPATH=/go", "HOME=/root"], + ); let sys_single_proc = collect_from(proc_path, 26232).expect("running proc 26232"); assert_eq!(sys_single_proc.cwd().is_err(), true); From fa86f9c155ccbb7c8ab762aa21be74caa097f8f5 Mon Sep 17 00:00:00 2001 From: Navid Yaghoobi Date: Sat, 17 Aug 2024 19:04:01 +1000 Subject: [PATCH 3/3] added /proc//io Signed-off-by: Navid Yaghoobi --- FEATURES.md | 7 +-- examples/proc_io.rs | 14 ++++++ src/lib.rs | 1 + src/proc.rs | 4 ++ src/proc_io.rs | 113 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 examples/proc_io.rs create mode 100644 src/proc_io.rs diff --git a/FEATURES.md b/FEATURES.md index c741748..2f1b244 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -1,10 +1,11 @@ Supported Features * ✅ `/proc/` + * cmdline * comm - * exe - * environ * cwd - * cmdline + * environ + * exe + * io * root * ✅ `/proc/buddyinfo` diff --git a/examples/proc_io.rs b/examples/proc_io.rs new file mode 100644 index 0000000..4e29a46 --- /dev/null +++ b/examples/proc_io.rs @@ -0,0 +1,14 @@ +use procsys::proc; + +fn main() { + let sys_proc = proc::collect(2).expect("system proc 2"); + let sys_proc_io = sys_proc.io().expect("system proc 2 io"); + + match serde_json::to_string_pretty(&sys_proc_io) { + Ok(output) => println!("{}", output), + Err(err) => { + log::error!("{}", err); + std::process::exit(1); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index ad199e6..3b6a602 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ pub mod meminfo; pub mod net_dev; pub mod net_protocols; pub mod proc; +pub mod proc_io; pub mod swaps; pub mod sysfs; pub mod utils; diff --git a/src/proc.rs b/src/proc.rs index e67588d..9877e75 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -26,6 +26,10 @@ impl Proc { self.id } + pub fn path(&self) -> PathBuf { + self.path.clone() + } + /// returns the command name of a process pub fn comm(&self) -> CollectResult { match utils::collect_info_string("comm", &self.path) { diff --git a/src/proc_io.rs b/src/proc_io.rs new file mode 100644 index 0000000..5ba10a1 --- /dev/null +++ b/src/proc_io.rs @@ -0,0 +1,113 @@ +use serde::Serialize; + +use crate::{ + error::{CollectResult, MetricError}, + proc::Proc, + utils, +}; + +enum ProcIOType { + RChar, + WChar, + SyscR, + SyscW, + ReadBytes, + WriteBytes, + CancelledWriteBytes, + Unknown, +} + +impl ProcIOType { + fn from(name: &str) -> ProcIOType { + match name { + "rchar" => ProcIOType::RChar, + "wchar" => ProcIOType::WChar, + "syscr" => ProcIOType::SyscR, + "syscw" => ProcIOType::SyscW, + "read_bytes" => ProcIOType::ReadBytes, + "write_bytes" => ProcIOType::WriteBytes, + "cancelled_write_bytes" => ProcIOType::CancelledWriteBytes, + _ => ProcIOType::Unknown, + } + } +} + +/// ProcIO models the content of /proc/\/io +#[derive(Debug, Serialize, Clone, Default)] +pub struct ProcIO { + pub rchar: u64, + pub wchar: u64, + pub syscr: u64, + pub syscw: u64, + pub read_bytes: u64, + pub write_bytes: u64, + pub cancelled_write_bytes: i64, +} + +impl ProcIO { + fn new() -> Self { + Default::default() + } +} + +impl Proc { + /// returns proc IO stats + pub fn io(&self) -> CollectResult { + let mut proc_io = ProcIO::new(); + let proc_io_path_str = format!("{:?}", self.path()); + let proc_io_file = format!("{}/io", proc_io_path_str.replace("\"", "")); + for line in utils::read_file_lines(&proc_io_file)? { + let item_fields: Vec<&str> = line.trim().split(':').filter(|s| !s.is_empty()).collect(); + + if item_fields.len() != 2 { + return Err(MetricError::InvalidFieldNumberError( + "proc io".to_string(), + item_fields.len(), + line, + )); + } + + let item_value = item_fields[1].trim(); + match ProcIOType::from(item_fields[0]) { + ProcIOType::RChar => proc_io.rchar = item_value.parse::().unwrap_or_default(), + ProcIOType::WChar => proc_io.wchar = item_value.parse::().unwrap_or_default(), + ProcIOType::SyscR => proc_io.syscr = item_value.parse::().unwrap_or_default(), + ProcIOType::SyscW => proc_io.syscw = item_value.parse::().unwrap_or_default(), + ProcIOType::ReadBytes => { + proc_io.read_bytes = item_value.parse::().unwrap_or_default() + } + ProcIOType::WriteBytes => { + proc_io.write_bytes = item_value.parse::().unwrap_or_default() + } + ProcIOType::CancelledWriteBytes => { + proc_io.cancelled_write_bytes = item_value.parse::().unwrap_or_default() + } + ProcIOType::Unknown => {} + } + } + + Ok(proc_io) + } +} + +#[cfg(test)] +mod tests { + use std::path::Path; + + use crate::proc::*; + + #[test] + fn proc_io() { + let proc_path = Path::new("test_data/fixtures/proc"); + let sys_proc = collect_from(proc_path, 26231).expect("running proc 26231"); + let sys_proc_io = sys_proc.io().expect("running proc 26231 io stat"); + + assert_eq!(sys_proc_io.rchar, 750339); + assert_eq!(sys_proc_io.wchar, 818609); + assert_eq!(sys_proc_io.syscr, 7405); + assert_eq!(sys_proc_io.syscw, 5245); + assert_eq!(sys_proc_io.read_bytes, 1024); + assert_eq!(sys_proc_io.write_bytes, 2048); + assert_eq!(sys_proc_io.cancelled_write_bytes, -1024); + } +}