use std::fmt::Debug;
use std::fs;
use std::io;
use std::fs::File;
use std::io::{BufRead, BufReader, Error};
#[derive(Debug)]
pub struct ProcessStat{
pid: Option<i32>,
rss: Option<String>,
cmd: Option<String>,
state: Option<String>,
ppid: Option<String>,
cstime: Option<String>,
cutime: Option<String>,
vsize: Option<String>,
cpu_use: Option<f64>,
utime: Option<String>,
stime: Option<String>,
vmpeak: Option<String>,
ruid: Option<String>,
rgid: Option<String>,
}
impl Default for ProcessStat{
fn default() ->ProcessStat {
ProcessStat { pid: None, rss: None, cmd: None, state: None, ppid: None, cstime: None, vsize: None , cpu_use: None, cutime: None, stime: None, utime: None, vmpeak: None, ruid: None,rgid: None}
}
}
impl ProcessStat{
pub fn new() -> ProcessStat {
ProcessStat::default()
}
pub fn new_from_pid(pid: i32, cpu_frequency: f64) -> Result<ProcessStat, io::Error> {
let status_str = fs::read_to_string(format!("/proc/{}/status", pid))?;
let stat_str = fs::read_to_string(format!("/proc/{}/stat", pid))?;
let comm_str = fs::read_to_string(format!("/proc/{}/cmdline", pid))?;
let utime = stat_str.split_whitespace().nth(14).map(|line| line.parse::<f64>().unwrap_or(0.0));
let stime = stat_str.split_whitespace().nth(15).map(|line| line.parse::<f64>().unwrap_or(0.0));
let u_time = utime.unwrap_or(0.0);
let s_time = stime.unwrap_or(0.0);
let cpu_usage = 100.0 * (s_time / cpu_frequency);
let rss = if let Some(line) = status_str.lines().nth(22) {
if line.contains("VmRSS") {
if line.trim_start_matches("VmRSS:").trim() == "sigign" {
None
} else {
Some(line.trim_start_matches("VmRSS:").trim().to_string())
}
} else {
None
}
} else {
None
};
let vmpeak = if let Some(line) = status_str.lines().nth(17) {
if line.contains("VmPeak") {
if line.trim_start_matches("VmPeak:").trim() == "fffffffff" {
None
} else {
Some(line.trim_start_matches("VmPeak:").trim().to_string())
}
} else {
None
}
} else {
None
};
let status_str_lines = status_str.lines();
Ok(ProcessStat {
pid: Some(pid),
state: status_str.lines().nth(2).map(|line| line.trim_start_matches("State:").trim().to_string()),
cmd: Some(comm_str.replace("\0","")),
rss,
ppid: status_str.lines().nth(6).map(|line| line.trim_start_matches("PPid:").trim().to_string()),
cstime: stat_str.split_whitespace().nth(17).map(|line| line.to_string()),
vsize: status_str.lines().nth(18).map(|line| line.trim_start_matches("VmSize:").trim().to_string()),
cpu_use: Some(cpu_usage),
utime: stat_str.split_whitespace().nth(13).map(|line| line.to_string()),
stime: stat_str.split_whitespace().nth(14).map(|line| line.to_string()),
cutime: stat_str.split_whitespace().nth(16).map(|line| line.to_string()),
vmpeak,
ruid: status_str.lines().nth(8).map(|line| line.trim_start_matches("Uid:").split_whitespace().next().unwrap().trim().to_string()),
rgid: status_str.lines().nth(9).map(|line| line.trim_start_matches("Gid:").split_whitespace().next().unwrap().trim().to_string())
})
}
}
pub fn get_cpu_frequency() -> io::Result<f64> {
let cpu_frequency = fs::read_to_string("/proc/cpuinfo")?
.lines()
.find(|line| line.contains("cpu MHz"))
.and_then(|line| line.split(':').last())
.and_then(|freq_str| freq_str.trim().parse::<f64>().ok())
.map(|freq| freq * 1e6 / 100.0); cpu_frequency.ok_or(io::Error::new(io::ErrorKind::Other, "Failed to read CPU MHz"))
}
pub fn load_process_stats() -> Result<Vec<ProcessStat>, io::Error> {
let mut process_stats: Vec<ProcessStat> = Vec::new();
let entries = fs::read_dir("/proc")?; for entry in entries {
if let Ok(entry) = entry {
if let Ok(file_name) = entry.file_name().into_string() {
if let Ok(pid) = file_name.parse::<i32>() { if let Ok(process_stat) = ProcessStat::new_from_pid(pid,get_cpu_frequency()?) {
process_stats.push(process_stat);
}
}
}
}
}
Ok(process_stats)
}
pub fn format_process_stats(process_stats: &[ProcessStat]) {
println!("{:<8} {:<8} {:<8} {:<8} {:<8} {:<8} {:<8} {:<8}",
"RUID", "PID", "PPID", "RSS", "VMPEAK", "STATE", "CPU%", "CMD");
for process_stat in process_stats {
let pid = process_stat.pid.unwrap_or(-1); let ppid = process_stat.ppid.as_ref().map_or("None", |s| s);
let rss = process_stat.rss.as_ref().map_or("None", |s| s);
let vmpeak = process_stat.vmpeak.as_ref().map_or("None", |s| s);
let state = process_stat.state.as_ref().map_or("None", |s| s);
let cpu_use = process_stat.cpu_use.unwrap_or(-1.0); let cmd = process_stat.cmd.as_ref().map_or("None", |s| s);
let ruid = process_stat.ruid.as_ref().map_or("unknow", |s| s);
println!("{:<8} {:<8} {:<8} {:<8} {:<8} {:<8} {:<8} {:<8}",
ruid,pid, ppid, rss, vmpeak, state, cpu_use, cmd);
}
}
pub fn format_one_process(pid: i32) -> Result<(), io::Error> {
if let Ok(process_stat) = ProcessStat::new_from_pid(pid, get_cpu_frequency()?) {
let pid = process_stat.pid.unwrap_or(-1); let ppid = process_stat.ppid.as_deref().unwrap_or("None");
let rss = process_stat.rss.as_deref().unwrap_or("None");
let vmpeak = process_stat.vmpeak.as_deref().unwrap_or("None");
let state = process_stat.state.as_deref().unwrap_or("None");
let cpu_use = process_stat.cpu_use.unwrap_or(-1.0); let cmd = process_stat.cmd.as_deref().unwrap_or("None");
println!("{:<8} {:<8} {:<8} {:<8} {:<8} {:<8} {:<8}",
pid, ppid, rss, vmpeak, state, cpu_use, cmd);
Ok(())
} else {
Err(io::Error::new(io::ErrorKind::Other, "Failed to read process stats"))
}
}