use std::fs::File;
use std::io::Result;
use std::str::{self, FromStr};
use libc::{clock_t, pid_t};
use nom::{self, IResult, line_ending, space};
use pid::State;
use parsers::{
map_result,
parse_clock,
parse_i32,
parse_u32,
parse_u64,
parse_usize,
read_to_end
};
#[derive(Debug, Default, PartialEq, Eq, Hash)]
pub struct Stat {
pub pid: pid_t,
pub command: String,
pub state: State,
pub ppid: pid_t,
pub pgrp: pid_t,
pub session: pid_t,
pub tty_nr: pid_t,
pub tty_pgrp: pid_t,
pub flags: u32,
pub minflt: usize,
pub cminflt: usize,
pub majflt: usize,
pub cmajflt: usize,
pub utime: clock_t,
pub stime: clock_t,
pub cutime: clock_t,
pub cstime: clock_t,
pub priority: i32,
pub nice: i32,
pub num_threads: i32,
pub start_time: u64,
pub vsize: usize,
pub rss: usize,
pub rsslim: usize,
pub start_code: usize,
pub end_code: usize,
pub startstack: usize,
pub kstkeep: usize,
pub kstkeip: usize,
pub signal: usize,
pub blocked: usize,
pub sigignore: usize,
pub sigcatch: usize,
pub wchan: usize,
pub exit_signal: i32,
pub processor: u32,
pub rt_priority: u32,
pub policy: u32,
pub delayacct_blkio_ticks: u64,
pub guest_time: clock_t,
pub cguest_time: clock_t,
pub start_data: usize,
pub end_data: usize,
pub start_brk: usize,
pub arg_start: usize,
pub arg_end: usize,
pub env_start: usize,
pub env_end: usize,
pub exit_code: i32,
}
named!(parse_command<String>,
map_res!(map_res!(preceded!(char!('('),
take_until_right_and_consume!(")")),
str::from_utf8),
FromStr::from_str));
named!(parse_stat_state<State>,
alt!(tag!("R") => { |_| State::Running }
| tag!("S") => { |_| State::Sleeping }
| tag!("D") => { |_| State::Waiting }
| tag!("Z") => { |_| State::Zombie }
| tag!("T") => { |_| State::Stopped }
| tag!("t") => { |_| State::TraceStopped }
| tag!("W") => { |_| State::Paging }
| tag!("X") => { |_| State::Dead }
| tag!("x") => { |_| State::Dead }
| tag!("K") => { |_| State::Wakekill }
| tag!("W") => { |_| State::Waking }
| tag!("P") => { |_| State::Parked }));
fn parse_stat(input: &[u8]) -> IResult<&[u8], Stat> {
macro_rules! s {
($i:expr, $f:expr) => (terminated!($i, call!($f), space))
}
macro_rules! l {
($i:expr, $f:expr) => (terminated!($i, call!($f), line_ending))
}
let rest = input;
let (rest, pid) = try_parse!(rest, s!(parse_i32 ));
let (rest, command) = try_parse!(rest, s!(parse_command ));
let (rest, state) = try_parse!(rest, s!(parse_stat_state ));
let (rest, ppid) = try_parse!(rest, s!(parse_i32 ));
let (rest, pgrp) = try_parse!(rest, s!(parse_i32 ));
let (rest, session) = try_parse!(rest, s!(parse_i32 ));
let (rest, tty_nr) = try_parse!(rest, s!(parse_i32 ));
let (rest, tty_pgrp) = try_parse!(rest, s!(parse_i32 ));
let (rest, flags) = try_parse!(rest, s!(parse_u32 ));
let (rest, minflt) = try_parse!(rest, s!(parse_usize ));
let (rest, cminflt) = try_parse!(rest, s!(parse_usize ));
let (rest, majflt) = try_parse!(rest, s!(parse_usize ));
let (rest, cmajflt) = try_parse!(rest, s!(parse_usize ));
let (rest, utime) = try_parse!(rest, s!(parse_clock ));
let (rest, stime) = try_parse!(rest, s!(parse_clock ));
let (rest, cutime) = try_parse!(rest, s!(parse_clock ));
let (rest, cstime) = try_parse!(rest, s!(parse_clock ));
let (rest, priority) = try_parse!(rest, s!(parse_i32 ));
let (rest, nice) = try_parse!(rest, s!(parse_i32 ));
let (rest, num_threads) = try_parse!(rest, s!(parse_i32 ));
let (rest, _itrealvalue) = try_parse!(rest, s!(parse_i32 ));
let (rest, start_time) = try_parse!(rest, s!(parse_u64 ));
let (rest, vsize) = try_parse!(rest, s!(parse_usize ));
let (rest, rss) = try_parse!(rest, s!(parse_usize ));
let (rest, rsslim) = try_parse!(rest, s!(parse_usize ));
let (rest, start_code) = try_parse!(rest, s!(parse_usize ));
let (rest, end_code) = try_parse!(rest, s!(parse_usize ));
let (rest, startstack) = try_parse!(rest, s!(parse_usize ));
let (rest, kstkeep) = try_parse!(rest, s!(parse_usize ));
let (rest, kstkeip) = try_parse!(rest, s!(parse_usize ));
let (rest, signal) = try_parse!(rest, s!(parse_usize ));
let (rest, blocked) = try_parse!(rest, s!(parse_usize ));
let (rest, sigignore) = try_parse!(rest, s!(parse_usize ));
let (rest, sigcatch) = try_parse!(rest, s!(parse_usize ));
let (rest, wchan) = try_parse!(rest, s!(parse_usize ));
let (rest, _nswap) = try_parse!(rest, s!(parse_usize ));
let (rest, _cnswap) = try_parse!(rest, s!(parse_usize ));
let (rest, exit_signal) = try_parse!(rest, s!(parse_i32 ));
let (rest, processor) = try_parse!(rest, s!(parse_u32 ));
let (rest, rt_priority) = try_parse!(rest, s!(parse_u32 ));
let (rest, policy) = try_parse!(rest, s!(parse_u32 ));
let (rest, delayacct_blkio_ticks) = try_parse!(rest, s!(parse_u64 ));
let (rest, guest_time) = try_parse!(rest, s!(parse_clock ));
let (rest, cguest_time) = try_parse!(rest, s!(parse_clock ));
let (rest, start_data) = try_parse!(rest, s!(parse_usize ));
let (rest, end_data) = try_parse!(rest, s!(parse_usize ));
let (rest, start_brk) = try_parse!(rest, s!(parse_usize ));
let (rest, arg_start) = try_parse!(rest, s!(parse_usize ));
let (rest, arg_end) = try_parse!(rest, s!(parse_usize ));
let (rest, env_start) = try_parse!(rest, s!(parse_usize ));
let (rest, env_end) = try_parse!(rest, s!(parse_usize ));
let (rest, exit_code) = try_parse!(rest, l!(parse_i32 ));
IResult::Done(rest, Stat {
pid : pid,
command : command,
state : state,
ppid : ppid,
pgrp : pgrp,
session : session,
tty_nr : tty_nr,
tty_pgrp : tty_pgrp,
flags : flags,
minflt : minflt,
cminflt : cminflt,
majflt : majflt,
cmajflt : cmajflt,
utime : utime,
stime : stime,
cutime : cutime,
cstime : cstime,
priority : priority,
nice : nice,
num_threads : num_threads,
start_time : start_time,
vsize : vsize,
rss : rss,
rsslim : rsslim,
start_code : start_code,
end_code : end_code,
startstack : startstack,
kstkeep : kstkeep,
kstkeip : kstkeip,
signal : signal,
blocked : blocked,
sigignore : sigignore,
sigcatch : sigcatch,
wchan : wchan,
exit_signal : exit_signal,
processor : processor,
rt_priority : rt_priority,
policy : policy,
delayacct_blkio_ticks : delayacct_blkio_ticks,
guest_time : guest_time,
cguest_time : cguest_time,
start_data : start_data,
end_data : end_data,
start_brk : start_brk,
arg_start : arg_start,
arg_end : arg_end,
env_start : env_start,
env_end : env_end,
exit_code : exit_code,
})
}
fn stat_file(file: &mut File) -> Result<Stat> {
let mut buf = [0; 1024];
map_result(parse_stat(try!(read_to_end(file, &mut buf))))
}
pub fn stat(pid: pid_t) -> Result<Stat> {
stat_file(&mut try!(File::open(&format!("/proc/{}/stat", pid))))
}
pub fn stat_self() -> Result<Stat> {
stat_file(&mut try!(File::open("/proc/self/stat")))
}
pub fn stat_task(process_id: pid_t, thread_id: pid_t) -> Result<Stat> {
stat_file(&mut try!(File::open(&format!("/proc/{}/task/{}/stat", process_id, thread_id))))
}
#[cfg(test)]
pub mod tests {
use parsers::tests::unwrap;
use pid::State;
use super::{
parse_command,
parse_stat,
stat,
stat_self
};
#[test]
fn test_parse_command() {
assert_eq!("cat", &unwrap(parse_command(b"(cat)")));
assert_eq!("cat ) (( )) ", &unwrap(parse_command(b"(cat ) (( )) )")));
}
#[test]
fn test_stat() {
stat_self().unwrap();
stat(1).unwrap();
}
#[test]
fn test_parse_stat() {
let text = b"19853 (cat) R 19435 19853 19435 34819 19853 4218880 98 0 0 0 0 0 0 0 20 0 1 0 \
279674171 112295936 180 18446744073709551615 4194304 4238772 140736513999744 \
140736513999080 139957028908944 0 0 0 0 0 0 0 17 15 0 0 0 0 0 6339648 6341408 \
17817600 140736514006312 140736514006332 140736514006332 140736514007019 0\n";
let stat = unwrap(parse_stat(text));
assert_eq!(19853, stat.pid);
assert_eq!("cat", &stat.command);
assert_eq!(State::Running, stat.state);
assert_eq!(19435, stat.ppid);
assert_eq!(19853, stat.pgrp);
assert_eq!(19435, stat.session);
assert_eq!(34819, stat.tty_nr);
assert_eq!(19853, stat.tty_pgrp);
assert_eq!(4218880, stat.flags);
assert_eq!(98, stat.minflt);
assert_eq!(0, stat.cminflt);
assert_eq!(0, stat.majflt);
assert_eq!(0, stat.cmajflt);
assert_eq!(0, stat.utime);
assert_eq!(0, stat.stime);
assert_eq!(0, stat.cutime);
assert_eq!(0, stat.cstime);
assert_eq!(20, stat.priority);
assert_eq!(0, stat.nice);
assert_eq!(1, stat.num_threads);
assert_eq!(279674171, stat.start_time);
assert_eq!(112295936, stat.vsize);
assert_eq!(180, stat.rss);
assert_eq!(18446744073709551615, stat.rsslim);
assert_eq!(4194304, stat.start_code);
assert_eq!(4238772, stat.end_code);
assert_eq!(140736513999744, stat.startstack);
assert_eq!(140736513999080, stat.kstkeep);
assert_eq!(139957028908944, stat.kstkeip);
assert_eq!(0, stat.signal);
assert_eq!(0, stat.blocked);
assert_eq!(0, stat.sigignore);
assert_eq!(0, stat.sigcatch);
assert_eq!(0, stat.wchan);
assert_eq!(17, stat.exit_signal);
assert_eq!(15, stat.processor);
assert_eq!(0, stat.rt_priority);
assert_eq!(0, stat.policy);
assert_eq!(0, stat.delayacct_blkio_ticks);
assert_eq!(0, stat.guest_time);
assert_eq!(0, stat.cguest_time);
assert_eq!(6339648, stat.start_data);
assert_eq!(6341408, stat.end_data);
assert_eq!(17817600, stat.start_brk);
assert_eq!(140736514006312, stat.arg_start);
assert_eq!(140736514006332, stat.arg_end);
assert_eq!(140736514006332, stat.env_start);
assert_eq!(140736514007019, stat.env_end);
assert_eq!(0, stat.exit_code);
}
}
#[cfg(all(test, rustc_nightly))]
mod benches {
extern crate test;
use std::fs::File;
use parsers::read_to_end;
use super::{parse_stat, stat};
#[bench]
fn bench_stat(b: &mut test::Bencher) {
b.iter(|| test::black_box(stat(1)));
}
#[bench]
fn bench_stat_parse(b: &mut test::Bencher) {
let mut buf = [0; 256];
let stat = read_to_end(&mut File::open("/proc/1/stat").unwrap(), &mut buf).unwrap();
b.iter(|| test::black_box(parse_stat(stat)));
}
}