linux_taskstats/
format.rs

1use crate::TaskStats;
2use prettytable::{self as ptable, row};
3use std::io::{self, Write};
4
5pub trait HeaderFormat {
6    fn format(&self, tid: u32) -> String;
7}
8
9#[derive(Default)]
10pub struct DefaultHeaderFormat {}
11
12impl DefaultHeaderFormat {
13    pub fn new() -> Self {
14        Default::default()
15    }
16}
17
18impl HeaderFormat for DefaultHeaderFormat {
19    fn format(&self, tid: u32) -> String {
20        format!("TID: {}", tid)
21    }
22}
23
24pub struct Printer<H: HeaderFormat> {
25    header_format: H,
26}
27
28impl<H: HeaderFormat> Printer<H> {
29    pub fn new(header_format: H) -> Self {
30        Self { header_format }
31    }
32
33    pub fn print_summary_lines<W: Write>(
34        &self,
35        out: &mut W,
36        stats: &[TaskStats],
37    ) -> io::Result<()> {
38        let mut table = ptable::Table::new();
39        table.set_format(*ptable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
40        table.add_row(row![
41            c =>
42            "Task",
43            "utime",
44            "stime",
45            "rss",
46            "vmem",
47            "read",
48            "write",
49            "d:cpu",
50            "d:bio",
51            "d:swap",
52            "d:reclaim"
53        ]);
54        for ts in stats {
55            table.add_row(row![
56                l->self.header_format.format(ts.tid),
57                r->ts.cpu.utime_total.as_micros(),
58                r->ts.cpu.stime_total.as_micros(),
59                r->ts.memory.rss_total,
60                r->ts.memory.virt_total,
61                r->ts.io.read_bytes,
62                r->ts.io.write_bytes,
63                r->ts.delays.cpu.delay_total.as_nanos(),
64                r->ts.delays.blkio.delay_total.as_nanos(),
65                r->ts.delays.swapin.delay_total.as_nanos(),
66                r->ts.delays.freepages.delay_total.as_nanos()
67            ]);
68        }
69        table.print(out)?;
70        Ok(())
71    }
72
73    pub fn print_delay_lines<W: Write>(&self, out: &mut W, stats: &[TaskStats]) -> io::Result<()> {
74        let mut table = ptable::Table::new();
75        table.set_format(*ptable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
76        table.add_row(row![
77            c =>
78            "Task",
79            "cpu avg",
80            "blkio avg",
81            "swapin avg",
82            "reclaim avg",
83            "cpu total",
84            "blkio total",
85            "swapin total",
86            "reclaim total",
87        ]);
88        for ts in stats {
89            let d = ts.delays;
90            table.add_row(row![
91                l->self.header_format.format(ts.tid),
92                r->d.cpu.delay_total.as_nanos() as u64 / d.cpu.count.max(1),
93                r->d.blkio.delay_total.as_nanos() as u64 / d.blkio.count.max(1),
94                r->d.swapin.delay_total.as_nanos() as u64 / d.swapin.count.max(1),
95                r->d.freepages.delay_total.as_nanos() as u64 / d.freepages.count.max(1),
96                r->d.cpu.delay_total.as_nanos(),
97                r->d.blkio.delay_total.as_nanos(),
98                r->d.swapin.delay_total.as_nanos(),
99                r->d.freepages.delay_total.as_nanos(),
100            ]);
101        }
102        table.print(out)?;
103        Ok(())
104    }
105
106    pub fn print_full<W: Write>(&self, out: &mut W, stats: &[TaskStats]) -> io::Result<()> {
107        for ts in stats {
108            writeln!(out, "=== {} ===", self.header_format.format(ts.tid))?;
109            let cpu = ts.cpu;
110            writeln!(out, "--- CPU ---")?;
111            writeln!(out, "User Time (us): {}", cpu.utime_total.as_micros())?;
112            writeln!(out, "System Time (us): {}", cpu.stime_total.as_micros())?;
113            writeln!(out, "Real Time (us): {}", cpu.real_time_total.as_micros())?;
114            writeln!(
115                out,
116                "Virtual Time (us): {}",
117                cpu.virtual_time_total.as_micros()
118            )?;
119            let mem = ts.memory;
120            writeln!(out, "--- Memory ---")?;
121            writeln!(out, "RSS (MB-usec): {}", mem.rss_total)?;
122            writeln!(out, "Virtual (MB-usec): {}", mem.virt_total)?;
123            writeln!(
124                out,
125                "Page Faults (minor:major): {}:{}",
126                mem.minor_faults, mem.major_faults
127            )?;
128            let io = ts.io;
129            writeln!(out, "--- IO ---")?;
130            writeln!(out, "Read (bytes): {}", io.read_bytes)?;
131            writeln!(out, "Write (bytes): {}", io.write_bytes)?;
132            writeln!(
133                out,
134                "Syscalls (read:write): {}:{}",
135                io.read_syscalls, io.write_syscalls
136            )?;
137            let bio = ts.blkio;
138            writeln!(out, "--- Block Device IO ---")?;
139            writeln!(out, "Read (bytes): {}", bio.read_bytes)?;
140            writeln!(out, "Write (bytes): {}", bio.write_bytes)?;
141            writeln!(
142                out,
143                "Write Cancelled (bytes): {}",
144                bio.cancelled_write_bytes
145            )?;
146            let cswt = ts.ctx_switches;
147            writeln!(out, "--- Context Switches ---")?;
148            writeln!(
149                out,
150                "Voluntary:Non-voluntary: {}:{}",
151                cswt.voluntary, cswt.non_voluntary
152            )?;
153            let delays = ts.delays;
154            writeln!(out, "--- Delays ---")?;
155            writeln!(
156                out,
157                "CPU Total(nsec)/Count: {}/{}",
158                delays.cpu.delay_total.as_nanos(),
159                delays.cpu.count
160            )?;
161            writeln!(
162                out,
163                "BlkIO Total(nsec)/Count: {}/{}",
164                delays.blkio.delay_total.as_nanos(),
165                delays.blkio.count
166            )?;
167            writeln!(
168                out,
169                "SwapIn Total(nsec)/Count: {}/{}",
170                delays.swapin.delay_total.as_nanos(),
171                delays.swapin.count
172            )?;
173            writeln!(
174                out,
175                "Mem Reclaim Total(nsec)/Count: {}/{}",
176                delays.freepages.delay_total.as_nanos(),
177                delays.freepages.count
178            )?;
179        }
180        Ok(())
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    use crate::model::*;
188    use std::time::Duration;
189
190    const TS: TaskStats = TaskStats {
191        tid: 1234,
192        cpu: Cpu {
193            utime_total: Duration::from_micros(12),
194            stime_total: Duration::from_micros(34),
195            real_time_total: Duration::from_micros(56),
196            virtual_time_total: Duration::from_micros(78),
197        },
198        memory: Memory {
199            rss_total: 12,
200            virt_total: 34,
201            minor_faults: 56,
202            major_faults: 78,
203        },
204        io: Io {
205            read_bytes: 12,
206            write_bytes: 34,
207            read_syscalls: 56,
208            write_syscalls: 78,
209        },
210        blkio: BlkIo {
211            read_bytes: 12,
212            write_bytes: 34,
213            cancelled_write_bytes: 56,
214        },
215        ctx_switches: ContextSwitches {
216            voluntary: 12,
217            non_voluntary: 34,
218        },
219        delays: Delays {
220            cpu: DelayStat {
221                count: 12,
222                delay_total: Duration::from_nanos(34),
223            },
224            blkio: DelayStat {
225                count: 56,
226                delay_total: Duration::from_nanos(78),
227            },
228            swapin: DelayStat {
229                count: 123,
230                delay_total: Duration::from_nanos(456),
231            },
232            freepages: DelayStat {
233                count: 789,
234                delay_total: Duration::from_nanos(1234),
235            },
236        },
237        inner_buf: [0u8; TASKSTATS_SIZE],
238    };
239
240    #[test]
241    fn test_print_summary_lines() {
242        let expect =
243            "   Task    | utime | stime | rss | vmem | read | write | d:cpu | d:bio | d:swap | d:reclaim 
244 TID: 1234 |    12 |    34 |  12 |   34 |   12 |    34 |    34 |    78 |    456 |      1234 
245 TID: 1234 |    12 |    34 |  12 |   34 |   12 |    34 |    34 |    78 |    456 |      1234 
246";
247
248        let printer = Printer::new(DefaultHeaderFormat::new());
249        let mut out = Vec::new();
250        printer.print_summary_lines(&mut out, &[TS, TS]).unwrap();
251        assert_eq!(expect, String::from_utf8(out).unwrap());
252    }
253
254    #[test]
255    fn test_print_delay_lines() {
256        let expect = "   Task    | cpu avg | blkio avg | swapin avg | reclaim avg | cpu total | blkio total | swapin total | reclaim total 
257 TID: 1234 |       2 |         1 |          3 |           1 |        34 |          78 |          456 |          1234 
258 TID: 1234 |       2 |         1 |          3 |           1 |        34 |          78 |          456 |          1234 
259";
260
261        let printer = Printer::new(DefaultHeaderFormat::new());
262        let mut out = Vec::new();
263        printer.print_delay_lines(&mut out, &[TS, TS]).unwrap();
264        assert_eq!(expect, String::from_utf8(out).unwrap());
265    }
266
267    #[test]
268    fn test_print_full() {
269        let expect = "=== TID: 1234 ===
270--- CPU ---
271User Time (us): 12
272System Time (us): 34
273Real Time (us): 56
274Virtual Time (us): 78
275--- Memory ---
276RSS (MB-usec): 12
277Virtual (MB-usec): 34
278Page Faults (minor:major): 56:78
279--- IO ---
280Read (bytes): 12
281Write (bytes): 34
282Syscalls (read:write): 56:78
283--- Block Device IO ---
284Read (bytes): 12
285Write (bytes): 34
286Write Cancelled (bytes): 56
287--- Context Switches ---
288Voluntary:Non-voluntary: 12:34
289--- Delays ---
290CPU Total(nsec)/Count: 34/12
291BlkIO Total(nsec)/Count: 78/56
292SwapIn Total(nsec)/Count: 456/123
293Mem Reclaim Total(nsec)/Count: 1234/789
294";
295
296        let printer = Printer::new(DefaultHeaderFormat::new());
297        let mut out = Vec::new();
298        printer.print_full(&mut out, &[TS]).unwrap();
299        assert_eq!(expect, String::from_utf8(out).unwrap());
300    }
301}