Skip to content

Commit d3a7dcb

Browse files
committed
/proc/[pid]/stat
1 parent 97e3c8e commit d3a7dcb

File tree

8 files changed

+549
-53
lines changed

8 files changed

+549
-53
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ exclude = [
1313
]
1414

1515
[dependencies]
16+
libc = "0.1"
1617
nom = "0.4"
1718
byteorder = "0.3"

src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
extern crate nom;
88

99
extern crate byteorder;
10+
extern crate libc;
1011

11-
mod loadavg;
12+
#[macro_use]
1213
mod parsers;
14+
15+
mod loadavg;
1316
pub mod pid;
1417

1518
pub use loadavg::{LoadAvg, loadavg};

src/loadavg.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Parsers and data structures for `/proc/loadavg`.
1+
//! System load and task statistics from `/proc/loadavg`.
22
33
use std::fs::File;
44
use std::io::Result;
@@ -13,7 +13,7 @@ use parsers::{map_result, parse_f32, parse_i32, parse_u32, read_to_end};
1313
/// The load average is the ratio of runnable and uninterruptible (waiting on IO) tasks to total
1414
/// tasks on the system.
1515
///
16-
/// See `man proc 5` and `Linux/fs/proc/loadavg.c`.
16+
/// See `man 5 proc` and `Linux/fs/proc/loadavg.c`.
1717
#[derive(Debug, Default, PartialEq)]
1818
pub struct LoadAvg {
1919
/// Load average over the last minute.

src/parsers.rs

+105-20
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
//! Parsers and utility functions.
22
3+
use std::borrow::ToOwned;
4+
use std::fs::File;
35
use std::io::{Error, ErrorKind, Read, Result};
46
use std::str::{self, FromStr};
5-
use std::fs::File;
67

78
use byteorder::{ByteOrder, LittleEndian};
8-
use nom::{IResult, alphanumeric, digit, is_digit, not_line_ending, space};
9+
use libc::clock_t;
10+
use nom::{
11+
alphanumeric,
12+
digit,
13+
Err,
14+
ErrorCode,
15+
IResult,
16+
is_digit,
17+
not_line_ending,
18+
space
19+
};
920

1021
/// Read all bytes in the file until EOF, placing them into `buf`.
1122
///
@@ -54,12 +65,13 @@ pub fn map_result<T>(result: IResult<&[u8], T>) -> Result<T> {
5465
}
5566
}
5667
IResult::Error(err) => Err(Error::new(ErrorKind::InvalidInput,
57-
format!("unable to parse input: {:?}", err))),
68+
format!("unable to parse input: {:?}", err))),
5869
_ => Err(Error::new(ErrorKind::InvalidInput, "unable to parse input")),
5970
}
6071
}
6172

6273

