1use std::io::{Read, Seek, SeekFrom};
2use std::fs::File;
3use std::fmt::Write;
4use std::num::ParseIntError;
5use std::time::{Instant, SystemTime};
6use std::collections::HashMap;
7
8use {Meter, Snapshot, ThreadInfo, Pid, Error};
9use error::{UptimeError, StatError, StatusError, IoStatError};
10
11
12impl Meter {
13 pub fn scan(&mut self) -> Result<(), Error> {
18 let mut snap = if self.snapshots.len() >= self.num_snapshots {
21 self.snapshots.pop_front().unwrap()
22 } else {
23 Snapshot::new(&self.thread_names)
24 };
25 snap.timestamp = SystemTime::now();
26 snap.instant = Instant::now();
27
28 try!(self.read_cpu_times(&mut snap.process,
31 &mut snap.threads,
32 &mut snap.uptime, &mut snap.idle_time));
33
34 try!(self.read_memory(&mut snap));
35 try!(self.read_io(&mut snap));
36
37 if snap.memory_rss > self.memory_rss_peak {
38 self.memory_rss_peak = snap.memory_rss;
39 }
40 if snap.memory_swap > self.memory_swap_peak {
41 self.memory_swap_peak = snap.memory_swap;
42 }
43
44 self.snapshots.push_back(snap);
45 Ok(())
46 }
47
48 #[cfg(target_os="linux")]
49 fn read_cpu_times(&mut self, process: &mut ThreadInfo,
50 threads: &mut HashMap<Pid, ThreadInfo>,
51 uptime: &mut u64, idle_time: &mut u64)
52 -> Result<(), Error>
53 {
54 self.text_buf.truncate(0);
55 try!(File::open("/proc/uptime")
56 .and_then(|mut f| f.read_to_string(&mut self.text_buf))
57 .map_err(|e| Error::Uptime(e.into())));
58 {
59 let mut iter = self.text_buf.split_whitespace();
60 let seconds = try!(iter.next()
61 .ok_or(Error::Uptime(UptimeError::BadFormat)));
62 let idle_sec = try!(iter.next()
63 .ok_or(Error::Uptime(UptimeError::BadFormat)));
64 *uptime = try!(parse_uptime(seconds));
65 *idle_time = try!(parse_uptime(idle_sec));
66 }
67 try!(read_stat(&mut self.text_buf, "/proc/self/stat", process)
68 .map_err(Error::Stat));
69 for (&tid, _) in &self.thread_names {
70 self.path_buf.truncate(0);
71 write!(&mut self.path_buf,
72 "/proc/self/task/{}/stat", tid).unwrap();
73 try!(read_stat(&mut self.text_buf, &self.path_buf[..],
74 threads.entry(tid).or_insert_with(ThreadInfo::new))
75 .map_err(|e| Error::ThreadStat(tid, e)));
76 }
77 Ok(())
78 }
79
80 #[cfg(not(target_os="linux"))]
81 fn read_cpu_times(&mut self, process: &mut ThreadInfo,
82 threads: &mut HashMap<Pid, ThreadInfo>,
83 uptime: &mut u64, idle_time: &mut u64)
84 -> Result<(), Error>
85 {
86 Ok(())
87 }
88
89 #[cfg(target_os="linux")]
90 fn read_memory(&mut self, snap: &mut Snapshot)
91 -> Result<(), StatusError>
92 {
93 self.text_buf.truncate(0);
94 try!(File::open("/proc/self/status")
95 .and_then(|mut f| f.read_to_string(&mut self.text_buf)));
96 for line in self.text_buf.lines() {
97 let mut pairs = line.split(':');
98 match (pairs.next(), pairs.next()) {
99 (Some("VmPeak"), Some(text))
100 => snap.memory_virtual_peak = try!(parse_memory(text)),
101 (Some("VmSize"), Some(text))
102 => snap.memory_virtual = try!(parse_memory(text)),
103 (Some("VmRSS"), Some(text))
104 => snap.memory_rss = try!(parse_memory(text)),
105 (Some("VmSwap"), Some(text))
106 => snap.memory_swap = try!(parse_memory(text)),
107 _ => {}
108 }
109 }
110 Ok(())
111 }
112
113 #[cfg(not(target_os="linux"))]
114 fn read_memory(&mut self, snap: &mut Snapshot)
115 -> Result<(), StatusError>
116 {
117 Ok(())
118 }
119
120 #[cfg(target_os="linux")]
121 fn read_io(&mut self, snap: &mut Snapshot)
122 -> Result<(), Error>
123 {
124 let err = &|e: ParseIntError| Error::IoStat(e.into());
125 self.text_buf.truncate(0);
126 self.io_file.seek(SeekFrom::Start(0))
127 .map_err(IoStatError::Io)?;
128 self.io_file.read_to_string(&mut self.text_buf)
129 .map_err(IoStatError::Io)?;
130 for line in self.text_buf.lines() {
131 let mut pairs = line.split(':');
132 match (pairs.next(), pairs.next().map(|x| x.trim())) {
133 (Some("rchar"), Some(text))
134 => snap.read_bytes = text.parse().map_err(err)?,
135 (Some("wchar"), Some(text))
136 => snap.write_bytes = text.parse().map_err(err)?,
137 (Some("syscr"), Some(text))
138 => snap.read_ops = text.parse().map_err(err)?,
139 (Some("syscw"), Some(text))
140 => snap.write_ops = text.parse().map_err(err)?,
141 (Some("read_bytes"), Some(text))
142 => snap.read_disk_bytes = text.parse().map_err(err)?,
143 (Some("write_bytes"), Some(text))
144 => snap.write_disk_bytes = text.parse().map_err(err)?,
145 (Some("cancelled_write_bytes"), Some(text)) => {
146 snap.write_cancelled_bytes = text.parse().map_err(err)?;
147 }
148 _ => {}
149 }
150 }
151 Ok(())
152 }
153 #[cfg(not(target_os="linux"))]
154 fn read_io(&mut self, snap: &mut Snapshot)
155 -> Result<(), Error>
156 {
157 Ok(())
159 }
160
161}
162
163fn parse_memory(value: &str) -> Result<u64, StatusError> {
164 let mut pair = value.split_whitespace();
165 let value = try!(try!(pair.next().ok_or(StatusError::BadFormat))
166 .parse::<u64>());
167 match pair.next() {
168 Some("kB") => Ok(value * 1024),
169 _ => Err(StatusError::BadUnit),
170 }
171}
172
173pub fn parse_uptime(value: &str) -> Result<u64, UptimeError> {
174 if value.len() <= 3 {
175 return Err(UptimeError::BadFormat);
176 }
177 let dot = value.find('.').ok_or(UptimeError::BadFormat)?;
178 let (integer, decimals) = value.split_at(dot);
179 if decimals.len() == 1+1 {
180 Ok(try!(integer.parse::<u64>()) * 100 +
181 try!(decimals[1..].parse::<u64>())*10)
182 } else if decimals.len() == 1+2 {
183 Ok(try!(integer.parse::<u64>()) * 100 +
184 try!(decimals[1..].parse::<u64>()))
185 } else {
186 Err(UptimeError::BadFormat)
187 }
188}
189
190fn read_stat(text_buf: &mut String, path: &str, thread_info: &mut ThreadInfo)
191 -> Result<(), StatError>
192{
193 text_buf.truncate(0);
194 try!(File::open(path)
195 .and_then(|mut f| f.read_to_string(text_buf)));
196 let right_paren = try!(text_buf.rfind(')')
197 .ok_or(StatError::BadFormat));
198 let mut iter = text_buf[right_paren+1..].split_whitespace();
199 thread_info.user_time = try!(
200 try!(iter.nth(11).ok_or(StatError::BadFormat)).parse());
201 thread_info.system_time = try!(
202 try!(iter.next().ok_or(StatError::BadFormat)).parse());
203 thread_info.child_user_time = try!(
204 try!(iter.next().ok_or(StatError::BadFormat)).parse());
205 thread_info.child_system_time = try!(
206 try!(iter.next().ok_or(StatError::BadFormat)).parse());
207 Ok(())
208}
209
210impl ThreadInfo {
211 fn new() -> ThreadInfo {
212 ThreadInfo {
213 user_time: 0,
214 system_time: 0,
215 child_user_time: 0,
216 child_system_time: 0,
217 }
218 }
219}
220
221impl Snapshot {
222 fn new(threads: &HashMap<Pid, String>) -> Snapshot {
223 Snapshot {
224 timestamp: SystemTime::now(),
225 instant: Instant::now(),
226 uptime: 0,
227 idle_time: 0,
228 process: ThreadInfo::new(),
229 memory_rss: 0,
230 memory_virtual: 0,
231 memory_virtual_peak: 0,
232 memory_swap: 0,
233 read_bytes: 0,
234 write_bytes: 0,
235 read_ops: 0,
236 write_ops: 0,
237 read_disk_bytes: 0,
238 write_disk_bytes: 0,
239 write_cancelled_bytes: 0,
240 threads: threads.iter()
241 .map(|(&pid, _)| (pid, ThreadInfo::new()))
242 .collect(),
243 }
244 }
245}
246
247#[cfg(test)]
248mod test {
249 use super::parse_uptime;
250
251 #[test]
252 fn normal_uptime() {
253 assert_eq!(parse_uptime("1927830.69").unwrap(), 192783069);
254 }
255 #[test]
256 fn one_zero_uptime() {
257 assert_eq!(parse_uptime("4780.0").unwrap(), 478000);
258 }
259}