1
1
use std:: fs:: File ;
2
2
use std:: io:: { BufReader , BufRead } ;
3
3
use std:: path:: Path ;
4
- use std:: collections:: HashSet ;
5
4
use std:: num:: ParseIntError ;
6
- use std:: str:: FromStr ;
7
5
use :: error:: { ProcError , ProcFile , ProcOper } ;
8
6
use :: { TaskId , MemSize } ;
9
7
8
+ /// Parse a line, by turning a parsing error into a ProcError
9
+ macro_rules! parse {
10
+ ( $value: expr, $key: expr) => {
11
+ Some ( try!(
12
+ $value. map_err( |e|
13
+ ProcError :: new( ProcOper :: ParsingField , ProcFile :: PidStatus ,
14
+ Some ( e) , Some ( $key) )
15
+ ) ) )
16
+ }
17
+ }
18
+
19
+ /// Unwrap a line, emitting a "missing '$key'" ProcError if None
20
+ macro_rules! unwrap {
21
+ ( $value: expr, $key: expr) => {
22
+ try!(
23
+ $value. ok_or(
24
+ ProcError :: new_more( ProcOper :: ParsingField , ProcFile :: PidStatus ,
25
+ Some ( concat!( "missing " , $key) ) )
26
+ ) )
27
+ }
28
+ }
29
+
10
30
#[ derive( Debug , PartialEq ) ]
11
31
/// A struct containing information from the status file for a process.
12
32
///
@@ -40,81 +60,6 @@ pub struct PidStatus {
40
60
pub threads : u32
41
61
}
42
62
43
- /// Extract a line, and error on missing value, or parsing failure.
44
- macro_rules! extract_line {
45
- ( $lines: expr, $key: expr, $func: expr) => {
46
- // Default value for missing fields.
47
- match extract_line_opt!( $lines, $key, $func) {
48
- Some ( value) => value,
49
- None => return Err (
50
- ProcError :: new_more( ProcOper :: ParsingField , ProcFile :: PidStatus ,
51
- Some ( concat!( "missing " , $key) ) )
52
- ) ,
53
- }
54
- }
55
- }
56
-
57
- /// Extract a line, evalute to an Option (None on missing field), error on parsing
58
- /// failure.
59
- macro_rules! extract_line_opt {
60
- ( $lines: expr, $key: expr, $func: expr) => { {
61
- // Default value for missing fields>
62
- let mut value = None ;
63
- let mut next_val = false ;
64
- loop {
65
- {
66
- if next_val {
67
- let _ = $lines. by_ref( ) . next( ) ;
68
- }
69
- // Break if iterator is finished
70
- if $lines. by_ref( ) . peek( ) . is_none( ) {
71
- break ;
72
- }
73
- // Return error if value is Err
74
- if $lines. by_ref( ) . peek( ) . as_ref( ) . unwrap( ) . is_err( ) {
75
- let _ = try!( $lines. by_ref( ) . next( ) . unwrap( ) ) ;
76
- }
77
- // Finally peek value to decode
78
- let line = $lines. by_ref( ) . peek( ) . as_ref( ) . unwrap( ) . as_ref( ) . unwrap( ) ;
79
- // Find colon offset, error on no match.
80
- let colon_offset = match line. find( ':' ) {
81
- Some ( i) => i,
82
- None => return Err (
83
- ProcError :: new_more( ProcOper :: ParsingField , ProcFile :: PidStatus , Some ( "Line missing colon" ) )
84
- ) ,
85
- } ;
86
- // Split into Key: Value based on colon offset.
87
- let ( first, second) = line. split_at( colon_offset) ;
88
- let key = first. trim( ) ;
89
- let ( _, last) = second. split_at( 1 ) ;
90
- let line_val = last. trim( ) ;
91
- // If we're not looking for this key, try the next one.
92
- if !STATUS_COLS . contains( key) {
93
- next_val = true ;
94
- continue ;
95
- }
96
- // If key doesn't match, break
97
- if $key != key {
98
- break ;
99
- }
100
-
101
- // Call parsing function after trimming value.
102
- // (Funcs must assume they will only get a borrowed string)
103
- value = Some ( try!(
104
- $func( line_val) . map_err( |e|
105
- ProcError :: new( ProcOper :: ParsingField , ProcFile :: PidStatus ,
106
- Some ( e) , Some ( $key) )
107
- )
108
- ) ) ;
109
- }
110
- let _ = $lines. by_ref( ) . next( ) ;
111
- // We have finished finding this value, get next one.
112
- break ;
113
- }
114
- value
115
- } }
116
- }
117
-
118
63
impl PidStatus {
119
64
/// Generate PidStatus struct given a process directory
120
65
pub fn new ( pid_dir : & Path ) -> Result < Self , ProcError > {
@@ -137,58 +82,81 @@ impl PidStatus {
137
82
}
138
83
139
84
/// Parse an Iterator of lines as a /proc/[pid]/status file.
140
- fn parse_string < I : Iterator < Item =Result < String , ProcError > > > ( lines_iter : I ) -> Result < Self , ProcError > {
141
- let mut lines = lines_iter. peekable ( ) ;
142
- // It's quite important that these appear in the order that they
143
- // appear in the status file
144
- Ok ( PidStatus {
145
- name : extract_line ! ( lines, "Name" , |s| Ok ( ( s as & str ) . to_owned( ) ) as Result <String , ProcError >) ,
146
- tgid : extract_line ! ( lines, "Tgid" , parse_any) ,
147
- pid : extract_line ! ( lines, "Pid" , parse_any) ,
148
- ppid : extract_line ! ( lines, "PPid" , parse_any) ,
149
- tracerpid : extract_line ! ( lines, "TracerPid" , parse_any) ,
150
- uid : extract_line ! ( lines, "Uid" , parse_uids) ,
151
- gid : extract_line ! ( lines, "Gid" , parse_uids) ,
152
- fdsize : extract_line ! ( lines, "FDSize" , parse_any) ,
153
- vmpeak : extract_line_opt ! ( lines, "VmPeak" , parse_mem) ,
154
- vmsize : extract_line_opt ! ( lines, "VmSize" , parse_mem) ,
155
- vmlck : extract_line_opt ! ( lines, "VmLck" , parse_mem) ,
156
- vmpin : extract_line_opt ! ( lines, "VmPin" , parse_mem) ,
157
- vmhwm : extract_line_opt ! ( lines, "VmHWM" , parse_mem) ,
158
- vmrss : extract_line_opt ! ( lines, "VmRSS" , parse_mem) ,
159
- vmdata : extract_line_opt ! ( lines, "VmData" , parse_mem) ,
160
- vmstk : extract_line_opt ! ( lines, "VmStk" , parse_mem) ,
161
- vmexe : extract_line_opt ! ( lines, "VmExe" , parse_mem) ,
162
- vmlib : extract_line_opt ! ( lines, "VmLib" , parse_mem) ,
163
- vmpte : extract_line_opt ! ( lines, "VmPTE" , parse_mem) ,
164
- vmpmd : extract_line_opt ! ( lines, "VmPMD" , parse_mem) ,
165
- vmswap : extract_line_opt ! ( lines, "VmSwap" , parse_mem) ,
166
- threads : extract_line ! ( lines, "Threads" , parse_any)
85
+ fn parse_string < I : Iterator < Item =Result < String , ProcError > > > ( lines : I ) -> Result < Self , ProcError > {
86
+ let ( mut name, mut tgid, mut pid, mut ppid, mut tracerpid, mut uid,
87
+ mut gid, mut fdsize, mut vmpeak, mut vmsize, mut vmlck, mut vmpin,
88
+ mut vmhwm, mut vmrss, mut vmdata, mut vmstk, mut vmexe, mut vmlib,
89
+ mut vmpte, mut vmpmd, mut vmswap, mut threads) =
90
+ ( None , None , None , None , None , None , None , None , None , None , None , None ,
91
+ None , None , None , None , None , None , None , None , None , None ) ;
92
+ for line in lines {
93
+ let line = try!( line) ;
94
+ // Find colon offset, error on no match.
95
+ let colon_offset = match line. find ( ':' ) {
96
+ Some ( i) => i,
97
+ None => return Err (
98
+ ProcError :: new_more ( ProcOper :: ParsingField , ProcFile :: PidStatus , Some ( "Line missing colon" ) )
99
+ ) ,
100
+ } ;
101
+ // Split into Key: Value based on colon offset.
102
+ let ( first, second) = line. split_at ( colon_offset) ;
103
+ let key = first. trim ( ) ;
104
+ let ( _, last) = second. split_at ( 1 ) ;
105
+ let value = last. trim ( ) ;
106
+
107
+ match key {
108
+ "Name" => name = parse ! ( Ok ( value. to_owned( ) ) as Result <String , ProcError >, "Name" ) ,
109
+ "Tgid" => tgid = parse ! ( value. parse( ) , "Tgid" ) ,
110
+ "Pid" => pid = parse ! ( value. parse( ) , "Pid" ) ,
111
+ "PPid" => ppid = parse ! ( value. parse( ) , "PPid" ) ,
112
+ "TracerPid" => tracerpid = parse ! ( value. parse( ) , "TracerPid" ) ,
113
+ "Uid" => uid = parse ! ( parse_uids( value) , "Uid" ) ,
114
+ "Gid" => gid = parse ! ( parse_uids( value) , "Gid" ) ,
115
+ "FDSize" => fdsize = parse ! ( value. parse( ) , "FDSize" ) ,
116
+ "VmPeak" => vmpeak = parse ! ( parse_mem( value) , "VmPeak" ) ,
117
+ "VmSize" => vmsize = parse ! ( parse_mem( value) , "VmSize" ) ,
118
+ "VmLck" => vmlck = parse ! ( parse_mem( value) , "VmLck" ) ,
119
+ "VmPin" => vmpin = parse ! ( parse_mem( value) , "VmPin" ) ,
120
+ "VmHWM" => vmhwm = parse ! ( parse_mem( value) , "VmHWM" ) ,
121
+ "VmRSS" => vmrss = parse ! ( parse_mem( value) , "VmRSS" ) ,
122
+ "VmData" => vmdata = parse ! ( parse_mem( value) , "VmData" ) ,
123
+ "VmStk" => vmstk = parse ! ( parse_mem( value) , "VmStk" ) ,
124
+ "VmExe" => vmexe = parse ! ( parse_mem( value) , "VmExe" ) ,
125
+ "VmLib" => vmlib = parse ! ( parse_mem( value) , "VmLib" ) ,
126
+ "VmPTE" => vmpte = parse ! ( parse_mem( value) , "VmPTE" ) ,
127
+ "VmPMD" => vmpmd = parse ! ( parse_mem( value) , "VmPMD" ) ,
128
+ "VmSwap" => vmswap = parse ! ( parse_mem( value) , "VmSwap" ) ,
129
+ "Threads" => threads = parse ! ( value. parse( ) , "Threads" ) ,
130
+ _ => continue ,
131
+ } ;
132
+ }
133
+ Ok ( PidStatus {
134
+ name : unwrap ! ( name, "Name" ) ,
135
+ tgid : unwrap ! ( tgid, "Tgid" ) ,
136
+ pid : unwrap ! ( pid, "Pid" ) ,
137
+ ppid : unwrap ! ( ppid, "PPid" ) ,
138
+ tracerpid : unwrap ! ( tracerpid, "TracerPid" ) ,
139
+ uid : unwrap ! ( uid, "Uid" ) ,
140
+ gid : unwrap ! ( gid, "Gid" ) ,
141
+ fdsize : unwrap ! ( fdsize, "FDSize" ) ,
142
+ vmpeak : vmpeak,
143
+ vmsize : vmsize,
144
+ vmlck : vmlck,
145
+ vmpin : vmpin,
146
+ vmhwm : vmhwm,
147
+ vmrss : vmrss,
148
+ vmdata : vmdata,
149
+ vmstk : vmstk,
150
+ vmexe : vmexe,
151
+ vmlib : vmlib,
152
+ vmpte : vmpte,
153
+ vmpmd : vmpmd,
154
+ vmswap : vmswap,
155
+ threads : unwrap ! ( threads, "Threads" ) ,
167
156
} )
168
157
}
169
158
}
170
159
171
- lazy_static ! {
172
- // This vec should contain all columns that the parser is looking for,
173
- // at the moment this is definitely static.
174
- //
175
- // If this is not kept uptodate, the values will be ignored.
176
- static ref STATUS_COLS : HashSet <String > = vec![ "Name" , "Tgid" , "Pid" , "PPid" ,
177
- "TracerPid" , "Uid" , "Gid" , "FDSize" , "VmPeak" , "VmSize" , "VmLck" ,
178
- "VmPin" , "VmHWM" , "VmRSS" , "VmData" , "VmStk" , "VmExe" , "VmLib" ,
179
- "VmPMD" , "VmPTE" , "VmSwap" , "Threads" ]
180
- . into_iter( )
181
- . map( |s| s. to_owned( ) )
182
- . collect( ) ;
183
- }
184
-
185
-
186
-
187
- /// Parse anything that's parsable from a string.
188
- fn parse_any < N : FromStr > ( str : & str ) -> Result < N , N :: Err > {
189
- str. parse ( )
190
- }
191
-
192
160
/// Parse a set of four numbers as uids or gids.
193
161
fn parse_uids ( uid_str : & str ) -> Result < ( u32 , u32 , u32 , u32 ) , ProcError > {
194
162
let uids = try!(
0 commit comments