Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I26df4d852b4b22d0df6b6871fe9cbde96a6a6964
136 lines
4 KiB
Rust
136 lines
4 KiB
Rust
use std::collections::HashMap;
|
|
use std::fs;
|
|
|
|
pub struct ProcessHelper;
|
|
|
|
#[derive(Debug)]
|
|
pub struct ProcessInfo {
|
|
pub pid: u32,
|
|
pub name: String,
|
|
pub state: String,
|
|
pub ppid: u32,
|
|
pub memory_kb: u64,
|
|
pub cpu_percent: f32,
|
|
}
|
|
|
|
impl ProcessHelper {
|
|
pub fn list_processes() -> std::io::Result<Vec<ProcessInfo>> {
|
|
let mut processes = Vec::new();
|
|
let proc_path = fs::read_dir("/proc")?;
|
|
|
|
for entry in proc_path.flatten() {
|
|
let path = entry.path();
|
|
if !path.is_dir() {
|
|
continue;
|
|
}
|
|
|
|
let pid: u32 = match path.file_name() {
|
|
Some(name) => match name.to_str() {
|
|
Some(s) => s.parse().ok(),
|
|
None => None,
|
|
}
|
|
.ok_or(std::io::Error::new(
|
|
std::io::ErrorKind::InvalidData,
|
|
"invalid pid",
|
|
))?,
|
|
None => continue,
|
|
};
|
|
|
|
if let Ok(info) = Self::process_info(pid) {
|
|
processes.push(info);
|
|
}
|
|
}
|
|
|
|
Ok(processes)
|
|
}
|
|
|
|
pub fn process_info(pid: u32) -> std::io::Result<ProcessInfo> {
|
|
let status_path = format!("/proc/{}/status", pid);
|
|
let content = fs::read_to_string(status_path)?;
|
|
|
|
let mut name = String::new();
|
|
let mut state = String::new();
|
|
let mut ppid: u32 = 0;
|
|
let mut memory_kb: u64 = 0;
|
|
|
|
for line in content.lines() {
|
|
if line.starts_with("Name:") {
|
|
name = line
|
|
.split_whitespace()
|
|
.skip(1)
|
|
.collect::<Vec<_>>()
|
|
.join(" ");
|
|
} else if line.starts_with("State:") {
|
|
state = line.split_whitespace().nth(1).unwrap_or("").to_string();
|
|
} else if line.starts_with("PPid:") {
|
|
ppid = line
|
|
.split_whitespace()
|
|
.nth(1)
|
|
.and_then(|s| s.parse().ok())
|
|
.unwrap_or(0);
|
|
} else if line.starts_with("VmRSS:") {
|
|
memory_kb = line
|
|
.split_whitespace()
|
|
.nth(1)
|
|
.and_then(|s| s.parse().ok())
|
|
.unwrap_or(0);
|
|
}
|
|
}
|
|
|
|
Ok(ProcessInfo {
|
|
pid,
|
|
name,
|
|
state,
|
|
ppid,
|
|
memory_kb,
|
|
cpu_percent: 0.0,
|
|
})
|
|
}
|
|
|
|
pub fn zombie_processes() -> std::io::Result<Vec<ProcessInfo>> {
|
|
Ok(Self::list_processes()?
|
|
.into_iter()
|
|
.filter(|p| p.state.starts_with('Z'))
|
|
.collect())
|
|
}
|
|
|
|
pub fn process_count() -> std::io::Result<HashMap<String, usize>> {
|
|
let mut counts = HashMap::new();
|
|
counts.insert("total".to_string(), 0);
|
|
counts.insert("running".to_string(), 0);
|
|
counts.insert("sleeping".to_string(), 0);
|
|
counts.insert("zombie".to_string(), 0);
|
|
|
|
for proc in Self::list_processes()? {
|
|
*counts.get_mut("total").unwrap() += 1;
|
|
|
|
let first_char = proc.state.chars().next().unwrap_or(' ');
|
|
match first_char {
|
|
'R' => *counts.get_mut("running").unwrap() += 1,
|
|
'S' | 'D' => *counts.get_mut("sleeping").unwrap() += 1,
|
|
'Z' => *counts.get_mut("zombie").unwrap() += 1,
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
Ok(counts)
|
|
}
|
|
|
|
pub fn top_memory_processes(count: usize) -> std::io::Result<Vec<ProcessInfo>> {
|
|
let mut processes = Self::list_processes()?;
|
|
processes.sort_by(|a, b| b.memory_kb.cmp(&a.memory_kb));
|
|
processes.truncate(count);
|
|
Ok(processes)
|
|
}
|
|
|
|
pub fn top_cpu_processes(count: usize) -> std::io::Result<Vec<ProcessInfo>> {
|
|
let mut processes = Self::list_processes()?;
|
|
processes.sort_by(|a, b| {
|
|
b.cpu_percent
|
|
.partial_cmp(&a.cpu_percent)
|
|
.unwrap_or(std::cmp::Ordering::Equal)
|
|
});
|
|
processes.truncate(count);
|
|
Ok(processes)
|
|
}
|
|
}
|