Remove deprecated --extra argument
[deliverable/lttng-analyses.git] / lttnganalysescli / lttnganalysescli / io.py
CommitLineData
4ed24f86
JD
1#!/usr/bin/env python3
2#
3# The MIT License (MIT)
4#
a3fa57c0 5# Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
6cd52af3 6# 2015 - Antoine Busque <abusque@efficios.com>
4ed24f86
JD
7#
8# Permission is hereby granted, free of charge, to any person obtaining a copy
9# of this software and associated documentation files (the "Software"), to deal
10# in the Software without restriction, including without limitation the rights
11# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12# copies of the Software, and to permit persons to whom the Software is
13# furnished to do so, subject to the following conditions:
14#
15# The above copyright notice and this permission notice shall be included in
16# all copies or substantial portions of the Software.
17#
18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24# SOFTWARE.
25
a9c9a2a6 26from .command import Command
6cd52af3 27import lttnganalyses.io
a9c9a2a6
JD
28from linuxautomaton import common, sv
29from ascii_graph import Pyasciigraph
30import operator
31import statistics
32
33
087e5d00 34class IoAnalysisCommand(Command):
a9c9a2a6
JD
35 _VERSION = '0.1.0'
36 _DESC = """The I/O command."""
37
ef49c8de
AB
38 _LATENCY_STATS_FORMAT = '{:<14} {:>14} {:>14} {:>14} {:>14} {:>14}'
39 _SECTION_SEPARATOR_STRING = '-' * 89
40
a9c9a2a6
JD
41 def __init__(self):
42 super().__init__(self._add_arguments,
43 enable_proc_filter_args=True,
44 enable_max_min_args=True,
45 enable_max_min_size_arg=True,
1c0f0e3c 46 enable_log_arg=True)
a9c9a2a6
JD
47
48 def _validate_transform_args(self):
1c0f0e3c
JD
49 self._arg_usage = self._args.usage
50 self._arg_stats = self._args.latencystats
275bdbb4 51 self._arg_latencytop = self._args.latencytop
1c0f0e3c
JD
52 self._arg_freq = self._args.latencyfreq
53 self._arg_freq_resolution = self._args.freq_resolution
54
275bdbb4 55 def _default_args(self, stats, log, freq, usage, latencytop):
1c0f0e3c
JD
56 if stats:
57 self._arg_stats = True
58 if log:
59 self._arg_log = True
60 if freq:
61 self._arg_freq = True
62 if usage:
63 self._arg_usage = True
275bdbb4
JD
64 if latencytop:
65 self._arg_latencytop = True
1c0f0e3c 66
275bdbb4
JD
67 def run(self, stats=False, log=False, freq=False, usage=False,
68 latencytop=False):
a9c9a2a6
JD
69 # parse arguments first
70 self._parse_args()
71 # validate, transform and save specific arguments
72 self._validate_transform_args()
1c0f0e3c 73 # handle the default args for different executables
275bdbb4 74 self._default_args(stats, log, freq, usage, latencytop)
a9c9a2a6
JD
75 # open the trace
76 self._open_trace()
77 # create the appropriate analysis/analyses
78 self._create_analysis()
79 # run the analysis
7a68fd89 80 self._run_analysis(self._reset_total, self._refresh)
a9c9a2a6 81 # print results
da6d0842 82 self._print_results(self.start_ns, self.trace_end_ts)
a9c9a2a6
JD
83 # close the trace
84 self._close_trace()
85
1c0f0e3c
JD
86 def run_stats(self):
87 self.run(stats=True)
88
275bdbb4
JD
89 def run_latencytop(self):
90 self.run(latencytop=True)
91
1c0f0e3c
JD
92 def run_log(self):
93 self.run(log=True)
94
95 def run_freq(self):
96 self.run(freq=True)
97
98 def run_usage(self):
99 self.run(usage=True)
100
a9c9a2a6 101 def _create_analysis(self):
6cd52af3 102 self._analysis = lttnganalyses.io.IoAnalysis(self.state)
a9c9a2a6 103
a9c9a2a6 104 def _refresh(self, begin, end):
da6d0842 105 self._print_results(begin, end)
a9c9a2a6
JD
106 self._reset_total(end)
107
882fcd19 108 # Filter predicates
882fcd19
AB
109 def _filter_size(self, size):
110 if size is None:
111 return True
112 if self._arg_maxsize is not None and size > self._arg_maxsize:
113 return False
114 if self._arg_minsize is not None and size < self._arg_minsize:
115 return False
116 return True
117
118 def _filter_latency(self, duration):
119 if self._arg_max is not None and (duration/1000) > self._arg_max:
120 return False
121 if self._arg_min is not None and (duration/1000) < self._arg_min:
122 return False
123 return True
124
ef49c8de 125 def _filter_time_range(self, begin, end):
acf400da 126 return not (self._arg_begin and self._arg_end and end and
ef49c8de
AB
127 begin > self._arg_end)
128
129 def _filter_io_request(self, io_rq):
130 proc = self._analysis.tids[io_rq.tid]
131 return self._filter_process(proc) and \
132 self._filter_size(io_rq.size) and \
133 self._filter_latency(io_rq.duration) and \
134 self._filter_time_range(io_rq.begin_ts, io_rq.end_ts)
135
88cfa546
AB
136 def _print_ascii_graph(self, input_list, get_datum_cb, graph_label,
137 graph_args={}):
138 """Print an ascii graph for given data
139
140 This method wraps the ascii_graph module and facilitates the
141 printing of a graph with a limited number of lines.
142
143 Args:
144 input_list (list): A list of objects from which the data
145 for the graph will be generated.
146
147 get_datum_cb (function): function that takes a single
148 object from the input list as an argument, and returns a
149 datum tuple for the graph, of the form (string, int). The
150 string element is printed as is in the graph, and the int
151 is the numeric value corresponding to this graph entry.
152
153 graph_label (string): Label used to identify the printed
154 graph.
155
156 graph_args (dict, optional): Dict of keyword args to be
157 passed to the graph() function as is.
158 """
159 count = 0
160 limit = self._arg_limit
161 graph = Pyasciigraph()
162 data = []
163
164 for elem in input_list:
165 datum = get_datum_cb(elem)
166 if datum is not None:
167 data.append(datum)
168 count += 1
169 if limit is not None and count >= limit:
170 break
171
172 for line in graph.graph(graph_label, data, **graph_args):
173 print(line)
174
91765fd2
AB
175 # I/O Top output methods
176 def _get_read_datum(self, proc_stats):
177 if not self._filter_process(proc_stats):
178 return None
179
180 if proc_stats.pid is None:
181 pid_str = 'unknown (tid=%d)' % (proc_stats.tid)
182 else:
183 pid_str = str(proc_stats.pid)
184
185 format_str = '{:>10} {:<25} {:>9} file {:>9} net {:>9} unknown'
186 output_str = format_str.format(
187 common.convert_size(proc_stats.total_read, padding_after=True),
188 '%s (%s)' % (proc_stats.comm, pid_str),
189 common.convert_size(proc_stats.disk_read, padding_after=True),
190 common.convert_size(proc_stats.net_read, padding_after=True),
191 common.convert_size(proc_stats.unk_read, padding_after=True))
192
193 return (output_str, proc_stats.total_read)
194
195 def _get_write_datum(self, proc_stats):
196 if not self._filter_process(proc_stats):
197 return None
198
199 if proc_stats.pid is None:
200 pid_str = 'unknown (tid=%d)' % (proc_stats.tid)
201 else:
202 pid_str = str(proc_stats.pid)
203
204 format_str = '{:>10} {:<25} {:>9} file {:>9} net {:>9} unknown'
205 output_str = format_str.format(
206 common.convert_size(proc_stats.total_write, padding_after=True),
207 '%s (%s)' % (proc_stats.comm, pid_str),
208 common.convert_size(proc_stats.disk_write, padding_after=True),
209 common.convert_size(proc_stats.net_write, padding_after=True),
210 common.convert_size(proc_stats.unk_write, padding_after=True))
211
212 return (output_str, proc_stats.total_write)
213
214 def _get_block_read_datum(self, proc_stats):
215 if not self._filter_process(proc_stats) or proc_stats.block_read == 0:
216 return None
217
218 comm = proc_stats.comm
219 if not comm:
220 comm = 'unknown'
221
222 if proc_stats.pid is None:
223 pid_str = 'unknown (tid=%d)' % (proc_stats.tid)
224 else:
225 pid_str = str(proc_stats.pid)
226
227 format_str = '{:>10} {:<22}'
228 output_str = format_str.format(
229 common.convert_size(proc_stats.block_read, padding_after=True),
230 '%s (pid=%s)' % (comm, pid_str))
231
232 return (output_str, proc_stats.block_read)
233
234 def _get_block_write_datum(self, proc_stats):
235 if not self._filter_process(proc_stats) or \
236 proc_stats.block_write == 0:
237 return None
238
239 comm = proc_stats.comm
240 if not comm:
241 comm = 'unknown'
242
243 if proc_stats.pid is None:
244 pid_str = 'unknown (tid=%d)' % (proc_stats.tid)
245 else:
246 pid_str = str(proc_stats.pid)
247
248 format_str = '{:>10} {:<22}'
249 output_str = format_str.format(
250 common.convert_size(proc_stats.block_write, padding_after=True),
251 '%s (pid=%s)' % (comm, pid_str))
252
253 return (output_str, proc_stats.block_write)
254
255 def _get_total_rq_sectors_datum(self, disk):
256 if disk.total_rq_sectors == 0:
257 return None
258
259 return (disk.disk_name, disk.total_rq_sectors)
260
261 def _get_rq_count_datum(self, disk):
262 if disk.rq_count == 0:
263 return None
264
265 return (disk.disk_name, disk.rq_count)
266
267 def _get_avg_disk_latency_datum(self, disk):
268 if disk.rq_count == 0:
269 return None
270
271 avg_latency = ((disk.total_rq_duration / disk.rq_count) /
272 common.MSEC_PER_NSEC)
273 avg_latency = round(avg_latency, 3)
274
275 return ('%s' % disk.disk_name, avg_latency)
276
277 def _get_net_recv_bytes_datum(self, iface):
278 return ('%s %s' % (common.convert_size(iface.recv_bytes), iface.name),
acf400da 279 iface.recv_bytes)
91765fd2
AB
280
281 def _get_net_sent_bytes_datum(self, iface):
282 return ('%s %s' % (common.convert_size(iface.sent_bytes), iface.name),
acf400da 283 iface.sent_bytes)
91765fd2 284
e0e5f1fd
AB
285 def _get_file_read_datum(self, file_stats):
286 if file_stats.read == 0:
287 return None
288
289 fd_by_pid_str = ''
290 for pid, fd in file_stats.fd_by_pid.items():
291 comm = self._analysis.tids[pid].comm
292 fd_by_pid_str += 'fd %d in %s (%s) ' % (fd, comm, pid)
293
294 format_str = '{:>10} {} {}'
295 output_str = format_str.format(
296 common.convert_size(file_stats.read, padding_after=True),
297 file_stats.filename,
298 fd_by_pid_str)
299
300 return (output_str, file_stats.read)
301
302 def _get_file_write_datum(self, file_stats):
303 if file_stats.write == 0:
304 return None
305
306 fd_by_pid_str = ''
307 for pid, fd in file_stats.fd_by_pid.items():
308 comm = self._analysis.tids[pid].comm
309 fd_by_pid_str += 'fd %d in %s (%s) ' % (fd, comm, pid)
310
311 format_str = '{:>10} {} {}'
312 output_str = format_str.format(
313 common.convert_size(file_stats.write, padding_after=True),
314 file_stats.filename,
315 fd_by_pid_str)
316
317 return (output_str, file_stats.write)
318
91765fd2
AB
319 def _output_read(self):
320 input_list = sorted(self._analysis.tids.values(),
321 key=operator.attrgetter('total_read'),
322 reverse=True)
323 label = 'Per-process I/O Read'
324 graph_args = {'with_value': False}
325 self._print_ascii_graph(input_list, self._get_read_datum, label,
326 graph_args)
327
328 def _output_write(self):
329 input_list = sorted(self._analysis.tids.values(),
330 key=operator.attrgetter('total_write'),
331 reverse=True)
332 label = 'Per-process I/O Write'
333 graph_args = {'with_value': False}
334 self._print_ascii_graph(input_list, self._get_write_datum, label,
335 graph_args)
336
337 def _output_block_read(self):
338 input_list = sorted(self._analysis.tids.values(),
339 key=operator.attrgetter('block_read'),
340 reverse=True)
341 label = 'Block I/O Read'
342 graph_args = {'with_value': False}
343 self._print_ascii_graph(input_list, self._get_block_read_datum,
344 label, graph_args)
345
346 def _output_block_write(self):
347 input_list = sorted(self._analysis.tids.values(),
348 key=operator.attrgetter('block_write'),
349 reverse=True)
350 label = 'Block I/O Write'
351 graph_args = {'with_value': False}
352 self._print_ascii_graph(input_list, self._get_block_write_datum,
353 label, graph_args)
354
355 def _output_total_rq_sectors(self):
356 input_list = sorted(self._analysis.disks.values(),
357 key=operator.attrgetter('total_rq_sectors'),
358 reverse=True)
359 label = 'Disk requests sector count'
360 graph_args = {'unit': ' sectors'}
361 self._print_ascii_graph(input_list, self._get_total_rq_sectors_datum,
362 label, graph_args)
363
364 def _output_rq_count(self):
365 input_list = sorted(self._analysis.disks.values(),
366 key=operator.attrgetter('rq_count'),
367 reverse=True)
368 label = 'Disk request count'
369 graph_args = {'unit': ' requests'}
370 self._print_ascii_graph(input_list, self._get_rq_count_datum,
371 label, graph_args)
372
373 def _output_avg_disk_latency(self):
374 input_list = self._analysis.disks.values()
375 label = 'Disk request average latency'
376 graph_args = {'unit': ' ms', 'sort': 2}
377 self._print_ascii_graph(input_list, self._get_avg_disk_latency_datum,
378 label, graph_args)
379
380 def _output_net_recv_bytes(self):
381 input_list = sorted(self._analysis.ifaces.values(),
382 key=operator.attrgetter('recv_bytes'),
383 reverse=True)
384 label = 'Network received bytes'
385 graph_args = {'with_value': False}
386 self._print_ascii_graph(input_list, self._get_net_recv_bytes_datum,
387 label, graph_args)
388
389 def _output_net_sent_bytes(self):
390 input_list = sorted(self._analysis.ifaces.values(),
391 key=operator.attrgetter('sent_bytes'),
392 reverse=True)
393 label = 'Network sent bytes'
394 graph_args = {'with_value': False}
395 self._print_ascii_graph(input_list, self._get_net_sent_bytes_datum,
396 label, graph_args)
397
e0e5f1fd
AB
398 def _output_file_read(self, files):
399 input_list = sorted(files.values(),
400 key=lambda file_stats: file_stats.read,
401 reverse=True)
402 label = 'Files read'
403 graph_args = {'with_value': False, 'sort': 2}
404 self._print_ascii_graph(input_list, self._get_file_read_datum,
405 label, graph_args)
a9c9a2a6 406
e0e5f1fd
AB
407 def _output_file_write(self, files):
408 input_list = sorted(files.values(),
409 key=lambda file_stats: file_stats.write,
410 reverse=True)
411 label = 'Files write'
412 graph_args = {'with_value': False, 'sort': 2}
413 self._print_ascii_graph(input_list, self._get_file_write_datum,
414 label, graph_args)
a9c9a2a6 415
91765fd2 416 def _output_file_read_write(self):
e0e5f1fd
AB
417 files = self._analysis.get_files_stats(self._arg_pid_list,
418 self._arg_proc_list)
419 self._output_file_read(files)
420 self._output_file_write(files)
a9c9a2a6
JD
421
422 def iotop_output(self):
91765fd2
AB
423 self._output_read()
424 self._output_write()
425 self._output_file_read_write()
426 self._output_block_read()
427 self._output_block_write()
428 self._output_total_rq_sectors()
429 self._output_rq_count()
430 self._output_avg_disk_latency()
431 self._output_net_recv_bytes()
432 self._output_net_sent_bytes()
433
434 # IO Latency output methods
a9c9a2a6
JD
435 def iolatency_freq_histogram(self, _min, _max, res, rq_list, title):
436 step = (_max - _min) / res
437 if step == 0:
438 return
439 buckets = []
440 values = []
441 graph = Pyasciigraph()
442 for i in range(res):
443 buckets.append(i * step)
444 values.append(0)
445 for i in rq_list:
446 v = i / 1000
447 b = min(int((v-_min)/step), res - 1)
448 values[b] += 1
449 g = []
450 i = 0
451 for v in values:
73b71522 452 g.append(('%0.03f' % (i * step + _min), v))
a9c9a2a6 453 i += 1
09071fb9 454 for line in graph.graph(title, g, info_before=True, count=True):
a9c9a2a6 455 print(line)
73b71522 456 print('')
a9c9a2a6 457
a9c9a2a6
JD
458 # iolatency functions
459 def iolatency_output_disk(self):
460 for dev in self.state.disks.keys():
461 d = self.state.disks[dev]
462 if d.max is None:
463 self.compute_disk_stats(d)
464 if d.count is not None:
465 self.iolatency_freq_histogram(d.min, d.max,
466 self._arg_freq_resolution,
467 d.rq_values,
73b71522
AB
468 'Frequency distribution for '
469 'disk %s (usec)' %
a9c9a2a6
JD
470 (d.prettyname))
471
472 def iolatency_output(self):
ef49c8de 473 self._output_disk_latency_freq()
a9c9a2a6
JD
474
475 def iolatency_syscalls_output(self):
476 s = self.syscalls_stats
73b71522 477 print('')
a9c9a2a6
JD
478 if s.open_count > 0:
479 self.iolatency_freq_histogram(s.open_min/1000, s.open_max/1000,
480 self._arg_freq_resolution, s.open_rq,
73b71522 481 'Open latency distribution (usec)')
a9c9a2a6
JD
482 if s.read_count > 0:
483 self.iolatency_freq_histogram(s.read_min/1000, s.read_max/1000,
484 self._arg_freq_resolution, s.read_rq,
73b71522 485 'Read latency distribution (usec)')
a9c9a2a6
JD
486 if s.write_count > 0:
487 self.iolatency_freq_histogram(s.write_min/1000, s.write_max/1000,
488 self._arg_freq_resolution,
489 s.write_rq,
73b71522 490 'Write latency distribution (usec)')
a9c9a2a6
JD
491 if s.sync_count > 0:
492 self.iolatency_freq_histogram(s.sync_min/1000, s.sync_max/1000,
493 self._arg_freq_resolution, s.sync_rq,
73b71522 494 'Sync latency distribution (usec)')
a9c9a2a6
JD
495
496 def iolatency_syscalls_list_output(self, title, rq_list,
497 sortkey, reverse):
498 limit = self._arg_limit
499 count = 0
500 outrange_legend = False
5faa923d 501 if not rq_list:
a9c9a2a6
JD
502 return
503 print(title)
504 if self._arg_extra:
73b71522
AB
505 extra_fmt = '{:<48}'
506 extra_title = '{:<8} {:<8} {:<8} {:<8} {:<8} {:<8} '.format(
507 'Dirtied', 'Alloc', 'Free', 'Written', 'Kswap', 'Cleared')
a9c9a2a6 508 else:
73b71522
AB
509 extra_fmt = '{:<0}'
510 extra_title = ''
511 title_fmt = '{:<19} {:<20} {:<16} {:<23} {:<5} {:<24} {:<8} ' + \
512 extra_fmt + '{:<14}'
513 fmt = '{:<40} {:<16} {:>16} {:>11} {:<24} {:<8} ' + \
514 extra_fmt + '{:<14}'
515 print(title_fmt.format('Begin', 'End', 'Name', 'Duration (usec)',
516 'Size', 'Proc', 'PID', extra_title, 'Filename'))
a9c9a2a6
JD
517 for rq in sorted(rq_list,
518 key=operator.attrgetter(sortkey), reverse=reverse):
73b71522 519 # only limit the output if in the 'top' view
a9c9a2a6
JD
520 if reverse and count > limit:
521 break
522 if rq.size is None:
73b71522 523 size = 'N/A'
a9c9a2a6
JD
524 else:
525 size = common.convert_size(rq.size)
526 if self._arg_extra:
73b71522 527 extra = '{:<8} {:<8} {:<8} {:<8} {:<8} {:<8} '.format(
a9c9a2a6
JD
528 rq.dirty, rq.page_alloc, rq.page_free, rq.page_written,
529 rq.woke_kswapd, rq.page_cleared)
530 else:
73b71522
AB
531 extra = ''
532 name = rq.name.replace('syscall_entry_', '').replace('sys_', '')
a9c9a2a6 533 if rq.fd is None:
73b71522
AB
534 filename = 'None'
535 fd = 'None'
a9c9a2a6
JD
536 else:
537 filename = rq.fd.filename
538 fd = rq.fd.fd
2e781b98
JD
539 if rq.proc.pid is None:
540 pid = 'unknown (tid=%d)' % (rq.proc.tid)
541 else:
542 pid = rq.proc.pid
a9c9a2a6
JD
543 end = common.ns_to_hour_nsec(rq.end, self._arg_multi_day,
544 self._arg_gmt)
545
73b71522 546 outrange = ' '
a9c9a2a6
JD
547 duration = rq.duration
548 if self._arg_begin and rq.begin < self._arg_begin:
73b71522 549 outrange = '*'
a9c9a2a6
JD
550 outrange_legend = True
551 if self._arg_end and rq.end > self._arg_end:
73b71522 552 outrange = '*'
a9c9a2a6
JD
553 outrange_legend = True
554
73b71522
AB
555 print(fmt.format('[' + common.ns_to_hour_nsec(
556 rq.begin, self._arg_multi_day, self._arg_gmt) + ',' +
557 end + ']' + outrange,
a9c9a2a6 558 name,
73b71522 559 '%0.03f' % (duration/1000) + outrange,
a9c9a2a6 560 size, rq.proc.comm,
2e781b98 561 pid, extra,
73b71522 562 '%s (fd=%s)' % (filename, fd)))
a9c9a2a6
JD
563 count += 1
564 if outrange_legend:
73b71522
AB
565 print('*: Syscalls started and/or completed outside of the '
566 'range specified')
a9c9a2a6
JD
567
568 def iolatency_syscalls_top_output(self):
569 s = self.syscalls_stats
570 self.iolatency_syscalls_list_output(
73b71522
AB
571 '\nTop open syscall latencies (usec)', s.all_open,
572 'duration', True)
a9c9a2a6 573 self.iolatency_syscalls_list_output(
73b71522
AB
574 '\nTop read syscall latencies (usec)', s.all_read,
575 'duration', True)
a9c9a2a6 576 self.iolatency_syscalls_list_output(
73b71522
AB
577 '\nTop write syscall latencies (usec)', s.all_write,
578 'duration', True)
a9c9a2a6 579 self.iolatency_syscalls_list_output(
73b71522
AB
580 '\nTop sync syscall latencies (usec)', s.all_sync,
581 'duration', True)
a9c9a2a6
JD
582
583 def iolatency_syscalls_log_output(self):
584 s = self.syscalls_stats
585 self.iolatency_syscalls_list_output(
73b71522 586 '\nLog of all I/O system calls',
a9c9a2a6 587 s.all_open + s.all_read + s.all_write + s.all_sync,
73b71522 588 'begin', False)
a9c9a2a6 589
ef49c8de
AB
590 # IO Stats output methods
591 def _output_latency_stats(self, name, rq_count, min_duration, max_duration,
592 total_duration, rq_durations):
593 if rq_count < 2:
594 stdev = '?'
595 else:
596 stdev = '%0.03f' % (statistics.stdev(rq_durations) / 1000)
597
598 avg = '%0.03f' % (total_duration / (rq_count) / 1000)
599 min_duration = '%0.03f' % (min_duration / 1000)
600 max_duration = '%0.03f' % (max_duration / 1000)
601
602 print(IoAnalysisCommand._LATENCY_STATS_FORMAT.format(
603 name, rq_count, min_duration, avg, max_duration, stdev))
604
605 def _output_latency_stats_from_requests(self, io_requests, name):
606 rq_durations = [io_rq.duration for io_rq in io_requests if
607 self._filter_io_request(io_rq)]
608 rq_count = len(rq_durations)
609 min_duration = min(rq_durations)
610 max_duration = max(rq_durations)
611 total_duration = sum(rq_durations)
612
613 self._output_latency_stats(name, rq_count, min_duration,
614 max_duration, total_duration,
615 rq_durations)
616
617 def _output_syscalls_latency_stats(self):
618 print('\nSyscalls latency statistics (usec):')
619 print(IoAnalysisCommand._LATENCY_STATS_FORMAT.format(
620 'Type', 'Count', 'Min', 'Average', 'Max', 'Stdev'))
621 print(IoAnalysisCommand._SECTION_SEPARATOR_STRING)
622
623 self._output_latency_stats_from_requests(
624 self._analysis.open_io_requests, 'Open')
625 self._output_latency_stats_from_requests(
626 self._analysis.read_io_requests, 'Read')
627 self._output_latency_stats_from_requests(
628 self._analysis.write_io_requests, 'Write')
629 self._output_latency_stats_from_requests(
630 self._analysis.sync_io_requests, 'Sync')
631
ef49c8de
AB
632 def _output_disk_latency_stats(self):
633 if not self._analysis.disks:
a9c9a2a6 634 return
a9c9a2a6 635
ef49c8de
AB
636 print('\nDisk latency statistics (usec):')
637 print(IoAnalysisCommand._LATENCY_STATS_FORMAT.format(
638 'Name', 'Count', 'Min', 'Average', 'Max', 'Stdev'))
639 print(IoAnalysisCommand._SECTION_SEPARATOR_STRING)
640
641 for disk in self._analysis.disks.values():
642 if disk.rq_count:
a7b98a32 643 rq_durations = [rq.duration for rq in disk.rq_list]
ef49c8de
AB
644 self._output_latency_stats(disk.disk_name,
645 disk.rq_count,
646 disk.min_rq_duration,
647 disk.max_rq_duration,
648 disk.total_rq_duration,
649 rq_durations)
a9c9a2a6
JD
650
651 def iostats_output(self):
ef49c8de
AB
652 self._output_syscalls_latency_stats()
653 self._output_disk_latency_stats()
a9c9a2a6 654
da6d0842 655 def _print_results(self, begin_ns, end_ns):
3664e4b0 656 self._print_date(begin_ns, end_ns)
1c0f0e3c
JD
657 if self._arg_usage:
658 self.iotop_output()
a9c9a2a6
JD
659 if self._arg_stats:
660 self.iostats_output()
275bdbb4 661 if self._arg_latencytop:
a9c9a2a6
JD
662 self.iolatency_syscalls_top_output()
663 if self._arg_freq:
664 self.iolatency_syscalls_output()
665 self.iolatency_output()
666 if self._arg_log:
667 self.iolatency_syscalls_log_output()
668
669 def _reset_total(self, start_ts):
3b1dda96 670 self._analysis.reset()
a9c9a2a6
JD
671
672 def _add_arguments(self, ap):
73b71522 673 ap.add_argument('--usage', action='store_true',
1c0f0e3c 674 help='Show the I/O usage')
73b71522 675 ap.add_argument('--latencystats', action='store_true',
1c0f0e3c 676 help='Show the I/O latency statistics')
73b71522 677 ap.add_argument('--latencytop', action='store_true',
275bdbb4 678 help='Show the I/O latency top')
73b71522 679 ap.add_argument('--latencyfreq', action='store_true',
1c0f0e3c
JD
680 help='Show the I/O latency frequency distribution')
681 ap.add_argument('--freq-resolution', type=int, default=20,
682 help='Frequency distribution resolution '
683 '(default 20)')
a9c9a2a6
JD
684
685
686# entry point
1c0f0e3c 687def runstats():
a9c9a2a6 688 # create command
087e5d00 689 iocmd = IoAnalysisCommand()
1c0f0e3c
JD
690 # execute command
691 iocmd.run_stats()
692
a9c9a2a6 693
275bdbb4
JD
694def runlatencytop():
695 # create command
087e5d00 696 iocmd = IoAnalysisCommand()
275bdbb4
JD
697 # execute command
698 iocmd.run_latencytop()
699
700
1c0f0e3c
JD
701def runlog():
702 # create command
087e5d00 703 iocmd = IoAnalysisCommand()
1c0f0e3c
JD
704 # execute command
705 iocmd.run_log()
706
707
708def runfreq():
709 # create command
087e5d00 710 iocmd = IoAnalysisCommand()
1c0f0e3c
JD
711 # execute command
712 iocmd.run_freq()
713
714
715def runusage():
716 # create command
087e5d00 717 iocmd = IoAnalysisCommand()
a9c9a2a6 718 # execute command
1c0f0e3c 719 iocmd.run_usage()
This page took 0.056078 seconds and 5 git commands to generate.