1
1
/// Collect CPU process information without GPU information, from files in /proc.
2
-
3
- extern crate libc;
4
- extern crate page_size;
5
- extern crate users;
2
+ ///
3
+ /// The underlying system is virtualized through ProcfsAPI.
6
4
7
5
use crate :: process;
6
+ use crate :: procfsapi:: ProcfsAPI ;
8
7
9
8
use std:: collections:: HashMap ;
10
- use std:: fs;
11
- use std:: path;
12
- use std:: os:: linux:: fs:: MetadataExt ;
13
- use std:: time:: { SystemTime , UNIX_EPOCH } ;
14
- use users:: { uid_t, get_user_by_uid} ;
15
9
16
10
/// Obtain process information via /proc and return a vector of structures with all the information
17
11
/// we need. In the returned vector, pids uniquely tag the records.
@@ -22,50 +16,44 @@ use users::{uid_t, get_user_by_uid};
22
16
/// This function uniformly uses /proc, even though in some cases there are system calls that
23
17
/// provide the same information.
24
18
25
- pub fn get_process_information ( ) -> Result < Vec < process:: Process > , String > {
19
+ pub fn get_process_information ( fs : & dyn ProcfsAPI ) -> Result < Vec < process:: Process > , String > {
26
20
27
21
// The boot time is the `btime` field of /proc/stat. It is measured in seconds since epoch. We
28
22
// need this to compute the process's real time, which we need to compute ps-compatible cpu
29
23
// utilization.
30
24
31
25
let mut boot_time = 0 ;
32
- if let Ok ( s) = fs:: read_to_string ( path:: Path :: new ( "/proc/stat" ) ) {
33
- for l in s. split ( '\n' ) {
34
- if l. starts_with ( "btime " ) {
35
- let fields = l. split_ascii_whitespace ( ) . collect :: < Vec < & str > > ( ) ;
36
- boot_time = parse_usize_field ( & fields, 1 , & l, "stat" , 0 , "btime" ) ? as u64 ;
37
- break ;
38
- }
39
- }
40
- if boot_time == 0 {
41
- return Err ( format ! ( "Could not find btime in /proc/stat: {s}" ) ) ;
26
+ let stat_s = fs. read_to_string ( "stat" ) ?;
27
+ for l in stat_s. split ( '\n' ) {
28
+ if l. starts_with ( "btime " ) {
29
+ let fields = l. split_ascii_whitespace ( ) . collect :: < Vec < & str > > ( ) ;
30
+ boot_time = parse_usize_field ( & fields, 1 , & l, "stat" , 0 , "btime" ) ? as u64 ;
31
+ break ;
42
32
}
43
- } else {
44
- return Err ( format ! ( "Could not open or read /proc/stat" ) ) ;
45
- } ;
33
+ }
34
+ if boot_time == 0 {
35
+ return Err ( format ! ( "Could not find btime in /proc/stat: {stat_s}" ) ) ;
36
+ }
46
37
47
38
// The total RAM installed is in the `MemTotal` field of /proc/meminfo. We need this to compute
48
39
// ps-compatible relative memory use.
49
40
50
41
let mut memtotal_kib = 0 ;
51
- if let Ok ( s) = fs:: read_to_string ( path:: Path :: new ( "/proc/meminfo" ) ) {
52
- for l in s. split ( '\n' ) {
53
- if l. starts_with ( "MemTotal: " ) {
54
- // We expect "MemTotal:\s+(\d+)\s+kB", roughly
55
- let fields = l. split_ascii_whitespace ( ) . collect :: < Vec < & str > > ( ) ;
56
- if fields. len ( ) != 3 || fields[ 2 ] != "kB" {
57
- return Err ( format ! ( "Unexpected MemTotal in /proc/meminfo: {l}" ) ) ;
58
- }
59
- memtotal_kib = parse_usize_field ( & fields, 1 , & l, "meminfo" , 0 , "MemTotal" ) ?;
60
- break ;
42
+ let meminfo_s = fs. read_to_string ( "meminfo" ) ?;
43
+ for l in meminfo_s. split ( '\n' ) {
44
+ if l. starts_with ( "MemTotal: " ) {
45
+ // We expect "MemTotal:\s+(\d+)\s+kB", roughly
46
+ let fields = l. split_ascii_whitespace ( ) . collect :: < Vec < & str > > ( ) ;
47
+ if fields. len ( ) != 3 || fields[ 2 ] != "kB" {
48
+ return Err ( format ! ( "Unexpected MemTotal in /proc/meminfo: {l}" ) ) ;
61
49
}
50
+ memtotal_kib = parse_usize_field ( & fields, 1 , & l, "meminfo" , 0 , "MemTotal" ) ?;
51
+ break ;
62
52
}
63
- if memtotal_kib == 0 {
64
- return Err ( format ! ( "Could not find MemTotal in /proc/meminfo: {s}" ) ) ;
65
- }
66
- } else {
67
- return Err ( format ! ( "Could not open or read /proc/meminfo" ) ) ;
68
- } ;
53
+ }
54
+ if memtotal_kib == 0 {
55
+ return Err ( format ! ( "Could not find MemTotal in /proc/meminfo: {meminfo_s}" ) ) ;
56
+ }
69
57
70
58
// Enumerate all pids, and collect the uids while we're here.
71
59
//
@@ -76,30 +64,14 @@ pub fn get_process_information() -> Result<Vec<process::Process>, String> {
76
64
// Note that a pid may disappear between the time we see it here and the time we get around to
77
65
// reading it, later, and that new pids may appear meanwhile. We should ignore both issues.
78
66
79
- let mut pids = vec ! [ ] ;
80
- if let Ok ( dir) = fs:: read_dir ( "/proc" ) {
81
- for dirent in dir {
82
- if let Ok ( dirent) = dirent {
83
- if let Ok ( meta) = dirent. metadata ( ) {
84
- let uid = meta. st_uid ( ) ;
85
- if let Some ( name) = dirent. path ( ) . file_name ( ) {
86
- if let Ok ( pid) = name. to_string_lossy ( ) . parse :: < usize > ( ) {
87
- pids. push ( ( pid, uid) ) ;
88
- }
89
- }
90
- }
91
- }
92
- }
93
- } else {
94
- return Err ( format ! ( "Could not open /proc" ) ) ;
95
- } ;
67
+ let pids = fs. read_proc_pids ( ) ?;
96
68
97
69
// Collect remaining system data from /proc/{pid}/stat for the enumerated pids.
98
70
99
- let kib_per_page = page_size:: get ( ) / 1024 ;
71
+ let kib_per_page = fs . page_size ( ) ;
100
72
let mut result = vec ! [ ] ;
101
73
let mut user_table = UserTable :: new ( ) ;
102
- let clock_ticks_per_sec: usize = unsafe { libc :: sysconf ( libc :: _SC_CLK_TCK ) as usize } ;
74
+ let clock_ticks_per_sec = fs . clock_ticks_per_sec ( ) ;
103
75
for ( pid, uid) in pids {
104
76
105
77
// Basic system variables.
@@ -111,7 +83,7 @@ pub fn get_process_information() -> Result<Vec<process::Process>, String> {
111
83
let comm;
112
84
let utime;
113
85
let stime;
114
- if let Ok ( line) = fs:: read_to_string ( path :: Path :: new ( & format ! ( "/proc/ {pid}/stat" ) ) ) {
86
+ if let Ok ( line) = fs. read_to_string ( & format ! ( "{pid}/stat" ) ) {
115
87
// The comm field is a little tricky, it must be extracted first as the contents between
116
88
// the first '(' and the last ')' in the line.
117
89
let commstart = line. find ( '(' ) ;
@@ -132,12 +104,13 @@ pub fn get_process_information() -> Result<Vec<process::Process>, String> {
132
104
let cstime = parse_usize_field ( & fields, 14 , & line, "stat" , pid, "cstime" ) ? / clock_ticks_per_sec;
133
105
bsdtime = utime + stime + cutime + cstime;
134
106
let start_time = ( parse_usize_field ( & fields, 19 , & line, "stat" , pid, "starttime" ) ? / clock_ticks_per_sec) as u64 ;
135
- let now = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . unwrap ( ) . as_secs ( ) ;
107
+ let now = fs . now ( ) ;
136
108
realtime = now - ( boot_time + start_time) ;
137
109
} else {
138
110
// This is *usually* benign - the process may have gone away since we enumerated the
139
111
// /proc directory. It is *possibly* indicative of a permission problem, but that
140
112
// problem would be so pervasive that diagnosing it here is not right.
113
+ println ! ( "E" ) ;
141
114
continue ;
142
115
}
143
116
@@ -152,12 +125,13 @@ pub fn get_process_information() -> Result<Vec<process::Process>, String> {
152
125
153
126
let size_kib;
154
127
let rss_kib;
155
- if let Ok ( s) = fs:: read_to_string ( path :: Path :: new ( & format ! ( "/proc/ {pid}/statm" ) ) ) {
128
+ if let Ok ( s) = fs. read_to_string ( & format ! ( "{pid}/statm" ) ) {
156
129
let fields = s. split_ascii_whitespace ( ) . collect :: < Vec < & str > > ( ) ;
157
130
rss_kib = parse_usize_field ( & fields, 1 , & s, "statm" , pid, "resident set size" ) ? * kib_per_page;
158
131
size_kib = parse_usize_field ( & fields, 5 , & s, "statm" , pid, "data size" ) ? * kib_per_page;
159
132
} else {
160
133
// This is *usually* benign - see above.
134
+ println ! ( "F" ) ;
161
135
continue ;
162
136
}
163
137
@@ -172,7 +146,7 @@ pub fn get_process_information() -> Result<Vec<process::Process>, String> {
172
146
173
147
let pcpu = ( ( ( utime + stime) as f64 * 1000.0 / realtime as f64 ) ) . ceil ( ) / 10.0 ;
174
148
let pmem = f64:: min ( ( ( rss_kib as f64 ) * 1000.0 / ( memtotal_kib as f64 ) ) . ceil ( ) / 10.0 , 99.9 ) ;
175
- let user = user_table. lookup ( uid) ;
149
+ let user = user_table. lookup ( fs , uid) ;
176
150
177
151
result. push ( process:: Process {
178
152
pid,
@@ -212,7 +186,7 @@ fn parse_usize_field(fields: &[&str], ix: usize, line: &str, file: &str, pid: us
212
186
// The UserTable optimizes uid -> name lookup.
213
187
214
188
struct UserTable {
215
- ht : HashMap < uid_t , String > ,
189
+ ht : HashMap < u32 , String > ,
216
190
}
217
191
218
192
impl UserTable {
@@ -222,11 +196,10 @@ impl UserTable {
222
196
}
223
197
}
224
198
225
- fn lookup ( & mut self , uid : uid_t ) -> String {
199
+ fn lookup ( & mut self , fs : & dyn ProcfsAPI , uid : u32 ) -> String {
226
200
if let Some ( name) = self . ht . get ( & uid) {
227
201
name. clone ( )
228
- } else if let Some ( u) = get_user_by_uid ( uid) {
229
- let name = u. name ( ) . to_string_lossy ( ) . to_string ( ) ;
202
+ } else if let Some ( name) = fs. user_by_uid ( uid) {
230
203
self . ht . insert ( uid, name. clone ( ) ) ;
231
204
name
232
205
} else {
0 commit comments