self_meter/
report.rs

1use std::time::{Duration};
2use std::collections::hash_map::Iter;
3
4use {Pid, Meter, Report, Snapshot, ThreadReport};
5
6
7/// Iterator over thread reports returned by ``Meter::thread_report``
8pub struct ThreadReportIter<'a> {
9    threads: Iter<'a,Pid, String>,
10    last: &'a Snapshot,
11    prev: &'a Snapshot,
12    centisecs: f32,
13}
14
15fn duration_from_ms(ms: u64) -> Duration {
16    Duration::new(ms / 1000, ((ms % 1000) * 1000_000) as u32)
17}
18
19
20impl Meter {
21    /// Get report of the last scan interval
22    ///
23    /// We need at least two scans to measure CPU usage, so this method
24    /// returns None if less than two scans were done ever in the past.
25    pub fn report(&self) -> Option<Report> {
26        if self.snapshots.len() < 2 {
27            return None;
28        }
29        let n = self.snapshots.len();
30        let last = &self.snapshots[n-1];
31        let prev = &self.snapshots[n-2];
32        let lpro = &last.process;
33        let ppro = &prev.process;
34        let centisecs = (last.uptime - prev.uptime) as f32;
35        let secs = centisecs / 100.0;
36        let mut cpu_usage = 100.0 * (1.0 -
37                (last.idle_time - prev.idle_time) as f32 /
38                (centisecs * self.num_cpus as f32));
39        if cpu_usage < 0. {  // sometimes we get inaccuracy
40            cpu_usage = 0.;
41        }
42        Some(Report {
43            timestamp: last.timestamp,
44            duration: last.instant - prev.instant,
45            start_time: self.start_time,
46            system_uptime: duration_from_ms(last.uptime * 10),  // centisecs
47            global_cpu_usage: cpu_usage,
48            process_cpu_usage: 100.0 *
49                (lpro.user_time + lpro.system_time -
50                 (ppro.user_time + ppro.system_time)) as f32 / centisecs,
51            gross_cpu_usage: 100.0 *
52                ((lpro.user_time  + lpro.system_time +
53                  lpro.child_user_time + lpro.child_system_time) -
54                 (ppro.user_time + ppro.system_time +
55                  ppro.child_user_time + ppro.child_system_time)) as f32 /
56                centisecs,
57            memory_rss: last.memory_rss,
58            memory_virtual: last.memory_virtual,
59            memory_swap: last.memory_swap,
60            memory_rss_peak: self.memory_rss_peak,
61            memory_virtual_peak: last.memory_virtual_peak,
62            memory_swap_peak: self.memory_swap_peak,
63            disk_read: (last.read_disk_bytes - prev.read_disk_bytes) as f32
64                / secs,
65            disk_write: (last.write_disk_bytes - prev.write_disk_bytes) as f32
66                / secs,
67            disk_cancelled: (last.write_cancelled_bytes -
68                             prev.write_cancelled_bytes) as f32 / secs,
69            io_read: (last.read_bytes - prev.read_bytes) as f32 / secs,
70            io_write: (last.write_bytes - prev.write_bytes) as f32 / secs,
71            io_read_ops: (last.read_ops - prev.read_ops) as f32 / secs,
72            io_write_ops: (last.write_ops - prev.write_ops) as f32 / secs,
73        })
74    }
75    /// Returns iterator over reports for threads
76    ///
77    /// Note: each thread must be registered with `Meter::track_thread` or
78    /// `Meter::track_current_thread` to be tracked here.
79    ///
80    /// We need at least two scans to measure CPU usage, so this method
81    /// returns None if less than two scans were done ever in the past.
82    pub fn thread_report(&self) -> Option<ThreadReportIter> {
83        if self.snapshots.len() < 2 {
84            return None;
85        }
86        let n = self.snapshots.len();
87        let last = &self.snapshots[n-1];
88        let prev = &self.snapshots[n-2];
89        let centisecs = (last.uptime - prev.uptime) as f32;
90        Some(ThreadReportIter {
91            threads: self.thread_names.iter(),
92            last: last,
93            prev: prev,
94            centisecs: centisecs,
95        })
96    }
97}
98
99impl<'a> Iterator for ThreadReportIter<'a> {
100    type Item = (&'a str, ThreadReport);
101    fn next(&mut self) -> Option<(&'a str, ThreadReport)> {
102        while let Some((&pid, name)) = self.threads.next() {
103            let lth = if let Some(thread) = self.last.threads.get(&pid) {
104                thread
105            } else {
106                continue;  // not enough stats for a thread yet
107            };
108            let pth = if let Some(thread) = self.prev.threads.get(&pid) {
109                thread
110            } else {
111                continue;  // not enough stats for a thread yet
112            };
113            let udelta = lth.user_time - pth.user_time;
114            let sdelta = lth.system_time - pth.system_time;
115            return Some((&name[..], ThreadReport {
116                cpu_usage: 100.0 * (udelta + sdelta) as f32 / self.centisecs,
117                system_cpu: 100.0 * sdelta as f32 / self.centisecs,
118                user_cpu: 100.0 * udelta as f32 / self.centisecs,
119            }))
120        }
121        None
122    }
123    fn size_hint(&self) -> (usize, Option<usize>) {
124        let (_min, max) = self.threads.size_hint();
125        // unfortunately we skip non-scanned threads, so we must assume that
126        // minimum size is zero
127        return (0, max);
128    }
129}