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