Skip to content

Commit b2a3f08

Browse files
Build the 'obvious' /proc/[pid]/status parser
1 parent 0de0403 commit b2a3f08

File tree

3 files changed

+93
-129
lines changed

3 files changed

+93
-129
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ readme = "README.md"
1010
[dependencies]
1111
prettytable-rs = "0.6"
1212
argparse = "0.2"
13-
lazy_static = "0.1"
1413

1514
[[bin]]
1615
name="psq"

src/procrs/lib.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
#[macro_use]
2-
extern crate lazy_static;
3-
41
/// Get information about a process (/proc/[pid]/)
52
pub mod pid;
63
/// The error type used for this crate

src/procrs/pid/status.rs

Lines changed: 93 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,32 @@
11
use std::fs::File;
22
use std::io::{BufReader, BufRead};
33
use std::path::Path;
4-
use std::collections::HashSet;
54
use std::num::ParseIntError;
6-
use std::str::FromStr;
75
use ::error::{ProcError, ProcFile, ProcOper};
86
use ::{TaskId, MemSize};
97

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+
1030
#[derive(Debug, PartialEq)]
1131
/// A struct containing information from the status file for a process.
1232
///
@@ -40,81 +60,6 @@ pub struct PidStatus {
4060
pub threads: u32
4161
}
4262

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-
11863
impl PidStatus {
11964
/// Generate PidStatus struct given a process directory
12065
pub fn new(pid_dir: &Path) -> Result<Self, ProcError> {
@@ -137,58 +82,81 @@ impl PidStatus {
13782
}
13883

13984
/// 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"),
167156
})
168157
}
169158
}
170159

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-
192160
/// Parse a set of four numbers as uids or gids.
193161
fn parse_uids(uid_str: &str) -> Result<(u32, u32, u32, u32), ProcError> {
194162
let uids = try!(

0 commit comments

Comments
 (0)