1use std::fmt::Debug;
2use std::fs;
3use std::io;
4use std::fs::File;
5use std::io::{BufRead, BufReader, Error};
6#[derive(Debug)]
7pub struct ProcessStat{
9 pid: Option<i32>,
11 rss: Option<String>,
13 cmd: Option<String>,
15 state: Option<String>,
17 ppid: Option<String>,
19 cstime: Option<String>,
21 cutime: Option<String>,
23 vsize: Option<String>,
25 cpu_use: Option<f64>,
27 utime: Option<String>,
29 stime: Option<String>,
31 vmpeak: Option<String>,
33 ruid: Option<String>,
35 rgid: Option<String>,
37}
38
39
40impl Default for ProcessStat{
41 fn default() ->ProcessStat {
42 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}
43 }
44}
45impl ProcessStat{
46 pub fn new() -> ProcessStat {
48 ProcessStat::default()
49 }
50 pub fn new_from_pid(pid: i32, cpu_frequency: f64) -> Result<ProcessStat, io::Error> {
52 let status_str = fs::read_to_string(format!("/proc/{}/status", pid))?;
55 let stat_str = fs::read_to_string(format!("/proc/{}/stat", pid))?;
56 let comm_str = fs::read_to_string(format!("/proc/{}/cmdline", pid))?;
58
59 let utime = stat_str.split_whitespace().nth(14).map(|line| line.parse::<f64>().unwrap_or(0.0));
60 let stime = stat_str.split_whitespace().nth(15).map(|line| line.parse::<f64>().unwrap_or(0.0));
61 let u_time = utime.unwrap_or(0.0);
62 let s_time = stime.unwrap_or(0.0);
63 let cpu_usage = 100.0 * (s_time / cpu_frequency); let rss = if let Some(line) = status_str.lines().nth(22) {
66 if line.contains("VmRSS") {
67 if line.trim_start_matches("VmRSS:").trim() == "sigign" {
68 None
69 } else {
70 Some(line.trim_start_matches("VmRSS:").trim().to_string())
71 }
72 } else {
73 None
74 }
75 } else {
76 None
77 };
78
79 let vmpeak = if let Some(line) = status_str.lines().nth(17) {
80 if line.contains("VmPeak") {
81 if line.trim_start_matches("VmPeak:").trim() == "fffffffff" {
82 None
83 } else {
84 Some(line.trim_start_matches("VmPeak:").trim().to_string())
85 }
86 } else {
87 None
88 }
89 } else {
90 None
91 };
92 let status_str_lines = status_str.lines();
93 Ok(ProcessStat {
94 pid: Some(pid),
95 state: status_str.lines().nth(2).map(|line| line.trim_start_matches("State:").trim().to_string()),
96 cmd: Some(comm_str.replace("\0","")),
97 rss,
98 ppid: status_str.lines().nth(6).map(|line| line.trim_start_matches("PPid:").trim().to_string()),
99 cstime: stat_str.split_whitespace().nth(17).map(|line| line.to_string()),
100 vsize: status_str.lines().nth(18).map(|line| line.trim_start_matches("VmSize:").trim().to_string()),
101 cpu_use: Some(cpu_usage),
102 utime: stat_str.split_whitespace().nth(13).map(|line| line.to_string()),
103 stime: stat_str.split_whitespace().nth(14).map(|line| line.to_string()),
104 cutime: stat_str.split_whitespace().nth(16).map(|line| line.to_string()),
105 vmpeak,
106 ruid: status_str.lines().nth(8).map(|line| line.trim_start_matches("Uid:").split_whitespace().next().unwrap().trim().to_string()),
107 rgid: status_str.lines().nth(9).map(|line| line.trim_start_matches("Gid:").split_whitespace().next().unwrap().trim().to_string())
108 })
109}
110}
111
112pub fn get_cpu_frequency() -> io::Result<f64> {
114 let cpu_frequency = fs::read_to_string("/proc/cpuinfo")?
115 .lines()
116 .find(|line| line.contains("cpu MHz"))
117 .and_then(|line| line.split(':').last())
118 .and_then(|freq_str| freq_str.trim().parse::<f64>().ok())
119 .map(|freq| freq * 1e6 / 100.0); cpu_frequency.ok_or(io::Error::new(io::ErrorKind::Other, "Failed to read CPU MHz"))
121}
122
123pub fn load_process_stats() -> Result<Vec<ProcessStat>, io::Error> {
125 let mut process_stats: Vec<ProcessStat> = Vec::new();
126 let entries = fs::read_dir("/proc")?; for entry in entries {
128 if let Ok(entry) = entry {
129 if let Ok(file_name) = entry.file_name().into_string() {
130 if let Ok(pid) = file_name.parse::<i32>() { if let Ok(process_stat) = ProcessStat::new_from_pid(pid,get_cpu_frequency()?) {
132 process_stats.push(process_stat);
133 }
134 }
135 }
136 }
137 }
138 Ok(process_stats)
139}
140pub fn format_process_stats(process_stats: &[ProcessStat]) {
142 println!("{:<8} {:<8} {:<8} {:<8} {:<8} {:<8} {:<8} {:<8}",
143 "RUID", "PID", "PPID", "RSS", "VMPEAK", "STATE", "CPU%", "CMD");
144 for process_stat in process_stats {
145 let pid = process_stat.pid.unwrap_or(-1); let ppid = process_stat.ppid.as_ref().map_or("None", |s| s);
147 let rss = process_stat.rss.as_ref().map_or("None", |s| s);
148 let vmpeak = process_stat.vmpeak.as_ref().map_or("None", |s| s);
149 let state = process_stat.state.as_ref().map_or("None", |s| s);
150 let cpu_use = process_stat.cpu_use.unwrap_or(-1.0); let cmd = process_stat.cmd.as_ref().map_or("None", |s| s);
152 let ruid = process_stat.ruid.as_ref().map_or("unknow", |s| s);
153 println!("{:<8} {:<8} {:<8} {:<8} {:<8} {:<8} {:<8} {:<8}",
154 ruid,pid, ppid, rss, vmpeak, state, cpu_use, cmd);
155 }
156}
157pub fn format_one_process(pid: i32) -> Result<(), io::Error> {
159 if let Ok(process_stat) = ProcessStat::new_from_pid(pid, get_cpu_frequency()?) {
160 let pid = process_stat.pid.unwrap_or(-1); let ppid = process_stat.ppid.as_deref().unwrap_or("None");
162 let rss = process_stat.rss.as_deref().unwrap_or("None");
163 let vmpeak = process_stat.vmpeak.as_deref().unwrap_or("None");
164 let state = process_stat.state.as_deref().unwrap_or("None");
165 let cpu_use = process_stat.cpu_use.unwrap_or(-1.0); let cmd = process_stat.cmd.as_deref().unwrap_or("None");
167 println!("{:<8} {:<8} {:<8} {:<8} {:<8} {:<8} {:<8}",
168 pid, ppid, rss, vmpeak, state, cpu_use, cmd);
169 Ok(())
170 } else {
171 Err(io::Error::new(io::ErrorKind::Other, "Failed to read process stats"))
172 }
173}
174