a272b6ea0b2b124b623b0726ee854634a2fdf601
[deliverable/lttng-analyses.git] / lttnganalyses / cli / cputop.py
1 # The MIT License (MIT)
2 #
3 # Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
4 # 2015 - Antoine Busque <abusque@efficios.com>
5 # 2015 - Philippe Proulx <pproulx@efficios.com>
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
25 import operator
26 from .command import Command
27 from ..core import cputop
28 from . import mi
29 from . import termgraph
30
31
32 class Cputop(Command):
33 _DESC = """The cputop command."""
34 _ANALYSIS_CLASS = cputop.Cputop
35 _MI_TITLE = 'Top CPU usage'
36 _MI_DESCRIPTION = 'Per-TID, per-CPU, and total top CPU usage'
37 _MI_TAGS = [mi.Tags.CPU, mi.Tags.TOP]
38 _MI_TABLE_CLASS_PER_PROC = 'per-process'
39 _MI_TABLE_CLASS_PER_CPU = 'per-cpu'
40 _MI_TABLE_CLASS_TOTAL = 'total'
41 _MI_TABLE_CLASS_SUMMARY = 'summary'
42 _MI_TABLE_CLASSES = [
43 (
44 _MI_TABLE_CLASS_PER_PROC,
45 'Per-TID top CPU usage', [
46 ('process', 'Process', mi.Process),
47 ('migrations', 'Migration count', mi.Integer, 'migrations'),
48 ('prio_list', 'Chronological priorities', mi.String),
49 ('usage', 'CPU usage', mi.Ratio),
50 ]
51 ),
52 (
53 _MI_TABLE_CLASS_PER_CPU,
54 'Per-CPU top CPU usage', [
55 ('cpu', 'CPU', mi.Cpu),
56 ('usage', 'CPU usage', mi.Ratio),
57 ]),
58 (
59 _MI_TABLE_CLASS_TOTAL,
60 'Total CPU usage', [
61 ('usage', 'CPU usage', mi.Ratio),
62 ]
63 ),
64 (
65 _MI_TABLE_CLASS_SUMMARY,
66 'CPU usage - summary', [
67 ('time_range', 'Time range', mi.TimeRange),
68 ('usage', 'Total CPU usage', mi.Ratio),
69 ]
70 ),
71 ]
72
73 def _analysis_tick(self, begin_ns, end_ns):
74 per_tid_table = self._get_per_tid_usage_result_table(begin_ns, end_ns)
75 per_cpu_table = self._get_per_cpu_usage_result_table(begin_ns, end_ns)
76 total_table = self._get_total_usage_result_table(begin_ns, end_ns)
77
78 if self._mi_mode:
79 self._mi_append_result_table(per_tid_table)
80 self._mi_append_result_table(per_cpu_table)
81 self._mi_append_result_table(total_table)
82 else:
83 self._print_date(begin_ns, end_ns)
84 self._print_per_tid_usage(per_tid_table)
85 self._print_per_cpu_usage(per_cpu_table)
86
87 if total_table:
88 self._print_total_cpu_usage(total_table)
89
90 def _create_summary_result_tables(self):
91 total_tables = self._mi_get_result_tables(self._MI_TABLE_CLASS_TOTAL)
92 begin = total_tables[0].timerange.begin
93 end = total_tables[-1].timerange.end
94 summary_table = \
95 self._mi_create_result_table(self._MI_TABLE_CLASS_SUMMARY,
96 begin, end)
97
98 for total_table in total_tables:
99 usage = total_table.rows[0].usage
100 summary_table.append_row(
101 time_range=total_table.timerange,
102 usage=usage,
103 )
104
105 self._mi_clear_result_tables()
106 self._mi_append_result_table(summary_table)
107
108 def _cleanup_prio_list(self, prio_list):
109 prios = {}
110 prio_str = None
111 for p in prio_list:
112 if p.prio in prios:
113 prios[p.prio] += 1
114 else:
115 prios[p.prio] = 1
116 for p in sorted(prios.keys()):
117 if prios[p] > 1:
118 count_str = " (%s times)" % prios[p]
119 else:
120 count_str = ""
121 if prio_str is None:
122 prio_str = "[%s%s" % (p, count_str)
123 else:
124 prio_str = "%s, %s%s" % (prio_str, p, count_str)
125 prio_str = prio_str + "]"
126 return prio_str
127
128 def _get_per_tid_usage_result_table(self, begin_ns, end_ns):
129 result_table = \
130 self._mi_create_result_table(self._MI_TABLE_CLASS_PER_PROC,
131 begin_ns, end_ns)
132 count = 0
133
134 for tid in sorted(self._analysis.tids.values(),
135 key=operator.attrgetter('usage_percent'),
136 reverse=True):
137 prio_list = self._cleanup_prio_list(tid.prio_list)
138
139 result_table.append_row(
140 process=mi.Process(tid.comm, tid=tid.tid),
141 migrations=mi.Integer(tid.migrate_count),
142 prio_list=mi.String(prio_list),
143 usage=mi.Ratio.from_percentage(tid.usage_percent)
144 )
145 count += 1
146
147 if self._args.limit > 0 and count >= self._args.limit:
148 break
149
150 return result_table
151
152 def _get_per_cpu_usage_result_table(self, begin_ns, end_ns):
153 result_table = \
154 self._mi_create_result_table(self._MI_TABLE_CLASS_PER_CPU,
155 begin_ns, end_ns)
156
157 for cpu in sorted(self._analysis.cpus.values(),
158 key=operator.attrgetter('cpu_id')):
159 result_table.append_row(
160 cpu=mi.Cpu(cpu.cpu_id),
161 usage=mi.Ratio.from_percentage(cpu.usage_percent)
162 )
163
164 return result_table
165
166 def _get_total_usage_result_table(self, begin_ns, end_ns):
167 result_table = \
168 self._mi_create_result_table(self._MI_TABLE_CLASS_TOTAL,
169 begin_ns, end_ns)
170
171 cpu_count = len(self.state.cpus)
172 usage_percent = 0
173
174 if not cpu_count:
175 return
176
177 for cpu in sorted(self._analysis.cpus.values(),
178 key=operator.attrgetter('usage_percent'),
179 reverse=True):
180 usage_percent += cpu.usage_percent
181
182 # average per CPU
183 usage_percent /= cpu_count
184 result_table.append_row(
185 usage=mi.Ratio.from_percentage(usage_percent),
186 )
187
188 return result_table
189
190 def _print_per_tid_usage(self, result_table):
191 row_format = '{:<25} {:>10} {}'
192 label_header = row_format.format('Process', 'Migrations', 'Priorities')
193
194 def format_label(row):
195 return row_format.format(
196 '%s (%d)' % (row.process.name, row.process.tid),
197 row.migrations.value,
198 row.prio_list.value,
199 )
200
201 graph = termgraph.BarGraph(
202 title='Per-TID Usage',
203 unit='%',
204 get_value=lambda row: row.usage.to_percentage(),
205 get_label=format_label,
206 label_header=label_header,
207 data=result_table.rows
208 )
209
210 graph.print_graph()
211
212 def _print_per_cpu_usage(self, result_table):
213 graph = termgraph.BarGraph(
214 title='Per-CPU Usage',
215 unit='%',
216 get_value=lambda row: row.usage.to_percentage(),
217 get_label=lambda row: 'CPU %d' % row.cpu.id,
218 data=result_table.rows
219 )
220
221 graph.print_graph()
222
223 def _print_total_cpu_usage(self, result_table):
224 usage_percent = result_table.rows[0].usage.to_percentage()
225 print('\nTotal CPU Usage: %0.02f%%\n' % usage_percent)
226
227 def _add_arguments(self, ap):
228 Command._add_proc_filter_args(ap)
229 Command._add_top_args(ap)
230
231
232 def _run(mi_mode):
233 cputopcmd = Cputop(mi_mode=mi_mode)
234 cputopcmd.run()
235
236
237 def run():
238 _run(mi_mode=False)
239
240
241 def run_mi():
242 _run(mi_mode=True)
This page took 0.035736 seconds and 4 git commands to generate.