45389e6edbe9cdc33aef4041967538be8e0fd7a2
[deliverable/lttng-analyses.git] / lttnganalyses / cli / sched.py
1 # The MIT License (MIT)
2 #
3 # Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
4 # 2015 - Antoine Busque <abusque@efficios.com>
5 #
6 # Permission is hereby granted, free of charge, to any person obtaining a copy
7 # of this software and associated documentation files (the "Software"), to deal
8 # in the Software without restriction, including without limitation the rights
9 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 # copies of the Software, and to permit persons to whom the Software is
11 # furnished to do so, subject to the following conditions:
12 #
13 # The above copyright notice and this permission notice shall be included in
14 # all copies or substantial portions of the Software.
15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 # SOFTWARE.
23
24 import sys
25 import math
26 import operator
27 import statistics
28 import collections
29 from . import mi
30 from . import termgraph
31 from ..core import sched
32 from .command import Command
33 from ..linuxautomaton import common
34
35
36 _SchedStats = collections.namedtuple('_SchedStats', [
37 'count',
38 'min',
39 'max',
40 'stdev',
41 'total',
42 ])
43
44
45 class SchedAnalysisCommand(Command):
46 _DESC = """The sched command."""
47 _ANALYSIS_CLASS = sched.SchedAnalysis
48 _MI_TITLE = 'Scheduling latencies analysis'
49 _MI_DESCRIPTION = \
50 'Scheduling latencies frequency distribution, statistics, top, and log'
51 _MI_TAGS = [mi.Tags.SCHED, mi.Tags.STATS, mi.Tags.FREQ, mi.Tags.TOP,
52 mi.Tags.LOG]
53 _MI_TABLE_CLASS_LOG = 'log'
54 _MI_TABLE_CLASS_TOP = 'top'
55 _MI_TABLE_CLASS_TOTAL_STATS = 'total_stats'
56 _MI_TABLE_CLASS_PER_TID_STATS = 'per_tid_stats'
57 _MI_TABLE_CLASS_PER_PRIO_STATS = 'per_prio_stats'
58 _MI_TABLE_CLASS_FREQ = 'freq'
59 # _MI_TABLE_CLASS_SUMMARY = 'summary'
60 _MI_TABLE_CLASSES = [
61 (
62 _MI_TABLE_CLASS_LOG,
63 'Scheduling log', [
64 ('wakeup_ts', 'Wakeup timestamp', mi.Timestamp),
65 ('switch_ts', 'Switch timestamp', mi.Timestamp),
66 ('latency', 'Scheduling latency', mi.Duration),
67 ('prio', 'Priority', mi.Integer),
68 ('target_cpu', 'Target CPU', mi.Integer),
69 ('wakee_proc', 'Wakee process', mi.Process),
70 ('waker_proc', 'Waker process', mi.Process),
71 ]
72 ),
73 (
74 _MI_TABLE_CLASS_TOP,
75 'Scheduling top', [
76 ('wakeup_ts', 'Wakeup timestamp', mi.Timestamp),
77 ('switch_ts', 'Switch timestamp', mi.Timestamp),
78 ('latency', 'Scheduling latency', mi.Duration),
79 ('prio', 'Priority', mi.Integer),
80 ('target_cpu', 'Target CPU', mi.Integer),
81 ('wakee_proc', 'Wakee process', mi.Process),
82 ('waker_proc', 'Waker process', mi.Process),
83 ]
84 ),
85 (
86 _MI_TABLE_CLASS_TOTAL_STATS,
87 'Scheduling latency stats (total)', [
88 ('count', 'Scheduling count', mi.Integer, 'schedulings'),
89 ('min_latency', 'Minimum latency', mi.Duration),
90 ('avg_latency', 'Average latency', mi.Duration),
91 ('max_latency', 'Maximum latency', mi.Duration),
92 ('stdev_latency', 'Scheduling latency standard deviation',
93 mi.Duration),
94 ]
95 ),
96 (
97 _MI_TABLE_CLASS_PER_TID_STATS,
98 'Scheduling latency stats (per-TID)', [
99 ('process', 'Wakee process', mi.Process),
100 ('count', 'Scheduling count', mi.Integer, 'schedulings'),
101 ('min_latency', 'Minimum latency', mi.Duration),
102 ('avg_latency', 'Average latency', mi.Duration),
103 ('max_latency', 'Maximum latency', mi.Duration),
104 ('stdev_latency', 'Scheduling latency standard deviation',
105 mi.Duration),
106 ('prio_list', 'Chronological priorities', mi.String),
107 ]
108 ),
109 (
110 _MI_TABLE_CLASS_PER_PRIO_STATS,
111 'Scheduling latency stats (per-prio)', [
112 ('prio', 'Priority', mi.Integer),
113 ('count', 'Scheduling count', mi.Integer, 'schedulings'),
114 ('min_latency', 'Minimum latency', mi.Duration),
115 ('avg_latency', 'Average latency', mi.Duration),
116 ('max_latency', 'Maximum latency', mi.Duration),
117 ('stdev_latency', 'Scheduling latency standard deviation',
118 mi.Duration),
119 ]
120 ),
121 (
122 _MI_TABLE_CLASS_FREQ,
123 'Scheduling latency frequency distribution', [
124 ('duration_lower', 'Duration (lower bound)', mi.Duration),
125 ('duration_upper', 'Duration (upper bound)', mi.Duration),
126 ('count', 'Scheduling count', mi.Integer, 'schedulings'),
127 ]
128 ),
129 ]
130
131 def _analysis_tick(self, begin_ns, end_ns):
132 log_table = None
133 top_table = None
134 total_stats_table = None
135 per_tid_stats_table = None
136 per_prio_stats_table = None
137 total_freq_tables = None
138 per_tid_freq_tables = None
139 per_prio_freq_tables = None
140
141 if self._args.log:
142 log_table = self._get_log_result_table(begin_ns, end_ns)
143
144 if self._args.top:
145 top_table = self._get_top_result_table(begin_ns, end_ns)
146
147 if self._args.stats:
148 if self._args.total:
149 total_stats_table = self._get_total_stats_result_table(
150 begin_ns, end_ns)
151
152 if self._args.per_tid:
153 per_tid_stats_table = self._get_per_tid_stats_result_table(
154 begin_ns, end_ns)
155
156 if self._args.per_prio:
157 per_prio_stats_table = self._get_per_prio_stats_result_table(
158 begin_ns, end_ns)
159
160 if self._args.freq:
161 if self._args.total:
162 total_freq_tables = self._get_total_freq_result_tables(
163 begin_ns, end_ns)
164
165 if self._args.per_tid:
166 per_tid_freq_tables = self._get_per_tid_freq_result_tables(
167 begin_ns, end_ns)
168
169 if self._args.per_prio:
170 per_prio_freq_tables = self._get_per_prio_freq_result_tables(
171 begin_ns, end_ns)
172
173 if self._mi_mode:
174 if log_table:
175 self._mi_append_result_table(log_table)
176
177 if top_table:
178 self._mi_append_result_table(top_table)
179
180 if total_stats_table and total_stats_table.rows:
181 self._mi_append_result_table(total_stats_table)
182
183 if per_tid_stats_table and per_tid_stats_table.rows:
184 self._mi_append_result_table(per_tid_stats_table)
185
186 if per_prio_stats_table and per_prio_stats_table.rows:
187 self._mi_append_result_table(per_prio_stats_table)
188
189 if self._args.freq_series:
190 if total_freq_tables:
191 self._mi_append_result_tables(total_freq_tables)
192
193 if per_tid_freq_tables:
194 per_tid_freq_tables = [
195 self._get_per_tid_freq_series_table(
196 per_tid_freq_tables)
197 ]
198
199 self._mi_append_result_tables(per_tid_freq_tables)
200
201 if per_prio_freq_tables:
202 per_prio_freq_tables = [
203 self._get_per_prio_freq_series_table(
204 per_prio_freq_tables)
205 ]
206
207 self._mi_append_result_tables(per_prio_freq_tables)
208 else:
209 self._print_date(begin_ns, end_ns)
210
211 if self._args.stats:
212 if total_stats_table:
213 self._print_total_stats(total_stats_table)
214 if per_tid_stats_table:
215 self._print_per_tid_stats(per_tid_stats_table)
216 if per_prio_stats_table:
217 self._print_per_prio_stats(per_prio_stats_table)
218
219 if self._args.freq:
220 if total_freq_tables:
221 self._print_freq(total_freq_tables)
222 if per_tid_freq_tables:
223 self._print_freq(per_tid_freq_tables)
224 if per_prio_freq_tables:
225 self._print_freq(per_prio_freq_tables)
226
227 if log_table:
228 self._print_sched_events(log_table)
229
230 if top_table:
231 self._print_sched_events(top_table)
232
233 def _get_total_sched_lists_stats(self):
234 total_list = self._analysis.sched_list
235 stdev = self._compute_sched_latency_stdev(total_list)
236 total_stats = _SchedStats(
237 count=self._analysis.count,
238 min=self._analysis.min_latency,
239 max=self._analysis.max_latency,
240 stdev=stdev,
241 total=self._analysis.total_latency
242 )
243
244 return [total_list], total_stats
245
246 def _get_tid_sched_lists_stats(self):
247 tid_sched_lists = {}
248 tid_stats = {}
249
250 for sched_event in self._analysis.sched_list:
251 tid = sched_event.wakee_proc.tid
252 if tid not in tid_sched_lists:
253 tid_sched_lists[tid] = []
254
255 tid_sched_lists[tid].append(sched_event)
256
257 for tid in tid_sched_lists:
258 sched_list = tid_sched_lists[tid]
259
260 if not sched_list:
261 continue
262
263 stdev = self._compute_sched_latency_stdev(sched_list)
264 latencies = [sched.latency for sched in sched_list]
265 count = len(latencies)
266 min_latency = min(latencies)
267 max_latency = max(latencies)
268 total_latency = sum(latencies)
269
270 tid_stats[tid] = _SchedStats(
271 count=count,
272 min=min_latency,
273 max=max_latency,
274 stdev=stdev,
275 total=total_latency,
276 )
277
278 return tid_sched_lists, tid_stats
279
280 def _get_prio_sched_lists_stats(self):
281 prio_sched_lists = {}
282 prio_stats = {}
283
284 for sched_event in self._analysis.sched_list:
285 if sched_event.prio not in prio_sched_lists:
286 prio_sched_lists[sched_event.prio] = []
287
288 prio_sched_lists[sched_event.prio].append(sched_event)
289
290 for prio in prio_sched_lists:
291 sched_list = prio_sched_lists[prio]
292
293 if not sched_list:
294 continue
295
296 stdev = self._compute_sched_latency_stdev(sched_list)
297 latencies = [sched.latency for sched in sched_list]
298 count = len(latencies)
299 min_latency = min(latencies)
300 max_latency = max(latencies)
301 total_latency = sum(latencies)
302
303 prio_stats[prio] = _SchedStats(
304 count=count,
305 min=min_latency,
306 max=max_latency,
307 stdev=stdev,
308 total=total_latency,
309 )
310
311 return prio_sched_lists, prio_stats
312
313 def _get_log_result_table(self, begin_ns, end_ns):
314 result_table = self._mi_create_result_table(self._MI_TABLE_CLASS_LOG,
315 begin_ns, end_ns)
316
317 for sched_event in self._analysis.sched_list:
318 wakee_proc = mi.Process(sched_event.wakee_proc.comm,
319 sched_event.wakee_proc.pid,
320 sched_event.wakee_proc.tid)
321
322 if sched_event.waker_proc:
323 waker_proc = mi.Process(sched_event.waker_proc.comm,
324 sched_event.waker_proc.pid,
325 sched_event.waker_proc.tid)
326 else:
327 waker_proc = mi.Empty()
328
329 result_table.append_row(
330 wakeup_ts=mi.Timestamp(sched_event.wakeup_ts),
331 switch_ts=mi.Timestamp(sched_event.switch_ts),
332 latency=mi.Duration(sched_event.latency),
333 prio=mi.Integer(sched_event.prio),
334 target_cpu=mi.Integer(sched_event.target_cpu),
335 wakee_proc=wakee_proc,
336 waker_proc=waker_proc,
337 )
338
339 return result_table
340
341 def _get_top_result_table(self, begin_ns, end_ns):
342 result_table = self._mi_create_result_table(
343 self._MI_TABLE_CLASS_TOP, begin_ns, end_ns)
344
345 top_events = sorted(self._analysis.sched_list,
346 key=operator.attrgetter('latency'),
347 reverse=True)
348 top_events = top_events[:self._args.limit]
349
350 for sched_event in top_events:
351 wakee_proc = mi.Process(sched_event.wakee_proc.comm,
352 sched_event.wakee_proc.pid,
353 sched_event.wakee_proc.tid)
354
355 if sched_event.waker_proc:
356 waker_proc = mi.Process(sched_event.waker_proc.comm,
357 sched_event.waker_proc.pid,
358 sched_event.waker_proc.tid)
359 else:
360 waker_proc = mi.Empty()
361
362 result_table.append_row(
363 wakeup_ts=mi.Timestamp(sched_event.wakeup_ts),
364 switch_ts=mi.Timestamp(sched_event.switch_ts),
365 latency=mi.Duration(sched_event.latency),
366 prio=mi.Integer(sched_event.prio),
367 target_cpu=mi.Integer(sched_event.target_cpu),
368 wakee_proc=wakee_proc,
369 waker_proc=waker_proc,
370 )
371
372 return result_table
373
374 def _get_total_stats_result_table(self, begin_ns, end_ns):
375 stats_table = \
376 self._mi_create_result_table(self._MI_TABLE_CLASS_TOTAL_STATS,
377 begin_ns, end_ns)
378
379 stdev = self._compute_sched_latency_stdev(self._analysis.sched_list)
380 if math.isnan(stdev):
381 stdev = mi.Unknown()
382 else:
383 stdev = mi.Duration(stdev)
384
385 stats_table.append_row(
386 count=mi.Integer(self._analysis.count),
387 min_latency=mi.Duration(self._analysis.min_latency),
388 avg_latency=mi.Duration(self._analysis.total_latency /
389 self._analysis.count),
390 max_latency=mi.Duration(self._analysis.max_latency),
391 stdev_latency=stdev,
392 )
393
394 return stats_table
395
396 def _cleanup_prio_list(self, prio_list):
397 prios = {}
398 prio_str = None
399 for p in prio_list:
400 if p.prio in prios:
401 prios[p.prio] += 1
402 else:
403 prios[p.prio] = 1
404 for p in sorted(prios.keys()):
405 if prios[p] > 1:
406 count_str = " (%s times)" % prios[p]
407 else:
408 count_str = ""
409 if prio_str is None:
410 prio_str = "[%s%s" % (p, count_str)
411 else:
412 prio_str = "%s, %s%s" % (prio_str, p, count_str)
413 prio_str = prio_str + "]"
414 return prio_str
415
416 def _get_per_tid_stats_result_table(self, begin_ns, end_ns):
417 stats_table = \
418 self._mi_create_result_table(self._MI_TABLE_CLASS_PER_TID_STATS,
419 begin_ns, end_ns)
420
421 tid_stats_list = sorted(list(self._analysis.tids.values()),
422 key=lambda proc: proc.comm.lower())
423
424 for tid_stats in tid_stats_list:
425 if not tid_stats.sched_list:
426 continue
427
428 stdev = self._compute_sched_latency_stdev(tid_stats.sched_list)
429 if math.isnan(stdev):
430 stdev = mi.Unknown()
431 else:
432 stdev = mi.Duration(stdev)
433
434 prio_list = self._cleanup_prio_list(tid_stats.prio_list)
435
436 stats_table.append_row(
437 process=mi.Process(tid=tid_stats.tid, name=tid_stats.comm),
438 count=mi.Integer(tid_stats.count),
439 min_latency=mi.Duration(tid_stats.min_latency),
440 avg_latency=mi.Duration(tid_stats.total_latency /
441 tid_stats.count),
442 max_latency=mi.Duration(tid_stats.max_latency),
443 stdev_latency=stdev,
444 prio_list=mi.String(prio_list),
445 )
446
447 return stats_table
448
449 def _get_per_prio_stats_result_table(self, begin_ns, end_ns):
450 stats_table = \
451 self._mi_create_result_table(self._MI_TABLE_CLASS_PER_PRIO_STATS,
452 begin_ns, end_ns)
453
454 _, prio_stats = self._get_prio_sched_lists_stats()
455
456 for prio in sorted(prio_stats):
457 stats = prio_stats[prio]
458 stdev = stats.stdev
459
460 if math.isnan(stdev):
461 stdev = mi.Unknown()
462 else:
463 stdev = mi.Duration(stdev)
464
465 count = stats.count
466 min_latency = stats.min
467 max_latency = stats.max
468 total_latency = stats.total
469
470 stats_table.append_row(
471 prio=mi.Integer(prio),
472 count=mi.Integer(count),
473 min_latency=mi.Duration(min_latency),
474 avg_latency=mi.Duration(total_latency / count),
475 max_latency=mi.Duration(max_latency),
476 stdev_latency=stdev,
477 )
478
479 return stats_table
480
481 def _get_per_tid_freq_series_table(self, freq_tables):
482 if not freq_tables:
483 return
484
485 column_infos = [
486 ('duration_lower', 'Duration (lower bound)', mi.Duration),
487 ('duration_upper', 'Duration (upper bound)', mi.Duration),
488 ]
489
490 for index, freq_table in enumerate(freq_tables):
491 column_infos.append((
492 'tid{}'.format(index),
493 freq_table.subtitle,
494 mi.Integer,
495 'schedulings'
496 ))
497
498 title = 'Scheduling latencies frequency distributions'
499 table_class = mi.TableClass(None, title, column_infos)
500 begin = freq_tables[0].timerange.begin
501 end = freq_tables[0].timerange.end
502 result_table = mi.ResultTable(table_class, begin, end)
503
504 for row_index, freq0_row in enumerate(freq_tables[0].rows):
505 row_tuple = [
506 freq0_row.duration_lower,
507 freq0_row.duration_upper,
508 ]
509
510 for freq_table in freq_tables:
511 freq_row = freq_table.rows[row_index]
512 row_tuple.append(freq_row.count)
513
514 result_table.append_row_tuple(tuple(row_tuple))
515
516 return result_table
517
518 def _get_per_prio_freq_series_table(self, freq_tables):
519 if not freq_tables:
520 return
521
522 column_infos = [
523 ('duration_lower', 'Duration (lower bound)', mi.Duration),
524 ('duration_upper', 'Duration (upper bound)', mi.Duration),
525 ]
526
527 for index, freq_table in enumerate(freq_tables):
528 column_infos.append((
529 'prio{}'.format(index),
530 freq_table.subtitle,
531 mi.Integer,
532 'schedulings'
533 ))
534
535 title = 'Scheduling latencies frequency distributions'
536 table_class = mi.TableClass(None, title, column_infos)
537 begin = freq_tables[0].timerange.begin
538 end = freq_tables[0].timerange.end
539 result_table = mi.ResultTable(table_class, begin, end)
540
541 for row_index, freq0_row in enumerate(freq_tables[0].rows):
542 row_tuple = [
543 freq0_row.duration_lower,
544 freq0_row.duration_upper,
545 ]
546
547 for freq_table in freq_tables:
548 freq_row = freq_table.rows[row_index]
549 row_tuple.append(freq_row.count)
550
551 result_table.append_row_tuple(tuple(row_tuple))
552
553 return result_table
554
555 def _fill_freq_result_table(self, sched_list, stats, min_duration,
556 max_duration, step, freq_table):
557 # The number of bins for the histogram
558 resolution = self._args.freq_resolution
559
560 if not self._args.freq_uniform:
561 if self._args.min is not None:
562 min_duration = self._args.min
563 else:
564 min_duration = stats.min
565
566 if self._args.max is not None:
567 max_duration = self._args.max
568 else:
569 max_duration = stats.max
570
571 # ns to µs
572 min_duration /= 1000
573 max_duration /= 1000
574
575 step = (max_duration - min_duration) / resolution
576
577 if step == 0:
578 return
579
580 buckets = []
581 counts = []
582
583 for i in range(resolution):
584 buckets.append(i * step)
585 counts.append(0)
586
587 for sched_event in sched_list:
588 duration = sched_event.latency / 1000
589 index = int((duration - min_duration) / step)
590
591 if index >= resolution:
592 # special case for max value: put in last bucket (includes
593 # its upper bound)
594 if duration == max_duration:
595 counts[index - 1] += 1
596
597 continue
598
599 counts[index] += 1
600
601 for index, count in enumerate(counts):
602 lower_bound = index * step + min_duration
603 upper_bound = (index + 1) * step + min_duration
604 freq_table.append_row(
605 duration_lower=mi.Duration.from_us(lower_bound),
606 duration_upper=mi.Duration.from_us(upper_bound),
607 count=mi.Integer(count),
608 )
609
610 def _get_total_freq_result_tables(self, begin_ns, end_ns):
611 freq_tables = []
612 sched_lists, sched_stats = self._get_total_sched_lists_stats()
613 min_duration = None
614 max_duration = None
615 step = None
616
617 if self._args.freq_uniform:
618 latencies = []
619
620 for sched_list in sched_lists:
621 latencies += [sched.latency for sched in sched_list]
622
623 min_duration, max_duration, step = \
624 self._get_uniform_freq_values(latencies)
625
626 for sched_list in sched_lists:
627 freq_table = \
628 self._mi_create_result_table(self._MI_TABLE_CLASS_FREQ,
629 begin_ns, end_ns)
630 self._fill_freq_result_table(sched_list, sched_stats, min_duration,
631 max_duration, step, freq_table)
632 freq_tables.append(freq_table)
633
634 return freq_tables
635
636 def _get_per_tid_freq_result_tables(self, begin_ns, end_ns):
637 freq_tables = []
638 tid_sched_lists, tid_stats = self._get_tid_sched_lists_stats()
639 min_duration = None
640 max_duration = None
641 step = None
642
643 if self._args.freq_uniform:
644 latencies = []
645
646 for sched_list in tid_sched_lists.values():
647 latencies += [sched.latency for sched in sched_list]
648
649 min_duration, max_duration, step = \
650 self._get_uniform_freq_values(latencies)
651
652 for tid in sorted(tid_sched_lists):
653 sched_list = tid_sched_lists[tid]
654 stats = tid_stats[tid]
655 subtitle = 'TID: {}'.format(tid)
656 freq_table = \
657 self._mi_create_result_table(self._MI_TABLE_CLASS_FREQ,
658 begin_ns, end_ns, subtitle)
659 self._fill_freq_result_table(sched_list, stats, min_duration,
660 max_duration, step, freq_table)
661 freq_tables.append(freq_table)
662
663 return freq_tables
664
665 def _get_per_prio_freq_result_tables(self, begin_ns, end_ns):
666 freq_tables = []
667 prio_sched_lists, prio_stats = self._get_prio_sched_lists_stats()
668 min_duration = None
669 max_duration = None
670 step = None
671
672 if self._args.freq_uniform:
673 latencies = []
674
675 for sched_list in prio_sched_lists.values():
676 latencies += [sched.latency for sched in sched_list]
677
678 min_duration, max_duration, step = \
679 self._get_uniform_freq_values(latencies)
680
681 for prio in sorted(prio_sched_lists):
682 sched_list = prio_sched_lists[prio]
683 stats = prio_stats[prio]
684 subtitle = 'Priority: {}'.format(prio)
685 freq_table = \
686 self._mi_create_result_table(self._MI_TABLE_CLASS_FREQ,
687 begin_ns, end_ns, subtitle)
688 self._fill_freq_result_table(sched_list, stats, min_duration,
689 max_duration, step, freq_table)
690 freq_tables.append(freq_table)
691
692 return freq_tables
693
694 def _compute_sched_latency_stdev(self, sched_events):
695 if len(sched_events) < 2:
696 return float('nan')
697
698 sched_latencies = []
699 for sched_event in sched_events:
700 sched_latencies.append(sched_event.latency)
701
702 return statistics.stdev(sched_latencies)
703
704 def _ns_to_hour_nsec(self, ts):
705 return common.ns_to_hour_nsec(ts, self._args.multi_day, self._args.gmt)
706
707 def _print_sched_events(self, result_table):
708 fmt = '[{:<18}, {:<18}] {:>15} {:>10} {:>3} {:<25} {:<25}'
709 title_fmt = '{:<20} {:<19} {:>15} {:>10} {:>3} {:<25} {:<25}'
710 print()
711 print(result_table.title)
712 print(title_fmt.format('Wakeup', 'Switch', 'Latency (us)', 'Priority',
713 'CPU', 'Wakee', 'Waker'))
714 for row in result_table.rows:
715 wakeup_ts = row.wakeup_ts.value
716 switch_ts = row.switch_ts.value
717 latency = row.latency.value
718 prio = row.prio.value
719 target_cpu = row.target_cpu.value
720 wakee_proc = row.wakee_proc
721 waker_proc = row.waker_proc
722
723 wakee_str = '%s (%d)' % (wakee_proc.name, wakee_proc.tid)
724 if isinstance(waker_proc, mi.Empty):
725 waker_str = 'Unknown (N/A)'
726 else:
727 waker_str = '%s (%d)' % (waker_proc.name, waker_proc.tid)
728
729 print(fmt.format(self._ns_to_hour_nsec(wakeup_ts),
730 self._ns_to_hour_nsec(switch_ts),
731 '%0.03f' % (latency / 1000), prio,
732 target_cpu, wakee_str, waker_str))
733
734 def _print_total_stats(self, stats_table):
735 row_format = '{:<12} {:<12} {:<12} {:<12} {:<12}'
736 header = row_format.format(
737 'Count', 'Min', 'Avg', 'Max', 'Stdev'
738 )
739
740 if stats_table.rows:
741 print()
742 print(stats_table.title + ' (us)')
743 print(header)
744 for row in stats_table.rows:
745 if type(row.stdev_latency) is mi.Unknown:
746 stdev_str = '?'
747 else:
748 stdev_str = '%0.03f' % row.stdev_latency.to_us()
749
750 row_str = row_format.format(
751 '%d' % row.count.value,
752 '%0.03f' % row.min_latency.to_us(),
753 '%0.03f' % row.avg_latency.to_us(),
754 '%0.03f' % row.max_latency.to_us(),
755 '%s' % stdev_str,
756 )
757
758 print(row_str)
759
760 def _print_per_tid_stats(self, stats_table):
761 row_format = '{:<25} {:>8} {:>12} {:>12} {:>12} {:>12} {}'
762 header = row_format.format(
763 'Process', 'Count', 'Min', 'Avg', 'Max', 'Stdev', 'Priorities'
764 )
765
766 if stats_table.rows:
767 print()
768 print(stats_table.title + ' (us)')
769 print(header)
770 for row in stats_table.rows:
771 if type(row.stdev_latency) is mi.Unknown:
772 stdev_str = '?'
773 else:
774 stdev_str = '%0.03f' % row.stdev_latency.to_us()
775
776 proc = row.process
777 proc_str = '%s (%d)' % (proc.name, proc.tid)
778
779 row_str = row_format.format(
780 '%s' % proc_str,
781 '%d' % row.count.value,
782 '%0.03f' % row.min_latency.to_us(),
783 '%0.03f' % row.avg_latency.to_us(),
784 '%0.03f' % row.max_latency.to_us(),
785 '%s' % stdev_str,
786 '%s' % row.prio_list.value,
787 )
788
789 print(row_str)
790
791 def _print_per_prio_stats(self, stats_table):
792 row_format = '{:>4} {:>8} {:>12} {:>12} {:>12} {:>12}'
793 header = row_format.format(
794 'Prio', 'Count', 'Min', 'Avg', 'Max', 'Stdev'
795 )
796
797 if stats_table.rows:
798 print()
799 print(stats_table.title + ' (us)')
800 print(header)
801 for row in stats_table.rows:
802 if type(row.stdev_latency) is mi.Unknown:
803 stdev_str = '?'
804 else:
805 stdev_str = '%0.03f' % row.stdev_latency.to_us()
806
807 row_str = row_format.format(
808 '%d' % row.prio.value,
809 '%d' % row.count.value,
810 '%0.03f' % row.min_latency.to_us(),
811 '%0.03f' % row.avg_latency.to_us(),
812 '%0.03f' % row.max_latency.to_us(),
813 '%s' % stdev_str,
814 )
815
816 print(row_str)
817
818 def _print_frequency_distribution(self, freq_table):
819 title_fmt = 'Scheduling latency frequency distribution - {}'
820
821 graph = termgraph.FreqGraph(
822 data=freq_table.rows,
823 get_value=lambda row: row.count.value,
824 get_lower_bound=lambda row: row.duration_lower.to_us(),
825 title=title_fmt.format(freq_table.subtitle),
826 unit='µs'
827 )
828
829 graph.print_graph()
830
831 def _print_freq(self, freq_tables):
832 for freq_table in freq_tables:
833 self._print_frequency_distribution(freq_table)
834
835 def _validate_transform_args(self, args):
836 # If neither --total nor --per-prio are specified, default
837 # to --per-tid
838 if not (args.total or args.per_prio):
839 args.per_tid = True
840
841 def _add_arguments(self, ap):
842 Command._add_min_max_args(ap)
843 Command._add_proc_filter_args(ap)
844 Command._add_freq_args(
845 ap, help='Output the frequency distribution of sched switch '
846 'latencies')
847 Command._add_top_args(ap, help='Output the top sched switch latencies')
848 Command._add_log_args(
849 ap, help='Output the sched switches in chronological order')
850 Command._add_stats_args(ap, help='Output sched switch statistics')
851 ap.add_argument('--total', action='store_true',
852 help='Group all results (applies to stats and freq)')
853 ap.add_argument('--per-tid', action='store_true',
854 help='Group results per-TID (applies to stats and '
855 'freq) (default)')
856 ap.add_argument('--per-prio', action='store_true',
857 help='Group results per-prio (applies to stats and '
858 'freq)')
859
860
861 def _run(mi_mode):
862 schedcmd = SchedAnalysisCommand(mi_mode=mi_mode)
863 schedcmd.run()
864
865
866 def _runstats(mi_mode):
867 sys.argv.insert(1, '--stats')
868 _run(mi_mode)
869
870
871 def _runlog(mi_mode):
872 sys.argv.insert(1, '--log')
873 _run(mi_mode)
874
875
876 def _runtop(mi_mode):
877 sys.argv.insert(1, '--top')
878 _run(mi_mode)
879
880
881 def _runfreq(mi_mode):
882 sys.argv.insert(1, '--freq')
883 _run(mi_mode)
884
885
886 def runstats():
887 _runstats(mi_mode=False)
888
889
890 def runlog():
891 _runlog(mi_mode=False)
892
893
894 def runtop():
895 _runtop(mi_mode=False)
896
897
898 def runfreq():
899 _runfreq(mi_mode=False)
900
901
902 def runstats_mi():
903 _runstats(mi_mode=True)
904
905
906 def runlog_mi():
907 _runlog(mi_mode=True)
908
909
910 def runtop_mi():
911 _runtop(mi_mode=True)
912
913
914 def runfreq_mi():
915 _runfreq(mi_mode=True)
This page took 0.049539 seconds and 4 git commands to generate.