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}