Implement sched switch latency analysis
[deliverable/lttng-analyses.git] / lttnganalyses / cli / syscallstats.py
CommitLineData
4ed24f86
JD
1# The MIT License (MIT)
2#
a3fa57c0 3# Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
b68fb35b 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
a04c353a 25from .command import Command
56936af2 26from ..core import syscalls
a0acc08c 27from . import mi
a04c353a 28import operator
21167679
JD
29import statistics
30import errno
a04c353a
JD
31
32
33class SyscallsAnalysis(Command):
b68fb35b 34 _DESC = """The syscallstats command."""
b6d9132b 35 _ANALYSIS_CLASS = syscalls.SyscallsAnalysis
a0acc08c
PP
36 _MI_TITLE = 'System call statistics'
37 _MI_DESCRIPTION = 'Per-TID and global system call statistics'
38 _MI_TAGS = [mi.Tags.SYSCALL, mi.Tags.STATS]
39 _MI_TABLE_CLASS_PER_TID_STATS = 'per-tid'
40 _MI_TABLE_CLASS_TOTAL = 'total'
41 _MI_TABLE_CLASS_SUMMARY = 'summary'
42 _MI_TABLE_CLASSES = [
43 (
44 _MI_TABLE_CLASS_PER_TID_STATS,
45 'System call statistics', [
46 ('syscall', 'System call', mi.Syscall),
47 ('count', 'Call count', mi.Integer, 'calls'),
48 ('min_duration', 'Minimum call duration', mi.Duration),
49 ('avg_duration', 'Average call duration', mi.Duration),
50 ('max_duration', 'Maximum call duration', mi.Duration),
b9f05f8d
AB
51 ('stdev_duration', 'Call duration standard deviation',
52 mi.Duration),
a0acc08c
PP
53 ('return_values', 'Return values count', mi.String),
54 ]
55 ),
56 (
57 _MI_TABLE_CLASS_TOTAL,
58 'Per-TID system call statistics', [
59 ('process', 'Process', mi.Process),
60 ('count', 'Total system call count', mi.Integer, 'calls'),
61 ]
62 ),
63 (
64 _MI_TABLE_CLASS_SUMMARY,
65 'System call statistics - summary', [
66 ('time_range', 'Time range', mi.TimeRange),
67 ('process', 'Process', mi.Process),
68 ('count', 'Total system call count', mi.Integer, 'calls'),
69 ]
70 ),
71 ]
72
73 def _analysis_tick(self, begin_ns, end_ns):
74 total_table, per_tid_tables = self._get_result_tables(begin_ns, end_ns)
75
76 if self._mi_mode:
77 self._mi_append_result_tables(per_tid_tables)
78 self._mi_append_result_table(total_table)
79 else:
80 self._print_date(begin_ns, end_ns)
81 self._print_results(total_table, per_tid_tables)
82
83 def _post_analysis(self):
84 if not self._mi_mode:
85 return
86
87 if len(self._mi_get_result_tables(self._MI_TABLE_CLASS_TOTAL)) > 1:
88 self._create_summary_result_table()
89
90 self._mi_print()
91
92 def _create_summary_result_table(self):
93 total_tables = self._mi_get_result_tables(self._MI_TABLE_CLASS_TOTAL)
94 begin = total_tables[0].timerange.begin
95 end = total_tables[-1].timerange.end
96 summary_table = \
97 self._mi_create_result_table(self._MI_TABLE_CLASS_SUMMARY,
98 begin, end)
99
100 for total_table in total_tables:
101 for row in total_table.rows:
102 process = row.process
103 count = row.count
104 summary_table.append_row(
105 time_range=total_table.timerange,
106 process=process,
107 count=count,
108 )
109
110 self._mi_clear_result_tables()
111 self._mi_append_result_table(summary_table)
112
113 def _get_result_tables(self, begin_ns, end_ns):
114 per_tid_tables = []
115 total_table = self._mi_create_result_table(self._MI_TABLE_CLASS_TOTAL,
116 begin_ns, end_ns)
100a92b2
AB
117
118 for proc_stats in sorted(self._analysis.tids.values(),
119 key=operator.attrgetter('total_syscalls'),
120 reverse=True):
121 if not self._filter_process(proc_stats) or \
122 proc_stats.total_syscalls == 0:
21167679 123 continue
ab094171 124
100a92b2 125 pid = proc_stats.pid
a0acc08c 126
100a92b2 127 if proc_stats.pid is None:
ab094171 128 pid = '?'
ab094171 129
a0acc08c
PP
130 subtitle = '%s (%s, TID: %d)' % (proc_stats.comm, pid,
131 proc_stats.tid)
132 result_table = \
b9f05f8d
AB
133 self._mi_create_result_table(
134 self._MI_TABLE_CLASS_PER_TID_STATS, begin_ns, end_ns,
135 subtitle)
ab094171 136
100a92b2 137 for syscall in sorted(proc_stats.syscalls.values(),
a04c353a
JD
138 key=operator.attrgetter('count'),
139 reverse=True):
100a92b2
AB
140 durations = []
141 return_count = {}
142
143 for syscall_event in syscall.syscalls_list:
144 durations.append(syscall_event.duration)
145
146 if syscall_event.ret >= 0:
147 return_key = 'success'
21167679 148 else:
debd33f3 149 try:
100a92b2
AB
150 return_key = errno.errorcode[-syscall_event.ret]
151 except KeyError:
152 return_key = str(syscall_event.ret)
153
154 if return_key not in return_count:
155 return_count[return_key] = 1
156
157 return_count[return_key] += 1
158
100a92b2 159 if len(durations) > 2:
a0acc08c 160 stdev = mi.Duration(statistics.stdev(durations))
21167679 161 else:
a0acc08c
PP
162 stdev = mi.Unknown()
163
164 result_table.append_row(
165 syscall=mi.Syscall(syscall.name),
166 count=mi.Integer(syscall.count),
167 min_duration=mi.Duration(syscall.min_duration),
b9f05f8d
AB
168 avg_duration=mi.Duration(syscall.total_duration /
169 syscall.count),
a0acc08c
PP
170 max_duration=mi.Duration(syscall.max_duration),
171 stdev_duration=stdev,
172 return_values=mi.String(str(return_count)),
173 )
174
175 per_tid_tables.append(result_table)
176 total_table.append_row(
177 process=mi.Process(proc_stats.comm, pid=proc_stats.pid,
178 tid=proc_stats.tid),
179 count=mi.Integer(proc_stats.total_syscalls),
180 )
181
182 return total_table, per_tid_tables
183
184 def _print_results(self, total_table, per_tid_tables):
185 line_format = '{:<38} {:>14} {:>14} {:>14} {:>12} {:>10} {:<14}'
186
187 print('Per-TID syscalls statistics (usec)')
188 total_calls = 0
189
190 for total_row, table in zip(total_table.rows, per_tid_tables):
191 print(line_format.format(table.subtitle,
192 'Count', 'Min', 'Average', 'Max',
193 'Stdev', 'Return values'))
194 for row in table.rows:
195 syscall_name = row.syscall.name
196 syscall_count = row.count.value
197 min_duration = round(row.min_duration.to_us(), 3)
198 avg_duration = round(row.avg_duration.to_us(), 3)
199 max_duration = round(row.max_duration.to_us(), 3)
200
201 if type(row.stdev_duration) is mi.Unknown:
73b71522 202 stdev = '?'
a0acc08c
PP
203 else:
204 stdev = round(row.stdev_duration.to_us(), 3)
100a92b2 205
a0acc08c 206 proc_total_calls = total_row.count.value
100a92b2 207 print(line_format.format(
a0acc08c
PP
208 ' - ' + syscall_name, syscall_count, min_duration,
209 avg_duration, max_duration, stdev,
210 row.return_values.value))
100a92b2 211
a0acc08c 212 print(line_format.format('Total:', proc_total_calls,
100a92b2 213 '', '', '', '', ''))
73b71522 214 print('-' * 113)
a0acc08c 215 total_calls += proc_total_calls
a04c353a 216
a0acc08c 217 print('\nTotal syscalls: %d' % (total_calls))
a04c353a 218
a04c353a 219 def _add_arguments(self, ap):
b6d9132b 220 Command._add_proc_filter_args(ap)
a04c353a
JD
221
222
a0acc08c
PP
223def _run(mi_mode):
224 syscallscmd = SyscallsAnalysis(mi_mode=mi_mode)
a04c353a 225 syscallscmd.run()
a0acc08c
PP
226
227
228# entry point (human)
229def run():
230 _run(mi_mode=False)
231
232
233# entry point (MI)
234def run_mi():
235 _run(mi_mode=True)
This page took 0.042586 seconds and 5 git commands to generate.