3 # The MIT License (MIT)
5 # Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
6 # 2015 - Antoine Busque <abusque@efficios.com>
8 # Permission is hereby granted, free of charge, to any person obtaining a copy
9 # of this software and associated documentation files (the "Software"), to deal
10 # in the Software without restriction, including without limitation the rights
11 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 # copies of the Software, and to permit persons to whom the Software is
13 # furnished to do so, subject to the following conditions:
15 # The above copyright notice and this permission notice shall be included in
16 # all copies or substantial portions of the Software.
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 from .command
import Command
27 from ..core
import irq
as core_irq
28 from ..linuxautomaton
import common
, sv
29 from ..ascii_graph
import Pyasciigraph
35 class IrqAnalysisCommand(Command
):
36 _DESC
= """The irq command."""
39 super().__init
__(self
._add
_arguments
,
40 enable_max_min_args
=True,
43 enable_stats_arg
=True)
45 def _validate_transform_args(self
):
46 self
._arg
_irq
_filter
_list
= None
47 self
._arg
_softirq
_filter
_list
= None
50 self
._arg
_irq
_filter
_list
= self
._args
.irq
.split(',')
51 if self
._args
.softirq
:
52 self
._arg
_softirq
_filter
_list
= self
._args
.softirq
.split(',')
54 def _default_args(self
, stats
, log
, freq
):
56 self
._arg
_stats
= True
62 def run(self
, stats
=False, log
=False, freq
=False):
63 # parse arguments first
65 # validate, transform and save specific arguments
66 self
._validate
_transform
_args
()
67 # handle the default args for different executables
68 self
._default
_args
(stats
, log
, freq
)
71 # create the appropriate analysis/analyses
72 self
._create
_analysis
()
74 self
._run
_analysis
(self
._reset
_total
, self
._refresh
)
76 self
._print
_results
(self
.start_ns
, self
.trace_end_ts
)
89 def _create_analysis(self
):
90 self
._analysis
= core_irq
.IrqAnalysis(self
.state
,
94 def _compute_duration_stdev(self
, irq_stats_item
):
95 if irq_stats_item
.count
< 2:
99 for irq
in irq_stats_item
.irq_list
:
100 durations
.append(irq
.end_ts
- irq
.begin_ts
)
102 return statistics
.stdev(durations
)
104 def _compute_raise_latency_stdev(self
, irq_stats_item
):
105 if irq_stats_item
.raise_count
< 2:
109 for irq
in irq_stats_item
.irq_list
:
110 if irq
.raise_ts
is None:
113 raise_latencies
.append(irq
.begin_ts
- irq
.raise_ts
)
115 return statistics
.stdev(raise_latencies
)
117 def _print_frequency_distribution(self
, irq_stats_item
, id):
118 # The number of bins for the histogram
119 resolution
= self
._arg
_freq
_resolution
121 min_duration
= irq_stats_item
.min_duration
122 max_duration
= irq_stats_item
.max_duration
127 step
= (max_duration
- min_duration
) / resolution
133 graph
= Pyasciigraph()
134 for i
in range(resolution
):
135 buckets
.append(i
* step
)
137 for irq
in irq_stats_item
.irq_list
:
138 duration
= (irq
.end_ts
- irq
.begin_ts
) / 1000
139 index
= min(int((duration
- min_duration
) / step
), resolution
- 1)
143 for index
, value
in enumerate(values
):
144 # The graph data format is a tuple (info, value). Here info
145 # is the lower bound of the bucket, value the bucket's count
146 graph_data
.append(('%0.03f' % (index
* step
+ min_duration
),
149 graph_lines
= graph
.graph(
150 'Handler duration frequency distribution %s (%s) (usec)' %
151 (irq_stats_item
.name
, id),
157 for line
in graph_lines
:
160 def _filter_irq(self
, irq
):
161 if type(irq
) is sv
.HardIRQ
:
162 if self
._arg
_irq
_filter
_list
:
163 return str(irq
.id) in self
._arg
_irq
_filter
_list
164 if self
._arg
_softirq
_filter
_list
:
167 if self
._arg
_softirq
_filter
_list
:
168 return str(irq
.id) in self
._arg
_softirq
_filter
_list
169 if self
._arg
_irq
_filter
_list
:
174 def _print_irq_log(self
):
175 fmt
= '[{:<18}, {:<18}] {:>15} {:>4} {:<9} {:>4} {:<22}'
176 title_fmt
= '{:<20} {:<19} {:>15} {:>4} {:<9} {:>4} {:<22}'
177 print(title_fmt
.format('Begin', 'End', 'Duration (us)', 'CPU',
178 'Type', '#', 'Name'))
179 for irq
in self
._analysis
.irq_list
:
180 if not self
._filter
_irq
(irq
):
184 if type(irq
) is sv
.HardIRQ
:
185 name
= self
._analysis
.hard_irq_stats
[irq
.id].name
188 name
= self
._analysis
.softirq_stats
[irq
.id].name
190 if irq
.raise_ts
is not None:
191 raise_ts
= ' (raised at %s)' % \
192 (common
.ns_to_hour_nsec(irq
.raise_ts
,
196 print(fmt
.format(common
.ns_to_hour_nsec(irq
.begin_ts
,
199 common
.ns_to_hour_nsec(irq
.end_ts
,
202 '%0.03f' % ((irq
.end_ts
- irq
.begin_ts
) / 1000),
203 '%d' % irq
.cpu_id
, irqtype
, irq
.id,
206 def _print_irq_stats(self
, irq_stats
, filter_list
, header
):
207 header_printed
= False
208 for id in sorted(irq_stats
):
209 if filter_list
and str(id) not in filter_list
:
212 irq_stats_item
= irq_stats
[id]
213 if irq_stats_item
.count
== 0:
217 if self
._arg
_freq
or not header_printed
:
219 header_printed
= True
221 if type(irq_stats_item
) is core_irq
.HardIrqStats
:
222 self
._print
_hard
_irq
_stats
_item
(irq_stats_item
, id)
224 self
._print
_soft
_irq
_stats
_item
(irq_stats_item
, id)
227 self
._print
_frequency
_distribution
(irq_stats_item
, id)
231 def _print_hard_irq_stats_item(self
, irq_stats_item
, id):
232 output_str
= self
._get
_duration
_stats
_str
(irq_stats_item
, id)
235 def _print_soft_irq_stats_item(self
, irq_stats_item
, id):
236 output_str
= self
._get
_duration
_stats
_str
(irq_stats_item
, id)
237 if irq_stats_item
.raise_count
!= 0:
238 output_str
+= self
._get
_raise
_latency
_str
(irq_stats_item
, id)
242 def _get_duration_stats_str(self
, irq_stats_item
, id):
243 format_str
= '{:<3} {:<18} {:>5} {:>12} {:>12} {:>12} {:>12} {:<2}'
245 avg_duration
= irq_stats_item
.total_duration
/ irq_stats_item
.count
246 duration_stdev
= self
._compute
_duration
_stdev
(irq_stats_item
)
247 min_duration
= irq_stats_item
.min_duration
248 max_duration
= irq_stats_item
.max_duration
251 duration_stdev
/= 1000
255 if math
.isnan(duration_stdev
):
256 duration_stdev_str
= '?'
258 duration_stdev_str
= '%0.03f' % duration_stdev
260 output_str
= format_str
.format('%d:' % id,
261 '<%s>' % irq_stats_item
.name
,
262 '%d' % irq_stats_item
.count
,
263 '%0.03f' % min_duration
,
264 '%0.03f' % avg_duration
,
265 '%0.03f' % max_duration
,
266 '%s' % duration_stdev_str
,
270 def _get_raise_latency_str(self
, irq_stats_item
, id):
271 format_str
= ' {:>6} {:>12} {:>12} {:>12} {:>12}'
273 avg_raise_latency
= (irq_stats_item
.total_raise_latency
/
274 irq_stats_item
.raise_count
)
275 raise_latency_stdev
= self
._compute
_raise
_latency
_stdev
(irq_stats_item
)
276 min_raise_latency
= irq_stats_item
.min_raise_latency
277 max_raise_latency
= irq_stats_item
.max_raise_latency
279 avg_raise_latency
/= 1000
280 raise_latency_stdev
/= 1000
281 min_raise_latency
/= 1000
282 max_raise_latency
/= 1000
284 if math
.isnan(raise_latency_stdev
):
285 raise_latency_stdev_str
= '?'
287 raise_latency_stdev_str
= '%0.03f' % raise_latency_stdev
289 output_str
= format_str
.format(irq_stats_item
.raise_count
,
290 '%0.03f' % min_raise_latency
,
291 '%0.03f' % avg_raise_latency
,
292 '%0.03f' % max_raise_latency
,
293 '%s' % raise_latency_stdev_str
)
296 def _print_results(self
, begin_ns
, end_ns
):
297 if self
._arg
_stats
or self
._arg
_freq
:
298 self
._print
_stats
(begin_ns
, end_ns
)
300 self
._print
_irq
_log
()
302 def _print_stats(self
, begin_ns
, end_ns
):
303 self
._print
_date
(begin_ns
, end_ns
)
305 if self
._arg
_irq
_filter
_list
is not None or \
306 self
._arg
_softirq
_filter
_list
is None:
307 header_format
= '{:<52} {:<12}\n' \
308 '{:<22} {:<14} {:<12} {:<12} {:<10} {:<12}\n'
309 header
= header_format
.format(
310 'Hard IRQ', 'Duration (us)',
311 '', 'count', 'min', 'avg', 'max', 'stdev'
313 header
+= ('-' * 82 + '|')
314 self
._print
_irq
_stats
(self
._analysis
.hard_irq_stats
,
315 self
._arg
_irq
_filter
_list
,
318 if self
._arg
_softirq
_filter
_list
is not None or \
319 self
._arg
_irq
_filter
_list
is None:
320 header_format
= '{:<52} {:<52} {:<12}\n' \
321 '{:<22} {:<14} {:<12} {:<12} {:<10} {:<4} ' \
322 '{:<3} {:<14} {:<12} {:<12} {:<10} {:<12}\n'
323 header
= header_format
.format(
324 'Soft IRQ', 'Duration (us)',
325 'Raise latency (us)', '',
326 'count', 'min', 'avg', 'max', 'stdev', ' |',
327 'count', 'min', 'avg', 'max', 'stdev'
329 header
+= '-' * 82 + '|' + '-' * 60
330 self
._print
_irq
_stats
(self
._analysis
.softirq_stats
,
331 self
._arg
_softirq
_filter
_list
,
334 def _reset_total(self
, start_ts
):
335 self
._analysis
.reset()
337 def _refresh(self
, begin
, end
):
338 self
._print
_results
(begin
, end
)
339 self
._reset
_total
(end
)
341 def _add_arguments(self
, ap
):
342 ap
.add_argument('--irq', type=str, default
=None,
343 help='Show results only for the list of IRQ')
344 ap
.add_argument('--softirq', type=str, default
=None,
345 help='Show results only for the list of '
352 irqcmd
= IrqAnalysisCommand()
359 irqcmd
= IrqAnalysisCommand()
366 irqcmd
= IrqAnalysisCommand()
This page took 0.056256 seconds and 5 git commands to generate.