74+
/// Recognizes numerical characters: 0-9, and periods: '.'.
6375
fn fdigit(input: &[u8]) -> IResult<&[u8], &[u8]> {
6476
for idx in 0..input.len() {
6577
if (!is_digit(input[idx])) && ('.' as u8 != input[idx]) {
@@ -69,26 +81,44 @@ fn fdigit(input: &[u8]) -> IResult<&[u8], &[u8]> {
6981
IResult::Done(b"", input)
7082
}
7183

72-
/// Parses the remainder of the line to a string.
73-
named!(pub parse_to_end<String>,
74-
map_res!(map_res!(not_line_ending, str::from_utf8), FromStr::from_str));
84+
/// Recognizes numerical characters: 0-9, and an optional leading dash: '-'.
85+
pub fn sdigit(input:&[u8]) -> IResult<&[u8], &[u8]> {
86+
if input.is_empty() {
87+
return IResult::Done(b"", input)
88+
}
7589

76-
/// Parses a u32 in base-10 format.
77-
named!(pub parse_u32<u32>,
78-
map_res!(map_res!(digit, str::from_utf8), FromStr::from_str));
90+
let start = if input[0] == '-' as u8 { 1 } else { 0 };
91+
for (idx, item) in input.iter().enumerate().skip(start) {
92+
if !is_digit(*item) {
93+
if idx == start {
94+
return IResult::Error(Err::Position(ErrorCode::Digit as u32, input));
95+
} else {
96+
return IResult::Done(&input[idx..], &input[0..idx]);
97+
}
98+
}
99+
}
100+
IResult::Done(b"", input)
101+
}
79102

80-
/// Parses a f32 in base-10 format.
81-
named!(pub parse_f32<f32>,
82-
map_res!(map_res!(fdigit, str::from_utf8), FromStr::from_str));
103+
/// Parses a line to a string.
104+
named!(pub parse_line<String>,
105+
map!(map_res!(not_line_ending, str::from_utf8), ToOwned::to_owned));
83106

84-
/// Parses a sequence of whitespace seperated u32s.
85-
named!(pub parse_u32s<Vec<u32> >, separated_list!(space, parse_u32));
107+
/// Parses a clock_t in base-10 format.
108+
named!(pub parse_clock<clock_t>,
109+
map_res!(map_res!(sdigit, str::from_utf8), FromStr::from_str));
86110

87111
/// Parses an i32 in base-10 format.
88-
named!(pub parse_i32<i32>, map!(parse_u32, { |n| { n as i32 } }));
112+
named!(pub parse_i32<i32>,
113+
map_res!(map_res!(sdigit, str::from_utf8), FromStr::from_str));
89114

90-
/// Parses a sequence of whitespace seperated i32s.
91-
named!(pub parse_i32s<Vec<i32> >, separated_list!(space, parse_i32));
115+
/// Parses an i64 in base-10 format.
116+
named!(pub parse_i64<i64>,
117+
map_res!(map_res!(sdigit, str::from_utf8), FromStr::from_str));
118+
119+
/// Parses a u32 in base-10 format.
120+
named!(pub parse_u32<u32>,
121+
map_res!(map_res!(digit, str::from_utf8), FromStr::from_str));
92122

93123
/// Parses a u64 in base-10 format.
94124
named!(pub parse_u64<u64>,
@@ -98,6 +128,16 @@ named!(pub parse_u64<u64>,
98128
named!(pub parse_usize<usize>,
99129
map_res!(map_res!(digit, str::from_utf8), FromStr::from_str));
100130

131+
/// Parses a f32 in base-10 format.
132+
named!(pub parse_f32<f32>,
133+
map_res!(map_res!(fdigit, str::from_utf8), FromStr::from_str));
134+
135+
/// Parses a sequence of whitespace seperated u32s.
136+
named!(pub parse_u32s<Vec<u32> >, separated_list!(space, parse_u32));
137+
138+
/// Parses a sequence of whitespace seperated i32s.
139+
named!(pub parse_i32s<Vec<i32> >, separated_list!(space, parse_i32));
140+
101141
/// Parses a usize followed by a kB unit tag.
102142
named!(pub parse_kb<usize>,
103143
chain!(space ~ bytes: parse_usize ~ space ~ tag!("kB"), || { bytes }));
@@ -138,6 +178,35 @@ named!(pub parse_u32_mask_list<Box<[u8]> >,
138178
bytes.into_boxed_slice()
139179
}));
140180

181+
/// `take_until_right_and_consume!(tag) => &[T] -> IResult<&[T], &[T]>`
182+
/// generates a parser consuming bytes until the specified byte sequence is found, and consumes it.
183+
/// The sequence is searched for in the input in right to left order.
184+
macro_rules! take_until_right_and_consume(
185+
($i:expr, $inp:expr) => ({
186+
#[inline(always)]
187+
fn as_bytes<T: nom::AsBytes>(b: &T) -> &[u8] {
188+
b.as_bytes()
189+
}
190+
191+
let expected = $inp;
192+
let bytes = as_bytes(&expected);
193+
let mut index = 0;
194+
let mut parsed = false;
195+
for idx in (0..(($i.len() + 1) - bytes.len())).rev() {
196+
if &$i[idx..idx + bytes.len()] == bytes {
197+
index = idx;
198+
parsed = true;
199+
break;
200+
}
201+
}
202+
if parsed {
203+
nom::IResult::Done(&$i[(index + bytes.len())..], &$i[0..index])
204+
} else {
205+
nom::IResult::Error(nom::Err::Position(nom::ErrorCode::TakeUntilAndConsume as u32, $i))
206+
}
207+
});
208+
);
209+
141210
#[cfg(test)]
142211
pub mod tests {
143212

@@ -147,8 +216,8 @@ pub mod tests {
147216

148217
use nom::IResult;
149218

150-
use super::{map_result, parse_f32, parse_i32s, parse_u32_hex, parse_u32_mask_list, parse_u32s,
151-
reverse};
219+
use super::{map_result, parse_f32, parse_i32, parse_i32s, parse_i64, parse_u32_hex,
220+
parse_u32_mask_list, parse_u32s, reverse};
152221

153222
/// Unwrap a complete parse result.
154223
pub fn unwrap<T>(result: IResult<&[u8], T>) -> T {
@@ -208,7 +277,23 @@ pub mod tests {
208277
assert_eq!(Vec::<i32>::new(), &*unwrap(parse_i32s(b"")));
209278
assert_eq!(vec![0i32], &*unwrap(parse_i32s(b"0")));
210279
assert_eq!(vec![0i32, 1], &*unwrap(parse_i32s(b"0 1")));
211-
assert_eq!(vec![99999i32, 32, 22, 888], &*unwrap(parse_i32s(b"99999 32 22 888")));
280+
assert_eq!(vec![99999i32, 0, -22, 32, 888], &*unwrap(parse_i32s(b"99999 0 -22 32 888")));
281+
}
282+
283+
#[test]
284+
fn test_parse_i32() {
285+
assert_eq!(0i32, unwrap(parse_i32(b"0")));
286+
assert_eq!(0i32, unwrap(parse_i32(b"-0")));
287+
assert_eq!(32i32, unwrap(parse_i32(b"32")));
288+
assert_eq!(-32i32, unwrap(parse_i32(b"-32")));
289+
}
290+
291+
#[test]
292+
fn test_parse_i64() {
293+
assert_eq!(0i64, unwrap(parse_i64(b"0")));
294+
assert_eq!(0i64, unwrap(parse_i64(b"-0")));
295+
assert_eq!(32i64, unwrap(parse_i64(b"32")));
296+
assert_eq!(-32i64, unwrap(parse_i64(b"-32")));
212297
}
213298

214299
#[test]

src/pid/mod.rs

+2-15
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
//! Process-specific information from `/proc/[pid]/`.
22
3+
mod stat;
34
mod statm;
45
mod status;
56

67
pub use pid::statm::{Statm, statm, statm_self};
78
pub use pid::status::{SeccompMode, Status, status, status_self};
9+
pub use pid::stat::{Stat};
810

911
/// The state of a process.
1012
#[derive(Debug, PartialEq, Eq, Hash)]
@@ -50,18 +52,3 @@ impl Default for State {
5052
State::Running
5153
}
5254
}
53-
54-
/// Parse the stat state format.
55-
named!(parse_stat_state<State>,
56-
alt!(tag!("R") => { |_| State::Running }
57-
| tag!("S") => { |_| State::Sleeping }
58-
| tag!("D") => { |_| State::Waiting }
59-
| tag!("Z") => { |_| State::Zombie }
60-
| tag!("T") => { |_| State::Stopped }
61-
| tag!("t") => { |_| State::TraceStopped }
62-
| tag!("W") => { |_| State::Paging }
63-
| tag!("X") => { |_| State::Dead }
64-
| tag!("x") => { |_| State::Dead }
65-
| tag!("K") => { |_| State::Wakekill }
66-
| tag!("W") => { |_| State::Waking }
67-
| tag!("P") => { |_| State::Parked }));

0 commit comments

Comments
 (0)