Initial core/stats module implementation
[deliverable/lttng-analyses.git] / lttnganalyses / cli / io.py
CommitLineData
4ed24f86
JD
1# The MIT License (MIT)
2#
a3fa57c0 3# Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
6cd52af3 4# 2015 - Antoine Busque <abusque@efficios.com>
cee855a2 5# 2015 - Philippe Proulx <pproulx@efficios.com>
4ed24f86
JD
6#
7# Permission is hereby granted, free of charge, to any person obtaining a copy
8# of this software and associated documentation files (the "Software"), to deal
9# in the Software without restriction, including without limitation the rights
10# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11# copies of the Software, and to permit persons to whom the Software is
12# furnished to do so, subject to the following conditions:
13#
14# The above copyright notice and this permission notice shall be included in
15# all copies or substantial portions of the Software.
16#
17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
24
0b250a71 25import collections
a9c9a2a6
JD
26import operator
27import statistics
b6d9132b 28import sys
0b250a71
AB
29from . import mi
30from ..core import io
31from .command import Command
32from ..linuxautomaton import common
33from ..ascii_graph import Pyasciigraph
a9c9a2a6
JD
34
35
a0acc08c
PP
36_UsageTables = collections.namedtuple('_UsageTables', [
37 'per_proc_read',
38 'per_proc_write',
39 'per_file_read',
40 'per_file_write',
41 'per_proc_block_read',
42 'per_proc_block_write',
43 'per_disk_sector',
44 'per_disk_request',
45 'per_disk_rtps',
46 'per_netif_recv',
47 'per_netif_send',
48])
49
50
087e5d00 51class IoAnalysisCommand(Command):
a9c9a2a6 52 _DESC = """The I/O command."""
b6d9132b 53 _ANALYSIS_CLASS = io.IoAnalysis
a0acc08c
PP
54 _MI_TITLE = 'I/O analysis'
55 _MI_DESCRIPTION = 'System call/disk latency statistics, system call ' + \
56 'latency distribution, system call top latencies, ' + \
57 'I/O usage top, and I/O operations log'
58 _MI_TAGS = [
59 mi.Tags.IO,
60 mi.Tags.SYSCALL,
61 mi.Tags.STATS,
62 mi.Tags.FREQ,
63 mi.Tags.LOG,
64 mi.Tags.TOP,
65 ]
66 _MI_TABLE_CLASS_SYSCALL_LATENCY_STATS = 'syscall-latency-stats'
67 _MI_TABLE_CLASS_PART_LATENCY_STATS = 'disk-latency-stats'
68 _MI_TABLE_CLASS_FREQ = 'freq'
69 _MI_TABLE_CLASS_TOP_SYSCALL = 'top-syscall'
70 _MI_TABLE_CLASS_LOG = 'log'
71 _MI_TABLE_CLASS_PER_PROCESS_TOP = 'per-process-top'
72 _MI_TABLE_CLASS_PER_FILE_TOP = 'per-file-top'
73 _MI_TABLE_CLASS_PER_PROCESS_TOP_BLOCK = 'per-process-top-block'
74 _MI_TABLE_CLASS_PER_DISK_TOP_SECTOR = 'per-disk-top-sector'
75 _MI_TABLE_CLASS_PER_DISK_TOP_REQUEST = 'per-disk-top-request'
76 _MI_TABLE_CLASS_PER_DISK_TOP_RTPS = 'per-disk-top-rps'
77 _MI_TABLE_CLASS_PER_NETIF_TOP = 'per-netif-top'
78 _MI_TABLE_CLASSES = [
79 (
80 _MI_TABLE_CLASS_SYSCALL_LATENCY_STATS,
81 'System call latency statistics', [
82 ('obj', 'System call category', mi.String),
83 ('count', 'Call count', mi.Integer, 'calls'),
84 ('min_latency', 'Minimum call latency', mi.Duration),
85 ('avg_latency', 'Average call latency', mi.Duration),
86 ('max_latency', 'Maximum call latency', mi.Duration),
650e7f57
AB
87 ('stdev_latency', 'System call latency standard deviation',
88 mi.Duration),
a0acc08c
PP
89 ]
90 ),
91 (
92 _MI_TABLE_CLASS_PART_LATENCY_STATS,
93 'Partition latency statistics', [
94 ('obj', 'Partition', mi.Disk),
95 ('count', 'Access count', mi.Integer, 'accesses'),
96 ('min_latency', 'Minimum access latency', mi.Duration),
97 ('avg_latency', 'Average access latency', mi.Duration),
98 ('max_latency', 'Maximum access latency', mi.Duration),
650e7f57
AB
99 ('stdev_latency', 'System access latency standard deviation',
100 mi.Duration),
a0acc08c
PP
101 ]
102 ),
103 (
104 _MI_TABLE_CLASS_FREQ,
105 'I/O request latency distribution', [
106 ('latency_lower', 'Latency (lower bound)', mi.Duration),
107 ('latency_upper', 'Latency (upper bound)', mi.Duration),
108 ('count', 'Request count', mi.Integer, 'requests'),
109 ]
110 ),
111 (
112 _MI_TABLE_CLASS_TOP_SYSCALL,
113 'Top system call latencies', [
114 ('time_range', 'Call time range', mi.TimeRange),
115 ('out_of_range', 'System call out of range?', mi.Boolean),
116 ('duration', 'Call duration', mi.Duration),
117 ('syscall', 'System call', mi.Syscall),
118 ('size', 'Read/write size', mi.Size),
119 ('process', 'Process', mi.Process),
120 ('path', 'File path', mi.Path),
121 ('fd', 'File descriptor', mi.Fd),
122 ]
123 ),
124 (
125 _MI_TABLE_CLASS_LOG,
126 'I/O operations log', [
127 ('time_range', 'Call time range', mi.TimeRange),
128 ('out_of_range', 'System call out of range?', mi.Boolean),
129 ('duration', 'Call duration', mi.Duration),
130 ('syscall', 'System call', mi.Syscall),
131 ('size', 'Read/write size', mi.Size),
132 ('process', 'Process', mi.Process),
133 ('path', 'File path', mi.Path),
134 ('fd', 'File descriptor', mi.Fd),
135 ]
136 ),
137 (
138 _MI_TABLE_CLASS_PER_PROCESS_TOP,
139 'Per-process top I/O operations', [
140 ('process', 'Process', mi.Process),
141 ('size', 'Total operations size', mi.Size),
142 ('disk_size', 'Disk operations size', mi.Size),
143 ('net_size', 'Network operations size', mi.Size),
144 ('unknown_size', 'Unknown operations size', mi.Size),
145 ]
146 ),
147 (
148 _MI_TABLE_CLASS_PER_FILE_TOP,
149 'Per-file top I/O operations', [
150 ('path', 'File path/info', mi.Path),
151 ('size', 'Operations size', mi.Size),
152 ('fd_owners', 'File descriptor owners', mi.String),
153 ]
154 ),
155 (
156 _MI_TABLE_CLASS_PER_PROCESS_TOP_BLOCK,
157 'Per-process top block I/O operations', [
158 ('process', 'Process', mi.Process),
159 ('size', 'Operations size', mi.Size),
160 ]
161 ),
162 (
163 _MI_TABLE_CLASS_PER_DISK_TOP_SECTOR,
164 'Per-disk top sector I/O operations', [
165 ('disk', 'Disk', mi.Disk),
166 ('count', 'Sector count', mi.Integer, 'sectors'),
167 ]
168 ),
169 (
170 _MI_TABLE_CLASS_PER_DISK_TOP_REQUEST,
171 'Per-disk top I/O requests', [
172 ('disk', 'Disk', mi.Disk),
173 ('count', 'Request count', mi.Integer, 'I/O requests'),
174 ]
175 ),
176 (
177 _MI_TABLE_CLASS_PER_DISK_TOP_RTPS,
178 'Per-disk top I/O request time/sector', [
179 ('disk', 'Disk', mi.Disk),
180 ('rtps', 'Request time/sector', mi.Duration),
181 ]
182 ),
183 (
184 _MI_TABLE_CLASS_PER_NETIF_TOP,
185 'Per-network interface top I/O operations', [
186 ('netif', 'Network interface', mi.NetIf),
187 ('size', 'Operations size', mi.Size),
188 ]
189 ),
190 ]
ef49c8de
AB
191 _LATENCY_STATS_FORMAT = '{:<14} {:>14} {:>14} {:>14} {:>14} {:>14}'
192 _SECTION_SEPARATOR_STRING = '-' * 89
193
a0acc08c
PP
194 def _analysis_tick(self, begin_ns, end_ns):
195 syscall_latency_stats_table = None
196 disk_latency_stats_table = None
197 freq_tables = None
198 top_tables = None
199 log_table = None
200 usage_tables = None
201
202 if self._args.stats:
203 syscall_latency_stats_table, disk_latency_stats_table = \
204 self._get_latency_stats_result_tables(begin_ns, end_ns)
205
206 if self._args.freq:
207 freq_tables = self._get_freq_result_tables(begin_ns, end_ns)
208
209 if self._args.usage:
210 usage_tables = self._get_usage_result_tables(begin_ns, end_ns)
211
212 if self._args.top:
213 top_tables = self._get_top_result_tables(begin_ns, end_ns)
214
215 if self._args.log:
216 log_table = self._get_log_result_table(begin_ns, end_ns)
217
218 if self._mi_mode:
219 self._mi_append_result_tables([
220 log_table,
221 syscall_latency_stats_table,
222 disk_latency_stats_table,
223 ])
224 self._mi_append_result_tables(top_tables)
225 self._mi_append_result_tables(usage_tables)
226 self._mi_append_result_tables(freq_tables)
227 else:
228 self._print_date(begin_ns, end_ns)
229
230 if self._args.usage:
231 self._print_usage(usage_tables)
232
233 if self._args.stats:
234 self._print_latency_stats(syscall_latency_stats_table,
235 disk_latency_stats_table)
236
237 if self._args.top:
238 self._print_top(top_tables)
239
240 if self._args.freq:
241 self._print_freq(freq_tables)
242
243 if self._args.log:
244 self._print_log(log_table)
245
246 def _create_summary_result_tables(self):
247 # TODO: create a summary table here
248 self._mi_clear_result_tables()
249
882fcd19 250 # Filter predicates
882fcd19
AB
251 def _filter_size(self, size):
252 if size is None:
253 return True
b6d9132b 254 if self._args.maxsize is not None and size > self._args.maxsize:
882fcd19 255 return False
b6d9132b 256 if self._args.minsize is not None and size < self._args.minsize:
882fcd19
AB
257 return False
258 return True
259
260 def _filter_latency(self, duration):
b6d9132b 261 if self._args.max is not None and duration > self._args.max:
882fcd19 262 return False
b6d9132b 263 if self._args.min is not None and duration < self._args.min:
882fcd19
AB
264 return False
265 return True
266
ef49c8de 267 def _filter_time_range(self, begin, end):
b6d9132b
AB
268 # Note: we only want to return False only when a request has
269 # ended and is completely outside the timerange (i.e. begun
270 # after the end of the time range).
271 return not (self._args.begin and self._args.end and end and
272 begin > self._args.end)
ef49c8de
AB
273
274 def _filter_io_request(self, io_rq):
f7a2ca1b
JD
275 if io_rq.tid in self._analysis.tids:
276 proc = self._analysis.tids[io_rq.tid]
277 else:
278 proc = None
43b66dd6 279 return self._filter_size(io_rq.size) and \
ef49c8de
AB
280 self._filter_latency(io_rq.duration) and \
281 self._filter_time_range(io_rq.begin_ts, io_rq.end_ts)
282
7e73fe34 283 def _is_io_rq_out_of_range(self, io_rq):
b6d9132b
AB
284 return self._args.begin and io_rq.begin_ts < self._args.begin or \
285 self._args.end and io_rq.end_ts > self._args.end
7e73fe34 286
a0acc08c 287 def _append_per_proc_read_usage_row(self, proc_stats, result_table):
a0acc08c
PP
288 result_table.append_row(
289 process=mi.Process(proc_stats.comm, pid=proc_stats.pid,
290 tid=proc_stats.tid),
291 size=mi.Size(proc_stats.total_read),
292 disk_size=mi.Size(proc_stats.disk_read),
293 net_size=mi.Size(proc_stats.net_read),
294 unknown_size=mi.Size(proc_stats.unk_read),
295 )
88cfa546 296
a0acc08c 297 return True
88cfa546 298
a0acc08c 299 def _append_per_proc_write_usage_row(self, proc_stats, result_table):
a0acc08c
PP
300 result_table.append_row(
301 process=mi.Process(proc_stats.comm, pid=proc_stats.pid,
302 tid=proc_stats.tid),
303 size=mi.Size(proc_stats.total_write),
304 disk_size=mi.Size(proc_stats.disk_write),
305 net_size=mi.Size(proc_stats.net_write),
306 unknown_size=mi.Size(proc_stats.unk_write),
307 )
91765fd2 308
a0acc08c 309 return True
91765fd2 310
a0acc08c 311 def _append_per_proc_block_read_usage_row(self, proc_stats, result_table):
43b66dd6 312 if proc_stats.block_read == 0:
a0acc08c 313 return False
91765fd2 314
a0acc08c
PP
315 if proc_stats.comm:
316 proc_name = proc_stats.comm
91765fd2 317 else:
a0acc08c 318 proc_name = None
91765fd2 319
a0acc08c
PP
320 result_table.append_row(
321 process=mi.Process(proc_name, pid=proc_stats.pid,
322 tid=proc_stats.tid),
323 size=mi.Size(proc_stats.block_read),
324 )
91765fd2 325
a0acc08c 326 return True
91765fd2 327
a0acc08c 328 def _append_per_proc_block_write_usage_row(self, proc_stats, result_table):
43b66dd6 329 if proc_stats.block_write == 0:
a0acc08c 330 return False
91765fd2 331
a0acc08c
PP
332 if proc_stats.comm:
333 proc_name = proc_stats.comm
91765fd2 334 else:
a0acc08c 335 proc_name = None
91765fd2 336
a0acc08c
PP
337 result_table.append_row(
338 process=mi.Process(proc_name, pid=proc_stats.pid,
339 tid=proc_stats.tid),
340 size=mi.Size(proc_stats.block_write),
341 )
91765fd2 342
a0acc08c 343 return True
91765fd2 344
a0acc08c
PP
345 def _append_disk_sector_usage_row(self, disk_stats, result_table):
346 if disk_stats.total_rq_sectors == 0:
91765fd2
AB
347 return None
348
a0acc08c
PP
349 result_table.append_row(
350 disk=mi.Disk(disk_stats.disk_name),
351 count=mi.Integer(disk_stats.total_rq_sectors),
352 )
91765fd2 353
a0acc08c 354 return True
91765fd2 355
a0acc08c
PP
356 def _append_disk_request_usage_row(self, disk_stats, result_table):
357 if disk_stats.rq_count == 0:
358 return False
91765fd2 359
a0acc08c
PP
360 result_table.append_row(
361 disk=mi.Disk(disk_stats.disk_name),
362 count=mi.Integer(disk_stats.rq_count),
363 )
91765fd2 364
a0acc08c 365 return True
91765fd2 366
a0acc08c
PP
367 def _append_disk_rtps_usage_row(self, disk_stats, result_table):
368 if disk_stats.rq_count == 0:
369 return False
91765fd2 370
a0acc08c
PP
371 avg_latency = (disk_stats.total_rq_duration / disk_stats.rq_count)
372 result_table.append_row(
373 disk=mi.Disk(disk_stats.disk_name),
374 rtps=mi.Duration(avg_latency),
375 )
91765fd2 376
a0acc08c 377 return True
91765fd2 378
a0acc08c
PP
379 def _append_netif_recv_usage_row(self, netif_stats, result_table):
380 result_table.append_row(
381 netif=mi.NetIf(netif_stats.name),
382 size=mi.Size(netif_stats.recv_bytes)
383 )
91765fd2 384
a0acc08c 385 return True
91765fd2 386
a0acc08c
PP
387 def _append_netif_send_usage_row(self, netif_stats, result_table):
388 result_table.append_row(
389 netif=mi.NetIf(netif_stats.name),
390 size=mi.Size(netif_stats.sent_bytes)
391 )
91765fd2 392
a0acc08c 393 return True
e0e5f1fd 394
a0acc08c 395 def _get_file_stats_fd_owners_str(self, file_stats):
e0e5f1fd 396 fd_by_pid_str = ''
a0acc08c 397
e0e5f1fd
AB
398 for pid, fd in file_stats.fd_by_pid.items():
399 comm = self._analysis.tids[pid].comm
400 fd_by_pid_str += 'fd %d in %s (%s) ' % (fd, comm, pid)
401
a0acc08c
PP
402 return fd_by_pid_str
403
404 def _append_file_read_usage_row(self, file_stats, result_table):
405 if file_stats.read == 0:
406 return False
e0e5f1fd 407
a0acc08c
PP
408 fd_owners = self._get_file_stats_fd_owners_str(file_stats)
409 result_table.append_row(
410 path=mi.Path(file_stats.filename),
411 size=mi.Size(file_stats.read),
412 fd_owners=mi.String(fd_owners),
413 )
414
415 return True
e0e5f1fd 416
a0acc08c 417 def _append_file_write_usage_row(self, file_stats, result_table):
e0e5f1fd 418 if file_stats.write == 0:
a0acc08c 419 return False
e0e5f1fd 420
a0acc08c
PP
421 fd_owners = self._get_file_stats_fd_owners_str(file_stats)
422 result_table.append_row(
423 path=mi.Path(file_stats.filename),
424 size=mi.Size(file_stats.write),
425 fd_owners=mi.String(fd_owners),
426 )
e0e5f1fd 427
a0acc08c 428 return True
e0e5f1fd 429
650e7f57
AB
430 def _fill_usage_result_table(self, input_list, append_row_cb,
431 result_table):
a0acc08c
PP
432 count = 0
433 limit = self._args.limit
e0e5f1fd 434
a0acc08c
PP
435 for elem in input_list:
436 if append_row_cb(elem, result_table):
437 count += 1
438
439 if limit is not None and count >= limit:
440 break
441
442 def _fill_per_process_read_usage_result_table(self, result_table):
91765fd2
AB
443 input_list = sorted(self._analysis.tids.values(),
444 key=operator.attrgetter('total_read'),
445 reverse=True)
a0acc08c
PP
446 self._fill_usage_result_table(input_list,
447 self._append_per_proc_read_usage_row,
448 result_table)
91765fd2 449
a0acc08c 450 def _fill_per_process_write_usage_result_table(self, result_table):
91765fd2
AB
451 input_list = sorted(self._analysis.tids.values(),
452 key=operator.attrgetter('total_write'),
453 reverse=True)
a0acc08c
PP
454 self._fill_usage_result_table(input_list,
455 self._append_per_proc_write_usage_row,
456 result_table)
91765fd2 457
a0acc08c 458 def _fill_per_process_block_read_usage_result_table(self, result_table):
91765fd2
AB
459 input_list = sorted(self._analysis.tids.values(),
460 key=operator.attrgetter('block_read'),
461 reverse=True)
650e7f57
AB
462 self._fill_usage_result_table(
463 input_list, self._append_per_proc_block_read_usage_row,
464 result_table)
91765fd2 465
a0acc08c 466 def _fill_per_process_block_write_usage_result_table(self, result_table):
91765fd2
AB
467 input_list = sorted(self._analysis.tids.values(),
468 key=operator.attrgetter('block_write'),
469 reverse=True)
650e7f57
AB
470 self._fill_usage_result_table(
471 input_list, self._append_per_proc_block_write_usage_row,
472 result_table)
91765fd2 473
a0acc08c 474 def _fill_disk_sector_usage_result_table(self, result_table):
91765fd2
AB
475 input_list = sorted(self._analysis.disks.values(),
476 key=operator.attrgetter('total_rq_sectors'),
477 reverse=True)
a0acc08c
PP
478 self._fill_usage_result_table(input_list,
479 self._append_disk_sector_usage_row,
480 result_table)
91765fd2 481
a0acc08c 482 def _fill_disk_request_usage_result_table(self, result_table):
91765fd2
AB
483 input_list = sorted(self._analysis.disks.values(),
484 key=operator.attrgetter('rq_count'),
485 reverse=True)
a0acc08c
PP
486 self._fill_usage_result_table(input_list,
487 self._append_disk_request_usage_row,
488 result_table)
91765fd2 489
a0acc08c 490 def _fill_disk_rtps_usage_result_table(self, result_table):
91765fd2 491 input_list = self._analysis.disks.values()
a0acc08c
PP
492 self._fill_usage_result_table(input_list,
493 self._append_disk_rtps_usage_row,
494 result_table)
91765fd2 495
a0acc08c 496 def _fill_netif_recv_usage_result_table(self, result_table):
91765fd2
AB
497 input_list = sorted(self._analysis.ifaces.values(),
498 key=operator.attrgetter('recv_bytes'),
499 reverse=True)
a0acc08c
PP
500 self._fill_usage_result_table(input_list,
501 self._append_netif_recv_usage_row,
502 result_table)
91765fd2 503
a0acc08c 504 def _fill_netif_send_usage_result_table(self, result_table):
91765fd2
AB
505 input_list = sorted(self._analysis.ifaces.values(),
506 key=operator.attrgetter('sent_bytes'),
507 reverse=True)
a0acc08c
PP
508 self._fill_usage_result_table(input_list,
509 self._append_netif_send_usage_row,
510 result_table)
91765fd2 511
a0acc08c 512 def _fill_file_read_usage_result_table(self, files, result_table):
e0e5f1fd
AB
513 input_list = sorted(files.values(),
514 key=lambda file_stats: file_stats.read,
515 reverse=True)
a0acc08c
PP
516 self._fill_usage_result_table(input_list,
517 self._append_file_read_usage_row,
518 result_table)
a9c9a2a6 519
a0acc08c 520 def _fill_file_write_usage_result_table(self, files, result_table):
e0e5f1fd
AB
521 input_list = sorted(files.values(),
522 key=lambda file_stats: file_stats.write,
523 reverse=True)
a0acc08c
PP
524 self._fill_usage_result_table(input_list,
525 self._append_file_write_usage_row,
526 result_table)
a9c9a2a6 527
a0acc08c 528 def _fill_file_usage_result_tables(self, read_table, write_table):
b9f05f8d 529 files = self._analysis.get_files_stats()
a0acc08c
PP
530 self._fill_file_read_usage_result_table(files, read_table)
531 self._fill_file_write_usage_result_table(files, write_table)
532
533 def _get_usage_result_tables(self, begin, end):
534 # create result tables
650e7f57
AB
535 per_proc_read_table = self._mi_create_result_table(
536 self._MI_TABLE_CLASS_PER_PROCESS_TOP, begin, end, 'read')
537 per_proc_write_table = self._mi_create_result_table(
538 self._MI_TABLE_CLASS_PER_PROCESS_TOP, begin, end, 'written')
539 per_file_read_table = self._mi_create_result_table(
540 self._MI_TABLE_CLASS_PER_FILE_TOP, begin, end, 'read')
541 per_file_write_table = self._mi_create_result_table(
542 self._MI_TABLE_CLASS_PER_FILE_TOP, begin, end, 'written')
543 per_proc_block_read_table = self._mi_create_result_table(
544 self._MI_TABLE_CLASS_PER_PROCESS_TOP_BLOCK, begin, end, 'read')
545 per_proc_block_write_table = self._mi_create_result_table(
546 self._MI_TABLE_CLASS_PER_PROCESS_TOP_BLOCK, begin, end, 'written')
547 per_disk_sector_table = self._mi_create_result_table(
548 self._MI_TABLE_CLASS_PER_DISK_TOP_SECTOR, begin, end)
549 per_disk_request_table = self._mi_create_result_table(
550 self._MI_TABLE_CLASS_PER_DISK_TOP_REQUEST, begin, end)
551 per_disk_rtps_table = self._mi_create_result_table(
552 self._MI_TABLE_CLASS_PER_DISK_TOP_RTPS, begin, end)
553 per_netif_recv_table = self._mi_create_result_table(
554 self._MI_TABLE_CLASS_PER_NETIF_TOP, begin, end, 'received')
555 per_netif_send_table = self._mi_create_result_table(
556 self._MI_TABLE_CLASS_PER_NETIF_TOP, begin, end, 'sent')
a0acc08c
PP
557
558 # fill result tables
559 self._fill_per_process_read_usage_result_table(per_proc_read_table)
560 self._fill_per_process_write_usage_result_table(per_proc_write_table)
561 self._fill_file_usage_result_tables(per_file_read_table,
562 per_file_write_table)
650e7f57
AB
563 self._fill_per_process_block_read_usage_result_table(
564 per_proc_block_read_table)
565 self._fill_per_process_block_read_usage_result_table(
566 per_proc_block_write_table)
a0acc08c
PP
567 self._fill_disk_sector_usage_result_table(per_disk_sector_table)
568 self._fill_disk_request_usage_result_table(per_disk_request_table)
569 self._fill_disk_rtps_usage_result_table(per_disk_rtps_table)
570 self._fill_netif_recv_usage_result_table(per_netif_recv_table)
571 self._fill_netif_send_usage_result_table(per_netif_send_table)
572
573 return _UsageTables(
574 per_proc_read=per_proc_read_table,
575 per_proc_write=per_proc_write_table,
576 per_file_read=per_file_read_table,
577 per_file_write=per_file_write_table,
578 per_proc_block_read=per_proc_block_read_table,
579 per_proc_block_write=per_proc_block_write_table,
580 per_disk_sector=per_disk_sector_table,
581 per_disk_request=per_disk_request_table,
582 per_disk_rtps=per_disk_rtps_table,
583 per_netif_recv=per_netif_recv_table,
584 per_netif_send=per_netif_send_table,
585 )
586
587 def _get_per_process_read_write_usage_datum(self, row):
588 if row.process.pid is None:
589 pid_str = 'unknown (tid=%d)' % (row.process.tid)
590 else:
591 pid_str = str(row.process.pid)
592
593 format_str = '{:>10} {:<25} {:>9} file {:>9} net {:>9} unknown'
594 output_str = format_str.format(
595 common.convert_size(row.size.value, padding_after=True),
596 '%s (%s)' % (row.process.name, pid_str),
597 common.convert_size(row.disk_size.value, padding_after=True),
598 common.convert_size(row.net_size.value, padding_after=True),
599 common.convert_size(row.unknown_size.value, padding_after=True))
600
601 return (output_str, row.size.value)
602
603 def _get_per_process_block_read_write_usage_datum(self, row):
604 proc_name = row.process.name
605
606 if not proc_name:
607 proc_name = 'unknown'
608
609 if row.process.pid is None:
610 pid_str = 'unknown (tid=%d)' % (row.process.tid)
611 else:
612 pid_str = str(row.process.pid)
613
614 format_str = '{:>10} {:<22}'
615 output_str = format_str.format(
616 common.convert_size(row.size.value, padding_after=True),
617 '%s (pid=%s)' % (proc_name, pid_str))
618
619 return (output_str, row.size.value)
620
621 def _get_per_disk_count_usage_datum(self, row):
622 return (row.disk.name, row.count.value)
623
624 def _get_per_disk_rtps_usage_datum(self, row):
625 avg_latency = row.rtps.value / common.NSEC_PER_MSEC
626 avg_latency = round(avg_latency, 3)
627
628 return (row.disk.name, avg_latency)
629
630 def _get_per_netif_recv_send_usage_datum(self, row):
650e7f57
AB
631 return ('%s %s' %
632 (common.convert_size(row.size.value), row.netif.name),
a0acc08c
PP
633 row.size.value)
634
635 def _get_per_file_read_write_usage_datum(self, row):
636 format_str = '{:>10} {} {}'
637 output_str = format_str.format(
638 common.convert_size(row.size.value, padding_after=True),
639 row.path.path, row.fd_owners.value)
640
641 return (output_str, row.size.value)
642
643 def _print_usage_ascii_graph(self, result_table, get_datum_cb, graph_label,
644 graph_args=None):
645 graph = Pyasciigraph()
646 data = []
647
648 if graph_args is None:
649 graph_args = {}
650
651 for row in result_table.rows:
652 datum = get_datum_cb(row)
653 data.append(datum)
654
655 for line in graph.graph(graph_label, data, **graph_args):
656 print(line)
657
658 def _print_per_process_read(self, result_table):
659 label = 'Per-process I/O Read'
660 graph_args = {'with_value': False}
650e7f57
AB
661 self._print_usage_ascii_graph(
662 result_table, self._get_per_process_read_write_usage_datum,
663 label, graph_args)
a0acc08c
PP
664
665 def _print_per_process_write(self, result_table):
666 label = 'Per-process I/O Write'
667 graph_args = {'with_value': False}
650e7f57
AB
668 self._print_usage_ascii_graph(
669 result_table, self._get_per_process_read_write_usage_datum,
670 label, graph_args)
a0acc08c
PP
671
672 def _print_per_process_block_read(self, result_table):
673 label = 'Block I/O Read'
674 graph_args = {'with_value': False}
650e7f57
AB
675 self._print_usage_ascii_graph(
676 result_table, self._get_per_process_block_read_write_usage_datum,
677 label, graph_args)
a0acc08c
PP
678
679 def _print_per_process_block_write(self, result_table):
680 label = 'Block I/O Write'
681 graph_args = {'with_value': False}
650e7f57
AB
682 self._print_usage_ascii_graph(
683 result_table, self._get_per_process_block_read_write_usage_datum,
684 label, graph_args)
a0acc08c
PP
685
686 def _print_per_disk_sector(self, result_table):
687 label = 'Disk requests sector count'
688 graph_args = {'unit': ' sectors'}
689 self._print_usage_ascii_graph(result_table,
690 self._get_per_disk_count_usage_datum,
691 label, graph_args)
692
693 def _print_per_disk_request(self, result_table):
694 label = 'Disk request count'
695 graph_args = {'unit': ' requests'}
696 self._print_usage_ascii_graph(result_table,
697 self._get_per_disk_count_usage_datum,
698 label, graph_args)
699
700 def _print_per_disk_rtps(self, result_table):
701 label = 'Disk request average latency'
702 graph_args = {'unit': ' ms', 'sort': 2}
703 self._print_usage_ascii_graph(result_table,
704 self._get_per_disk_rtps_usage_datum,
705 label, graph_args)
706
707 def _print_per_netif_recv(self, result_table):
708 label = 'Network received bytes'
709 graph_args = {'with_value': False}
650e7f57
AB
710 self._print_usage_ascii_graph(
711 result_table, self._get_per_netif_recv_send_usage_datum,
712 label, graph_args)
a0acc08c
PP
713
714 def _print_per_netif_send(self, result_table):
715 label = 'Network sent bytes'
716 graph_args = {'with_value': False}
650e7f57
AB
717 self._print_usage_ascii_graph(
718 result_table, self._get_per_netif_recv_send_usage_datum,
719 label, graph_args)
a0acc08c
PP
720
721 def _print_per_file_read(self, result_table):
722 label = 'Files read'
723 graph_args = {'with_value': False, 'sort': 2}
650e7f57
AB
724 self._print_usage_ascii_graph(
725 result_table, self._get_per_file_read_write_usage_datum,
726 label, graph_args)
a0acc08c
PP
727
728 def _print_per_file_write(self, result_table):
729 label = 'Files write'
730 graph_args = {'with_value': False, 'sort': 2}
650e7f57
AB
731 self._print_usage_ascii_graph(
732 result_table, self._get_per_file_read_write_usage_datum,
733 label, graph_args)
a0acc08c
PP
734
735 def _print_usage(self, usage_tables):
736 self._print_per_process_read(usage_tables.per_proc_read)
737 self._print_per_process_write(usage_tables.per_proc_write)
738 self._print_per_file_read(usage_tables.per_file_read)
739 self._print_per_file_write(usage_tables.per_file_write)
740 self._print_per_process_block_read(usage_tables.per_proc_block_read)
741 self._print_per_process_block_write(usage_tables.per_proc_block_write)
742 self._print_per_disk_sector(usage_tables.per_disk_sector)
743 self._print_per_disk_request(usage_tables.per_disk_request)
744 self._print_per_disk_rtps(usage_tables.per_disk_rtps)
745 self._print_per_netif_recv(usage_tables.per_netif_recv)
746 self._print_per_netif_send(usage_tables.per_netif_send)
747
748 def _fill_freq_result_table(self, duration_list, result_table):
9ac03ff5
AB
749 if not duration_list:
750 return
751
752 # The number of bins for the histogram
b6d9132b 753 resolution = self._args.freq_resolution
9ac03ff5
AB
754
755 min_duration = min(duration_list)
756 max_duration = max(duration_list)
757 # ns to µs
758 min_duration /= 1000
759 max_duration /= 1000
760
761 step = (max_duration - min_duration) / resolution
a0acc08c 762
a9c9a2a6
JD
763 if step == 0:
764 return
9ac03ff5 765
a9c9a2a6
JD
766 buckets = []
767 values = []
a0acc08c 768
9ac03ff5 769 for i in range(resolution):
a9c9a2a6
JD
770 buckets.append(i * step)
771 values.append(0)
a0acc08c 772
9ac03ff5
AB
773 for duration in duration_list:
774 duration /= 1000
775 index = min(int((duration - min_duration) / step), resolution - 1)
776 values[index] += 1
777
9ac03ff5 778 for index, value in enumerate(values):
a0acc08c
PP
779 result_table.append_row(
780 latency_lower=mi.Duration.from_us(index * step + min_duration),
650e7f57
AB
781 latency_upper=mi.Duration.from_us((index + 1) * step +
782 min_duration),
a0acc08c
PP
783 count=mi.Integer(value),
784 )
785
786 def _get_disk_freq_result_tables(self, begin, end):
787 result_tables = []
788
789 for disk in self._analysis.disks.values():
790 rq_durations = [rq.duration for rq in disk.rq_list if
791 self._filter_io_request(rq)]
792 subtitle = 'disk: {}'.format(disk.disk_name)
793 result_table = \
794 self._mi_create_result_table(self._MI_TABLE_CLASS_FREQ,
795 begin, end, subtitle)
796 self._fill_freq_result_table(rq_durations, result_table)
797 result_tables.append(result_table)
798
799 return result_tables
800
801 def _get_syscall_freq_result_tables(self, begin, end):
802 open_table = \
803 self._mi_create_result_table(self._MI_TABLE_CLASS_FREQ,
804 begin, end, 'open')
805 read_table = \
806 self._mi_create_result_table(self._MI_TABLE_CLASS_FREQ,
807 begin, end, 'read')
808 write_table = \
809 self._mi_create_result_table(self._MI_TABLE_CLASS_FREQ,
810 begin, end, 'write')
811 sync_table = \
812 self._mi_create_result_table(self._MI_TABLE_CLASS_FREQ,
813 begin, end, 'sync')
814 self._fill_freq_result_table([io_rq.duration for io_rq in
815 self._analysis.open_io_requests if
816 self._filter_io_request(io_rq)],
650e7f57 817 open_table)
a0acc08c
PP
818 self._fill_freq_result_table([io_rq.duration for io_rq in
819 self._analysis.read_io_requests if
820 self._filter_io_request(io_rq)],
650e7f57 821 read_table)
a0acc08c
PP
822 self._fill_freq_result_table([io_rq.duration for io_rq in
823 self._analysis.write_io_requests if
824 self._filter_io_request(io_rq)],
650e7f57 825 write_table)
a0acc08c
PP
826 self._fill_freq_result_table([io_rq.duration for io_rq in
827 self._analysis.sync_io_requests if
828 self._filter_io_request(io_rq)],
650e7f57 829 sync_table)
a0acc08c
PP
830
831 return [open_table, read_table, write_table, sync_table]
832
833 def _get_freq_result_tables(self, begin, end):
834 syscall_tables = self._get_syscall_freq_result_tables(begin, end)
835 disk_tables = self._get_disk_freq_result_tables(begin, end)
836
837 return syscall_tables + disk_tables
838
839 def _print_one_freq(self, result_table):
840 if not result_table.rows:
841 return
9ac03ff5 842
a0acc08c
PP
843 graph = Pyasciigraph()
844 graph_data = []
845
846 for row in result_table.rows:
847 graph_data.append(('%0.03f' % row.latency_lower.to_us(),
848 row.count.value))
849
650e7f57
AB
850 title = '{} {} (usec)'.format(result_table.title,
851 result_table.subtitle)
9ac03ff5
AB
852 graph_lines = graph.graph(
853 title,
854 graph_data,
855 info_before=True,
856 count=True
857 )
858
859 for line in graph_lines:
a9c9a2a6 860 print(line)
9ac03ff5
AB
861
862 print()
863
a0acc08c
PP
864 def _print_freq(self, freq_tables):
865 for freq_table in freq_tables:
866 self._print_one_freq(freq_table)
7e73fe34 867
a0acc08c 868 def _append_log_row(self, io_rq, result_table):
7e73fe34 869 if io_rq.size is None:
a0acc08c 870 size = mi.Empty()
7e73fe34 871 else:
a0acc08c 872 size = mi.Size(io_rq.size)
7e73fe34
AB
873
874 tid = io_rq.tid
875 proc_stats = self._analysis.tids[tid]
a0acc08c 876 proc_name = proc_stats.comm
7e73fe34
AB
877
878 # TODO: handle fd_in/fd_out for RW type operations
879 if io_rq.fd is None:
a0acc08c
PP
880 path = mi.Empty()
881 fd = mi.Empty()
7e73fe34 882 else:
a0acc08c 883 fd = mi.Fd(io_rq.fd)
7e73fe34 884 parent_proc = proc_stats
a0acc08c 885
7e73fe34
AB
886 if parent_proc.pid is not None:
887 parent_proc = self._analysis.tids[parent_proc.pid]
888
a0acc08c
PP
889 fd_stats = parent_proc.get_fd(io_rq.fd, io_rq.end_ts)
890
7e73fe34 891 if fd_stats is not None:
a0acc08c 892 path = mi.Path(fd_stats.filename)
7e73fe34 893 else:
a0acc08c
PP
894 path = mi.Unknown()
895
896 result_table.append_row(
897 time_range=mi.TimeRange(io_rq.begin_ts, io_rq.end_ts),
898 out_of_range=mi.Boolean(self._is_io_rq_out_of_range(io_rq)),
899 duration=mi.Duration(io_rq.duration),
900 syscall=mi.Syscall(io_rq.syscall_name),
901 size=size,
902 process=mi.Process(proc_name, tid=tid),
903 path=path,
904 fd=fd,
905 )
906
907 def _fill_log_result_table(self, rq_list, sort_key, is_top, result_table):
908 if not rq_list:
909 return
910
911 count = 0
7e73fe34 912
a0acc08c
PP
913 for io_rq in sorted(rq_list, key=operator.attrgetter(sort_key),
914 reverse=is_top):
915 if is_top and count > self._args.limit:
916 break
917
918 self._append_log_row(io_rq, result_table)
919 count += 1
920
921 def _fill_log_result_table_from_io_requests(self, io_requests, sort_key,
922 is_top, result_table):
923 io_requests = [io_rq for io_rq in io_requests if
924 self._filter_io_request(io_rq)]
650e7f57
AB
925 self._fill_log_result_table(io_requests, sort_key, is_top,
926 result_table)
a0acc08c
PP
927
928 def _get_top_result_tables(self, begin, end):
929 open_table = \
930 self._mi_create_result_table(self._MI_TABLE_CLASS_TOP_SYSCALL,
931 begin, end, 'open')
932 read_table = \
933 self._mi_create_result_table(self._MI_TABLE_CLASS_TOP_SYSCALL,
934 begin, end, 'read')
935 write_table = \
936 self._mi_create_result_table(self._MI_TABLE_CLASS_TOP_SYSCALL,
937 begin, end, 'write')
938 sync_table = \
939 self._mi_create_result_table(self._MI_TABLE_CLASS_TOP_SYSCALL,
940 begin, end, 'sync')
941 self._fill_log_result_table_from_io_requests(
942 self._analysis.open_io_requests, 'duration', True, open_table)
943 self._fill_log_result_table_from_io_requests(
944 self._analysis.read_io_requests, 'duration', True, read_table)
945 self._fill_log_result_table_from_io_requests(
946 self._analysis.write_io_requests, 'duration', True, write_table)
947 self._fill_log_result_table_from_io_requests(
948 self._analysis.sync_io_requests, 'duration', True, sync_table)
949
950 return [open_table, read_table, write_table, sync_table]
951
952 def _print_log_row(self, row):
953 fmt = '{:<40} {:<16} {:>16} {:>11} {:<24} {:<8} {:<14}'
954 begin_time = common.ns_to_hour_nsec(row.time_range.begin,
955 self._args.multi_day,
956 self._args.gmt)
957 end_time = common.ns_to_hour_nsec(row.time_range.end,
958 self._args.multi_day,
959 self._args.gmt)
960 time_range_str = '[' + begin_time + ',' + end_time + ']'
961 duration_str = '%0.03f' % row.duration.to_us()
7e73fe34 962
a0acc08c
PP
963 if type(row.size) is mi.Empty:
964 size = 'N/A'
965 else:
966 size = common.convert_size(row.size.value)
967
968 tid = row.process.tid
969 proc_name = row.process.name
970
971 if type(row.fd) is mi.Empty:
972 file_str = 'N/A'
973 else:
974 if type(row.path) is mi.Unknown:
975 path = 'unknown'
976 else:
977 path = row.path.path
978
979 file_str = '%s (fd=%s)' % (path, row.fd.fd)
980
981 if row.out_of_range.value:
7e73fe34
AB
982 time_range_str += '*'
983 duration_str += '*'
984 else:
985 time_range_str += ' '
986 duration_str += ' '
987
a0acc08c
PP
988 print(fmt.format(time_range_str, row.syscall.name, duration_str,
989 size, proc_name, tid, file_str))
7e73fe34 990
a0acc08c
PP
991 def _print_log(self, result_table):
992 if not result_table.rows:
a9c9a2a6 993 return
7e73fe34 994
7e73fe34
AB
995 has_out_of_range_rq = False
996
997 print()
a0acc08c
PP
998 fmt = '{} {} (usec)'
999 print(fmt.format(result_table.title, result_table.subtitle))
7e73fe34
AB
1000 header_fmt = '{:<19} {:<20} {:<16} {:<23} {:<5} {:<24} {:<8} {:<14}'
1001 print(header_fmt.format(
1002 'Begin', 'End', 'Name', 'Duration (usec)', 'Size', 'Proc', 'PID',
1003 'Filename'))
1004
a0acc08c
PP
1005 for row in result_table.rows:
1006 self._print_log_row(row)
7e73fe34 1007
a0acc08c 1008 if not has_out_of_range_rq and row.out_of_range.value:
7e73fe34
AB
1009 has_out_of_range_rq = True
1010
7e73fe34 1011 if has_out_of_range_rq:
73b71522
AB
1012 print('*: Syscalls started and/or completed outside of the '
1013 'range specified')
a9c9a2a6 1014
a0acc08c
PP
1015 def _print_top(self, top_tables):
1016 for table in top_tables:
1017 self._print_log(table)
1018
1019 def _get_log_result_table(self, begin, end):
1020 log_table = self._mi_create_result_table(self._MI_TABLE_CLASS_LOG,
1021 begin, end)
1022 self._fill_log_result_table_from_io_requests(
1023 self._analysis.io_requests, 'begin_ts', False, log_table)
1024
1025 return log_table
1026
1027 def _append_latency_stats_row(self, obj, rq_durations, result_table):
a205a2b4
JD
1028 rq_count = len(rq_durations)
1029 total_duration = sum(rq_durations)
a0acc08c 1030
a205a2b4
JD
1031 if len(rq_durations) > 0:
1032 min_duration = min(rq_durations)
1033 max_duration = max(rq_durations)
1034 else:
1035 min_duration = 0
1036 max_duration = 0
1037
ef49c8de 1038 if rq_count < 2:
a0acc08c 1039 stdev = mi.Unknown()
ef49c8de 1040 else:
a0acc08c 1041 stdev = mi.Duration(statistics.stdev(rq_durations))
ef49c8de 1042
d872de29 1043 if rq_count > 0:
a0acc08c 1044 avg = total_duration / rq_count
d872de29 1045 else:
a0acc08c
PP
1046 avg = 0
1047
1048 result_table.append_row(
1049 obj=obj,
1050 count=mi.Integer(rq_count),
1051 min_latency=mi.Duration(min_duration),
1052 avg_latency=mi.Duration(avg),
1053 max_latency=mi.Duration(max_duration),
1054 stdev_latency=stdev,
1055 )
ef49c8de 1056
a0acc08c
PP
1057 def _append_latency_stats_row_from_requests(self, obj, io_requests,
1058 result_table):
ef49c8de
AB
1059 rq_durations = [io_rq.duration for io_rq in io_requests if
1060 self._filter_io_request(io_rq)]
a0acc08c
PP
1061 self._append_latency_stats_row(obj, rq_durations, result_table)
1062
1063 def _get_syscall_latency_stats_result_table(self, begin, end):
650e7f57
AB
1064 result_table = self._mi_create_result_table(
1065 self._MI_TABLE_CLASS_SYSCALL_LATENCY_STATS, begin, end)
a0acc08c
PP
1066 append_fn = self._append_latency_stats_row_from_requests
1067 append_fn(mi.String('Open'), self._analysis.open_io_requests,
1068 result_table)
1069 append_fn(mi.String('Read'), self._analysis.read_io_requests,
1070 result_table)
1071 append_fn(mi.String('Write'), self._analysis.write_io_requests,
1072 result_table)
1073 append_fn(mi.String('Sync'), self._analysis.sync_io_requests,
1074 result_table)
1075
1076 return result_table
1077
1078 def _get_disk_latency_stats_result_table(self, begin, end):
1079 if not self._analysis.disks:
1080 return
1081
650e7f57
AB
1082 result_table = self._mi_create_result_table(
1083 self._MI_TABLE_CLASS_PART_LATENCY_STATS, begin, end)
a0acc08c
PP
1084
1085 for disk in self._analysis.disks.values():
1086 if disk.rq_count:
1087 rq_durations = [rq.duration for rq in disk.rq_list if
1088 self._filter_io_request(rq)]
1089 disk = mi.Disk(disk.disk_name)
650e7f57
AB
1090 self._append_latency_stats_row(disk, rq_durations,
1091 result_table)
a0acc08c
PP
1092
1093 return result_table
1094
1095 def _get_latency_stats_result_tables(self, begin, end):
1096 syscall_tbl = self._get_syscall_latency_stats_result_table(begin, end)
1097 disk_tbl = self._get_disk_latency_stats_result_table(begin, end)
1098
1099 return syscall_tbl, disk_tbl
ef49c8de 1100
a0acc08c
PP
1101 def _print_latency_stats_row(self, row):
1102 if type(row.stdev_latency) is mi.Unknown:
1103 stdev = '?'
1104 else:
1105 stdev = '%0.03f' % row.stdev_latency.to_us()
1106
1107 avg = '%0.03f' % row.avg_latency.to_us()
1108 min_duration = '%0.03f' % row.min_latency.to_us()
1109 max_duration = '%0.03f' % row.max_latency.to_us()
1110
1111 print(IoAnalysisCommand._LATENCY_STATS_FORMAT.format(
1112 str(row.obj), row.count.value, min_duration,
1113 avg, max_duration, stdev))
1114
1115 def _print_syscall_latency_stats(self, stats_table):
ef49c8de
AB
1116 print('\nSyscalls latency statistics (usec):')
1117 print(IoAnalysisCommand._LATENCY_STATS_FORMAT.format(
1118 'Type', 'Count', 'Min', 'Average', 'Max', 'Stdev'))
1119 print(IoAnalysisCommand._SECTION_SEPARATOR_STRING)
1120
a0acc08c
PP
1121 for row in stats_table.rows:
1122 self._print_latency_stats_row(row)
ef49c8de 1123
a0acc08c
PP
1124 def _print_disk_latency_stats(self, stats_table):
1125 if not stats_table.rows:
a9c9a2a6 1126 return
a9c9a2a6 1127
ef49c8de
AB
1128 print('\nDisk latency statistics (usec):')
1129 print(IoAnalysisCommand._LATENCY_STATS_FORMAT.format(
1130 'Name', 'Count', 'Min', 'Average', 'Max', 'Stdev'))
1131 print(IoAnalysisCommand._SECTION_SEPARATOR_STRING)
1132
a0acc08c
PP
1133 for row in stats_table.rows:
1134 self._print_latency_stats_row(row)
a9c9a2a6 1135
a0acc08c
PP
1136 def _print_latency_stats(self, syscall_latency_stats_table,
1137 disk_latency_stats_table):
1138 self._print_syscall_latency_stats(syscall_latency_stats_table)
1139 self._print_disk_latency_stats(disk_latency_stats_table)
a9c9a2a6 1140
a9c9a2a6 1141 def _add_arguments(self, ap):
b6d9132b
AB
1142 Command._add_proc_filter_args(ap)
1143 Command._add_min_max_args(ap)
1144 Command._add_log_args(
1145 ap, help='Output the I/O requests in chronological order')
b9f05f8d
AB
1146 Command._add_top_args(
1147 ap, help='Output the top I/O latencies by category')
b6d9132b
AB
1148 Command._add_stats_args(ap, help='Output the I/O latency statistics')
1149 Command._add_freq_args(
1150 ap, help='Output the I/O latency frequency distribution')
73b71522 1151 ap.add_argument('--usage', action='store_true',
b6d9132b
AB
1152 help='Output the I/O usage')
1153 ap.add_argument('--minsize', type=float,
1154 help='Filter out, I/O operations working with '
1155 'less that minsize bytes')
1156 ap.add_argument('--maxsize', type=float,
1157 help='Filter out, I/O operations working with '
1158 'more that maxsize bytes')
b6d9132b
AB
1159
1160
a0acc08c
PP
1161def _run(mi_mode):
1162 iocmd = IoAnalysisCommand(mi_mode=mi_mode)
b6d9132b 1163 iocmd.run()
1c0f0e3c 1164
a9c9a2a6 1165
a0acc08c
PP
1166def _runstats(mi_mode):
1167 sys.argv.insert(1, '--stats')
1168 _run(mi_mode)
1169
1170
1171def _runlog(mi_mode):
1172 sys.argv.insert(1, '--log')
1173 _run(mi_mode)
1174
1175
1176def _runfreq(mi_mode):
1177 sys.argv.insert(1, '--freq')
1178 _run(mi_mode)
1179
1180
1181def _runlatencytop(mi_mode):
b6d9132b 1182 sys.argv.insert(1, '--top')
a0acc08c
PP
1183 _run(mi_mode)
1184
1185
1186def _runusage(mi_mode):
1187 sys.argv.insert(1, '--usage')
1188 _run(mi_mode)
1189
1190
1191def runstats():
1192 _runstats(mi_mode=False)
275bdbb4
JD
1193
1194
1c0f0e3c 1195def runlog():
a0acc08c 1196 _runlog(mi_mode=False)
1c0f0e3c
JD
1197
1198
1199def runfreq():
a0acc08c
PP
1200 _runfreq(mi_mode=False)
1201
1202
1203def runlatencytop():
1204 _runlatencytop(mi_mode=False)
1c0f0e3c
JD
1205
1206
1207def runusage():
a0acc08c
PP
1208 _runusage(mi_mode=False)
1209
1210
1211def runstats_mi():
1212 _runstats(mi_mode=True)
1213
1214
1215def runlog_mi():
1216 _runlog(mi_mode=True)
1217
1218
1219def runfreq_mi():
1220 _runfreq(mi_mode=True)
1221
1222
1223def runlatencytop_mi():
1224 _runlatencytop(mi_mode=True)
1225
1226
1227def runusage_mi():
1228 _runusage(mi_mode=True)
This page took 0.105016 seconds and 5 git commands to generate.