drm/rockchip: Don't change hdmi reference clock rate
[drm/drm-misc.git] / tools / perf / scripts / python / task-analyzer.py
blob3f1df9894246bc357fbd32c1361505a0e22ac546
1 # task-analyzer.py - comprehensive perf tasks analysis
2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (c) 2022, Hagen Paul Pfeifer <hagen@jauu.net>
4 # Licensed under the terms of the GNU GPL License version 2
6 # Usage:
8 # perf record -e sched:sched_switch -a -- sleep 10
9 # perf script report task-analyzer
12 from __future__ import print_function
13 import sys
14 import os
15 import string
16 import argparse
17 import decimal
20 sys.path.append(
21 os.environ["PERF_EXEC_PATH"] + "/scripts/python/Perf-Trace-Util/lib/Perf/Trace"
23 from perf_trace_context import *
24 from Core import *
26 # Definition of possible ASCII color codes
27 _COLORS = {
28 "grey": "\033[90m",
29 "red": "\033[91m",
30 "green": "\033[92m",
31 "yellow": "\033[93m",
32 "blue": "\033[94m",
33 "violet": "\033[95m",
34 "reset": "\033[0m",
37 # Columns will have a static size to align everything properly
38 # Support of 116 days of active update with nano precision
39 LEN_SWITCHED_IN = len("9999999.999999999") # 17
40 LEN_SWITCHED_OUT = len("9999999.999999999") # 17
41 LEN_CPU = len("000")
42 LEN_PID = len("maxvalue") # 8
43 LEN_TID = len("maxvalue") # 8
44 LEN_COMM = len("max-comms-length") # 16
45 LEN_RUNTIME = len("999999.999") # 10
46 # Support of 3.45 hours of timespans
47 LEN_OUT_IN = len("99999999999.999") # 15
48 LEN_OUT_OUT = len("99999999999.999") # 15
49 LEN_IN_IN = len("99999999999.999") # 15
50 LEN_IN_OUT = len("99999999999.999") # 15
53 # py2/py3 compatibility layer, see PEP469
54 try:
55 dict.iteritems
56 except AttributeError:
57 # py3
58 def itervalues(d):
59 return iter(d.values())
61 def iteritems(d):
62 return iter(d.items())
64 else:
65 # py2
66 def itervalues(d):
67 return d.itervalues()
69 def iteritems(d):
70 return d.iteritems()
73 def _check_color():
74 global _COLORS
75 """user enforced no-color or if stdout is no tty we disable colors"""
76 if sys.stdout.isatty() and args.stdio_color != "never":
77 return
78 _COLORS = {
79 "grey": "",
80 "red": "",
81 "green": "",
82 "yellow": "",
83 "blue": "",
84 "violet": "",
85 "reset": "",
89 def _parse_args():
90 global args
91 parser = argparse.ArgumentParser(description="Analyze tasks behavior")
92 parser.add_argument(
93 "--time-limit",
94 default=[],
95 help=
96 "print tasks only in time[s] window e.g"
97 " --time-limit 123.111:789.222(print all between 123.111 and 789.222)"
98 " --time-limit 123: (print all from 123)"
99 " --time-limit :456 (print all until incl. 456)",
101 parser.add_argument(
102 "--summary", action="store_true", help="print addtional runtime information"
104 parser.add_argument(
105 "--summary-only", action="store_true", help="print only summary without traces"
107 parser.add_argument(
108 "--summary-extended",
109 action="store_true",
110 help="print the summary with additional information of max inter task times"
111 " relative to the prev task",
113 parser.add_argument(
114 "--ns", action="store_true", help="show timestamps in nanoseconds"
116 parser.add_argument(
117 "--ms", action="store_true", help="show timestamps in milliseconds"
119 parser.add_argument(
120 "--extended-times",
121 action="store_true",
122 help="Show the elapsed times between schedule in/schedule out"
123 " of this task and the schedule in/schedule out of previous occurrence"
124 " of the same task",
126 parser.add_argument(
127 "--filter-tasks",
128 default=[],
129 help="filter out unneeded tasks by tid, pid or processname."
130 " E.g --filter-task 1337,/sbin/init ",
132 parser.add_argument(
133 "--limit-to-tasks",
134 default=[],
135 help="limit output to selected task by tid, pid, processname."
136 " E.g --limit-to-tasks 1337,/sbin/init",
138 parser.add_argument(
139 "--highlight-tasks",
140 default="",
141 help="colorize special tasks by their pid/tid/comm."
142 " E.g. --highlight-tasks 1:red,mutt:yellow"
143 " Colors available: red,grey,yellow,blue,violet,green",
145 parser.add_argument(
146 "--rename-comms-by-tids",
147 default="",
148 help="rename task names by using tid (<tid>:<newname>,<tid>:<newname>)"
149 " This option is handy for inexpressive processnames like python interpreted"
150 " process. E.g --rename 1337:my-python-app",
152 parser.add_argument(
153 "--stdio-color",
154 default="auto",
155 choices=["always", "never", "auto"],
156 help="always, never or auto, allowing configuring color output"
157 " via the command line",
159 parser.add_argument(
160 "--csv",
161 default="",
162 help="Write trace to file selected by user. Options, like --ns or --extended"
163 "-times are used.",
165 parser.add_argument(
166 "--csv-summary",
167 default="",
168 help="Write summary to file selected by user. Options, like --ns or"
169 " --summary-extended are used.",
171 args = parser.parse_args()
172 args.tid_renames = dict()
174 _argument_filter_sanity_check()
175 _argument_prepare_check()
178 def time_uniter(unit):
179 picker = {
180 "s": 1,
181 "ms": 1e3,
182 "us": 1e6,
183 "ns": 1e9,
185 return picker[unit]
188 def _init_db():
189 global db
190 db = dict()
191 db["running"] = dict()
192 db["cpu"] = dict()
193 db["tid"] = dict()
194 db["global"] = []
195 if args.summary or args.summary_extended or args.summary_only:
196 db["task_info"] = dict()
197 db["runtime_info"] = dict()
198 # min values for summary depending on the header
199 db["task_info"]["pid"] = len("PID")
200 db["task_info"]["tid"] = len("TID")
201 db["task_info"]["comm"] = len("Comm")
202 db["runtime_info"]["runs"] = len("Runs")
203 db["runtime_info"]["acc"] = len("Accumulated")
204 db["runtime_info"]["max"] = len("Max")
205 db["runtime_info"]["max_at"] = len("Max At")
206 db["runtime_info"]["min"] = len("Min")
207 db["runtime_info"]["mean"] = len("Mean")
208 db["runtime_info"]["median"] = len("Median")
209 if args.summary_extended:
210 db["inter_times"] = dict()
211 db["inter_times"]["out_in"] = len("Out-In")
212 db["inter_times"]["inter_at"] = len("At")
213 db["inter_times"]["out_out"] = len("Out-Out")
214 db["inter_times"]["in_in"] = len("In-In")
215 db["inter_times"]["in_out"] = len("In-Out")
218 def _median(numbers):
219 """phython3 hat statistics module - we have nothing"""
220 n = len(numbers)
221 index = n // 2
222 if n % 2:
223 return sorted(numbers)[index]
224 return sum(sorted(numbers)[index - 1 : index + 1]) / 2
227 def _mean(numbers):
228 return sum(numbers) / len(numbers)
231 class Timespans(object):
233 The elapsed time between two occurrences of the same task is being tracked with the
234 help of this class. There are 4 of those Timespans Out-Out, In-Out, Out-In and
235 In-In.
236 The first half of the name signals the first time point of the
237 first task. The second half of the name represents the second
238 timepoint of the second task.
241 def __init__(self):
242 self._last_start = None
243 self._last_finish = None
244 self.out_out = -1
245 self.in_out = -1
246 self.out_in = -1
247 self.in_in = -1
248 if args.summary_extended:
249 self._time_in = -1
250 self.max_out_in = -1
251 self.max_at = -1
252 self.max_in_out = -1
253 self.max_in_in = -1
254 self.max_out_out = -1
256 def feed(self, task):
258 Called for every recorded trace event to find process pair and calculate the
259 task timespans. Chronological ordering, feed does not do reordering
261 if not self._last_finish:
262 self._last_start = task.time_in(time_unit)
263 self._last_finish = task.time_out(time_unit)
264 return
265 self._time_in = task.time_in()
266 time_in = task.time_in(time_unit)
267 time_out = task.time_out(time_unit)
268 self.in_in = time_in - self._last_start
269 self.out_in = time_in - self._last_finish
270 self.in_out = time_out - self._last_start
271 self.out_out = time_out - self._last_finish
272 if args.summary_extended:
273 self._update_max_entries()
274 self._last_finish = task.time_out(time_unit)
275 self._last_start = task.time_in(time_unit)
277 def _update_max_entries(self):
278 if self.in_in > self.max_in_in:
279 self.max_in_in = self.in_in
280 if self.out_out > self.max_out_out:
281 self.max_out_out = self.out_out
282 if self.in_out > self.max_in_out:
283 self.max_in_out = self.in_out
284 if self.out_in > self.max_out_in:
285 self.max_out_in = self.out_in
286 self.max_at = self._time_in
290 class Summary(object):
292 Primary instance for calculating the summary output. Processes the whole trace to
293 find and memorize relevant data such as mean, max et cetera. This instance handles
294 dynamic alignment aspects for summary output.
297 def __init__(self):
298 self._body = []
300 class AlignmentHelper:
302 Used to calculated the alignment for the output of the summary.
304 def __init__(self, pid, tid, comm, runs, acc, mean,
305 median, min, max, max_at):
306 self.pid = pid
307 self.tid = tid
308 self.comm = comm
309 self.runs = runs
310 self.acc = acc
311 self.mean = mean
312 self.median = median
313 self.min = min
314 self.max = max
315 self.max_at = max_at
316 if args.summary_extended:
317 self.out_in = None
318 self.inter_at = None
319 self.out_out = None
320 self.in_in = None
321 self.in_out = None
323 def _print_header(self):
325 Output is trimmed in _format_stats thus additional adjustment in the header
326 is needed, depending on the choice of timeunit. The adjustment corresponds
327 to the amount of column titles being adjusted in _column_titles.
329 decimal_precision = 6 if not args.ns else 9
330 fmt = " {{:^{}}}".format(sum(db["task_info"].values()))
331 fmt += " {{:^{}}}".format(
332 sum(db["runtime_info"].values()) - 2 * decimal_precision
334 _header = ("Task Information", "Runtime Information")
336 if args.summary_extended:
337 fmt += " {{:^{}}}".format(
338 sum(db["inter_times"].values()) - 4 * decimal_precision
340 _header += ("Max Inter Task Times",)
341 fd_sum.write(fmt.format(*_header) + "\n")
343 def _column_titles(self):
345 Cells are being processed and displayed in different way so an alignment adjust
346 is implemented depeding on the choice of the timeunit. The positions of the max
347 values are being displayed in grey. Thus in their format two additional {},
348 are placed for color set and reset.
350 separator, fix_csv_align = _prepare_fmt_sep()
351 decimal_precision, time_precision = _prepare_fmt_precision()
352 fmt = "{{:>{}}}".format(db["task_info"]["pid"] * fix_csv_align)
353 fmt += "{}{{:>{}}}".format(separator, db["task_info"]["tid"] * fix_csv_align)
354 fmt += "{}{{:>{}}}".format(separator, db["task_info"]["comm"] * fix_csv_align)
355 fmt += "{}{{:>{}}}".format(separator, db["runtime_info"]["runs"] * fix_csv_align)
356 fmt += "{}{{:>{}}}".format(separator, db["runtime_info"]["acc"] * fix_csv_align)
357 fmt += "{}{{:>{}}}".format(separator, db["runtime_info"]["mean"] * fix_csv_align)
358 fmt += "{}{{:>{}}}".format(
359 separator, db["runtime_info"]["median"] * fix_csv_align
361 fmt += "{}{{:>{}}}".format(
362 separator, (db["runtime_info"]["min"] - decimal_precision) * fix_csv_align
364 fmt += "{}{{:>{}}}".format(
365 separator, (db["runtime_info"]["max"] - decimal_precision) * fix_csv_align
367 fmt += "{}{{}}{{:>{}}}{{}}".format(
368 separator, (db["runtime_info"]["max_at"] - time_precision) * fix_csv_align
371 column_titles = ("PID", "TID", "Comm")
372 column_titles += ("Runs", "Accumulated", "Mean", "Median", "Min", "Max")
373 column_titles += (_COLORS["grey"], "Max At", _COLORS["reset"])
375 if args.summary_extended:
376 fmt += "{}{{:>{}}}".format(
377 separator,
378 (db["inter_times"]["out_in"] - decimal_precision) * fix_csv_align
380 fmt += "{}{{}}{{:>{}}}{{}}".format(
381 separator,
382 (db["inter_times"]["inter_at"] - time_precision) * fix_csv_align
384 fmt += "{}{{:>{}}}".format(
385 separator,
386 (db["inter_times"]["out_out"] - decimal_precision) * fix_csv_align
388 fmt += "{}{{:>{}}}".format(
389 separator,
390 (db["inter_times"]["in_in"] - decimal_precision) * fix_csv_align
392 fmt += "{}{{:>{}}}".format(
393 separator,
394 (db["inter_times"]["in_out"] - decimal_precision) * fix_csv_align
397 column_titles += ("Out-In", _COLORS["grey"], "Max At", _COLORS["reset"],
398 "Out-Out", "In-In", "In-Out")
400 fd_sum.write(fmt.format(*column_titles) + "\n")
403 def _task_stats(self):
404 """calculates the stats of every task and constructs the printable summary"""
405 for tid in sorted(db["tid"]):
406 color_one_sample = _COLORS["grey"]
407 color_reset = _COLORS["reset"]
408 no_executed = 0
409 runtimes = []
410 time_in = []
411 timespans = Timespans()
412 for task in db["tid"][tid]:
413 pid = task.pid
414 comm = task.comm
415 no_executed += 1
416 runtimes.append(task.runtime(time_unit))
417 time_in.append(task.time_in())
418 timespans.feed(task)
419 if len(runtimes) > 1:
420 color_one_sample = ""
421 color_reset = ""
422 time_max = max(runtimes)
423 time_min = min(runtimes)
424 max_at = time_in[runtimes.index(max(runtimes))]
426 # The size of the decimal after sum,mean and median varies, thus we cut
427 # the decimal number, by rounding it. It has no impact on the output,
428 # because we have a precision of the decimal points at the output.
429 time_sum = round(sum(runtimes), 3)
430 time_mean = round(_mean(runtimes), 3)
431 time_median = round(_median(runtimes), 3)
433 align_helper = self.AlignmentHelper(pid, tid, comm, no_executed, time_sum,
434 time_mean, time_median, time_min, time_max, max_at)
435 self._body.append([pid, tid, comm, no_executed, time_sum, color_one_sample,
436 time_mean, time_median, time_min, time_max,
437 _COLORS["grey"], max_at, _COLORS["reset"], color_reset])
438 if args.summary_extended:
439 self._body[-1].extend([timespans.max_out_in,
440 _COLORS["grey"], timespans.max_at,
441 _COLORS["reset"], timespans.max_out_out,
442 timespans.max_in_in,
443 timespans.max_in_out])
444 align_helper.out_in = timespans.max_out_in
445 align_helper.inter_at = timespans.max_at
446 align_helper.out_out = timespans.max_out_out
447 align_helper.in_in = timespans.max_in_in
448 align_helper.in_out = timespans.max_in_out
449 self._calc_alignments_summary(align_helper)
451 def _format_stats(self):
452 separator, fix_csv_align = _prepare_fmt_sep()
453 decimal_precision, time_precision = _prepare_fmt_precision()
454 len_pid = db["task_info"]["pid"] * fix_csv_align
455 len_tid = db["task_info"]["tid"] * fix_csv_align
456 len_comm = db["task_info"]["comm"] * fix_csv_align
457 len_runs = db["runtime_info"]["runs"] * fix_csv_align
458 len_acc = db["runtime_info"]["acc"] * fix_csv_align
459 len_mean = db["runtime_info"]["mean"] * fix_csv_align
460 len_median = db["runtime_info"]["median"] * fix_csv_align
461 len_min = (db["runtime_info"]["min"] - decimal_precision) * fix_csv_align
462 len_max = (db["runtime_info"]["max"] - decimal_precision) * fix_csv_align
463 len_max_at = (db["runtime_info"]["max_at"] - time_precision) * fix_csv_align
464 if args.summary_extended:
465 len_out_in = (
466 db["inter_times"]["out_in"] - decimal_precision
467 ) * fix_csv_align
468 len_inter_at = (
469 db["inter_times"]["inter_at"] - time_precision
470 ) * fix_csv_align
471 len_out_out = (
472 db["inter_times"]["out_out"] - decimal_precision
473 ) * fix_csv_align
474 len_in_in = (db["inter_times"]["in_in"] - decimal_precision) * fix_csv_align
475 len_in_out = (
476 db["inter_times"]["in_out"] - decimal_precision
477 ) * fix_csv_align
479 fmt = "{{:{}d}}".format(len_pid)
480 fmt += "{}{{:{}d}}".format(separator, len_tid)
481 fmt += "{}{{:>{}}}".format(separator, len_comm)
482 fmt += "{}{{:{}d}}".format(separator, len_runs)
483 fmt += "{}{{:{}.{}f}}".format(separator, len_acc, time_precision)
484 fmt += "{}{{}}{{:{}.{}f}}".format(separator, len_mean, time_precision)
485 fmt += "{}{{:{}.{}f}}".format(separator, len_median, time_precision)
486 fmt += "{}{{:{}.{}f}}".format(separator, len_min, time_precision)
487 fmt += "{}{{:{}.{}f}}".format(separator, len_max, time_precision)
488 fmt += "{}{{}}{{:{}.{}f}}{{}}{{}}".format(
489 separator, len_max_at, decimal_precision
491 if args.summary_extended:
492 fmt += "{}{{:{}.{}f}}".format(separator, len_out_in, time_precision)
493 fmt += "{}{{}}{{:{}.{}f}}{{}}".format(
494 separator, len_inter_at, decimal_precision
496 fmt += "{}{{:{}.{}f}}".format(separator, len_out_out, time_precision)
497 fmt += "{}{{:{}.{}f}}".format(separator, len_in_in, time_precision)
498 fmt += "{}{{:{}.{}f}}".format(separator, len_in_out, time_precision)
499 return fmt
502 def _calc_alignments_summary(self, align_helper):
503 # Length is being cut in 3 groups so that further addition is easier to handle.
504 # The length of every argument from the alignment helper is being checked if it
505 # is longer than the longest until now. In that case the length is being saved.
506 for key in db["task_info"]:
507 if len(str(getattr(align_helper, key))) > db["task_info"][key]:
508 db["task_info"][key] = len(str(getattr(align_helper, key)))
509 for key in db["runtime_info"]:
510 if len(str(getattr(align_helper, key))) > db["runtime_info"][key]:
511 db["runtime_info"][key] = len(str(getattr(align_helper, key)))
512 if args.summary_extended:
513 for key in db["inter_times"]:
514 if len(str(getattr(align_helper, key))) > db["inter_times"][key]:
515 db["inter_times"][key] = len(str(getattr(align_helper, key)))
518 def print(self):
519 self._task_stats()
520 fmt = self._format_stats()
522 if not args.csv_summary:
523 print("\nSummary")
524 self._print_header()
525 self._column_titles()
526 for i in range(len(self._body)):
527 fd_sum.write(fmt.format(*tuple(self._body[i])) + "\n")
531 class Task(object):
532 """ The class is used to handle the information of a given task."""
534 def __init__(self, id, tid, cpu, comm):
535 self.id = id
536 self.tid = tid
537 self.cpu = cpu
538 self.comm = comm
539 self.pid = None
540 self._time_in = None
541 self._time_out = None
543 def schedule_in_at(self, time):
544 """set the time where the task was scheduled in"""
545 self._time_in = time
547 def schedule_out_at(self, time):
548 """set the time where the task was scheduled out"""
549 self._time_out = time
551 def time_out(self, unit="s"):
552 """return time where a given task was scheduled out"""
553 factor = time_uniter(unit)
554 return self._time_out * decimal.Decimal(factor)
556 def time_in(self, unit="s"):
557 """return time where a given task was scheduled in"""
558 factor = time_uniter(unit)
559 return self._time_in * decimal.Decimal(factor)
561 def runtime(self, unit="us"):
562 factor = time_uniter(unit)
563 return (self._time_out - self._time_in) * decimal.Decimal(factor)
565 def update_pid(self, pid):
566 self.pid = pid
569 def _task_id(pid, cpu):
570 """returns a "unique-enough" identifier, please do not change"""
571 return "{}-{}".format(pid, cpu)
574 def _filter_non_printable(unfiltered):
575 """comm names may contain loony chars like '\x00000'"""
576 filtered = ""
577 for char in unfiltered:
578 if char not in string.printable:
579 continue
580 filtered += char
581 return filtered
584 def _fmt_header():
585 separator, fix_csv_align = _prepare_fmt_sep()
586 fmt = "{{:>{}}}".format(LEN_SWITCHED_IN*fix_csv_align)
587 fmt += "{}{{:>{}}}".format(separator, LEN_SWITCHED_OUT*fix_csv_align)
588 fmt += "{}{{:>{}}}".format(separator, LEN_CPU*fix_csv_align)
589 fmt += "{}{{:>{}}}".format(separator, LEN_PID*fix_csv_align)
590 fmt += "{}{{:>{}}}".format(separator, LEN_TID*fix_csv_align)
591 fmt += "{}{{:>{}}}".format(separator, LEN_COMM*fix_csv_align)
592 fmt += "{}{{:>{}}}".format(separator, LEN_RUNTIME*fix_csv_align)
593 fmt += "{}{{:>{}}}".format(separator, LEN_OUT_IN*fix_csv_align)
594 if args.extended_times:
595 fmt += "{}{{:>{}}}".format(separator, LEN_OUT_OUT*fix_csv_align)
596 fmt += "{}{{:>{}}}".format(separator, LEN_IN_IN*fix_csv_align)
597 fmt += "{}{{:>{}}}".format(separator, LEN_IN_OUT*fix_csv_align)
598 return fmt
601 def _fmt_body():
602 separator, fix_csv_align = _prepare_fmt_sep()
603 decimal_precision, time_precision = _prepare_fmt_precision()
604 fmt = "{{}}{{:{}.{}f}}".format(LEN_SWITCHED_IN*fix_csv_align, decimal_precision)
605 fmt += "{}{{:{}.{}f}}".format(
606 separator, LEN_SWITCHED_OUT*fix_csv_align, decimal_precision
608 fmt += "{}{{:{}d}}".format(separator, LEN_CPU*fix_csv_align)
609 fmt += "{}{{:{}d}}".format(separator, LEN_PID*fix_csv_align)
610 fmt += "{}{{}}{{:{}d}}{{}}".format(separator, LEN_TID*fix_csv_align)
611 fmt += "{}{{}}{{:>{}}}".format(separator, LEN_COMM*fix_csv_align)
612 fmt += "{}{{:{}.{}f}}".format(separator, LEN_RUNTIME*fix_csv_align, time_precision)
613 if args.extended_times:
614 fmt += "{}{{:{}.{}f}}".format(separator, LEN_OUT_IN*fix_csv_align, time_precision)
615 fmt += "{}{{:{}.{}f}}".format(separator, LEN_OUT_OUT*fix_csv_align, time_precision)
616 fmt += "{}{{:{}.{}f}}".format(separator, LEN_IN_IN*fix_csv_align, time_precision)
617 fmt += "{}{{:{}.{}f}}{{}}".format(
618 separator, LEN_IN_OUT*fix_csv_align, time_precision
620 else:
621 fmt += "{}{{:{}.{}f}}{{}}".format(
622 separator, LEN_OUT_IN*fix_csv_align, time_precision
624 return fmt
627 def _print_header():
628 fmt = _fmt_header()
629 header = ("Switched-In", "Switched-Out", "CPU", "PID", "TID", "Comm", "Runtime",
630 "Time Out-In")
631 if args.extended_times:
632 header += ("Time Out-Out", "Time In-In", "Time In-Out")
633 fd_task.write(fmt.format(*header) + "\n")
637 def _print_task_finish(task):
638 """calculating every entry of a row and printing it immediately"""
639 c_row_set = ""
640 c_row_reset = ""
641 out_in = -1
642 out_out = -1
643 in_in = -1
644 in_out = -1
645 fmt = _fmt_body()
646 # depending on user provided highlight option we change the color
647 # for particular tasks
648 if str(task.tid) in args.highlight_tasks_map:
649 c_row_set = _COLORS[args.highlight_tasks_map[str(task.tid)]]
650 c_row_reset = _COLORS["reset"]
651 if task.comm in args.highlight_tasks_map:
652 c_row_set = _COLORS[args.highlight_tasks_map[task.comm]]
653 c_row_reset = _COLORS["reset"]
654 # grey-out entries if PID == TID, they
655 # are identical, no threaded model so the
656 # thread id (tid) do not matter
657 c_tid_set = ""
658 c_tid_reset = ""
659 if task.pid == task.tid:
660 c_tid_set = _COLORS["grey"]
661 c_tid_reset = _COLORS["reset"]
662 if task.tid in db["tid"]:
663 # get last task of tid
664 last_tid_task = db["tid"][task.tid][-1]
665 # feed the timespan calculate, last in tid db
666 # and second the current one
667 timespan_gap_tid = Timespans()
668 timespan_gap_tid.feed(last_tid_task)
669 timespan_gap_tid.feed(task)
670 out_in = timespan_gap_tid.out_in
671 out_out = timespan_gap_tid.out_out
672 in_in = timespan_gap_tid.in_in
673 in_out = timespan_gap_tid.in_out
676 if args.extended_times:
677 line_out = fmt.format(c_row_set, task.time_in(), task.time_out(), task.cpu,
678 task.pid, c_tid_set, task.tid, c_tid_reset, c_row_set, task.comm,
679 task.runtime(time_unit), out_in, out_out, in_in, in_out,
680 c_row_reset) + "\n"
681 else:
682 line_out = fmt.format(c_row_set, task.time_in(), task.time_out(), task.cpu,
683 task.pid, c_tid_set, task.tid, c_tid_reset, c_row_set, task.comm,
684 task.runtime(time_unit), out_in, c_row_reset) + "\n"
685 try:
686 fd_task.write(line_out)
687 except(IOError):
688 # don't mangle the output if user SIGINT this script
689 sys.exit()
691 def _record_cleanup(_list):
693 no need to store more then one element if --summarize
694 is not enabled
696 if not args.summary and len(_list) > 1:
697 _list = _list[len(_list) - 1 :]
700 def _record_by_tid(task):
701 tid = task.tid
702 if tid not in db["tid"]:
703 db["tid"][tid] = []
704 db["tid"][tid].append(task)
705 _record_cleanup(db["tid"][tid])
708 def _record_by_cpu(task):
709 cpu = task.cpu
710 if cpu not in db["cpu"]:
711 db["cpu"][cpu] = []
712 db["cpu"][cpu].append(task)
713 _record_cleanup(db["cpu"][cpu])
716 def _record_global(task):
717 """record all executed task, ordered by finish chronological"""
718 db["global"].append(task)
719 _record_cleanup(db["global"])
722 def _handle_task_finish(tid, cpu, time, perf_sample_dict):
723 if tid == 0:
724 return
725 _id = _task_id(tid, cpu)
726 if _id not in db["running"]:
727 # may happen, if we missed the switch to
728 # event. Seen in combination with --exclude-perf
729 # where the start is filtered out, but not the
730 # switched in. Probably a bug in exclude-perf
731 # option.
732 return
733 task = db["running"][_id]
734 task.schedule_out_at(time)
736 # record tid, during schedule in the tid
737 # is not available, update now
738 pid = int(perf_sample_dict["sample"]["pid"])
740 task.update_pid(pid)
741 del db["running"][_id]
743 # print only tasks which are not being filtered and no print of trace
744 # for summary only, but record every task.
745 if not _limit_filtered(tid, pid, task.comm) and not args.summary_only:
746 _print_task_finish(task)
747 _record_by_tid(task)
748 _record_by_cpu(task)
749 _record_global(task)
752 def _handle_task_start(tid, cpu, comm, time):
753 if tid == 0:
754 return
755 if tid in args.tid_renames:
756 comm = args.tid_renames[tid]
757 _id = _task_id(tid, cpu)
758 if _id in db["running"]:
759 # handle corner cases where already running tasks
760 # are switched-to again - saw this via --exclude-perf
761 # recorded traces. We simple ignore this "second start"
762 # event.
763 return
764 assert _id not in db["running"]
765 task = Task(_id, tid, cpu, comm)
766 task.schedule_in_at(time)
767 db["running"][_id] = task
770 def _time_to_internal(time_ns):
772 To prevent float rounding errors we use Decimal internally
774 return decimal.Decimal(time_ns) / decimal.Decimal(1e9)
777 def _limit_filtered(tid, pid, comm):
778 if args.filter_tasks:
779 if str(tid) in args.filter_tasks or comm in args.filter_tasks:
780 return True
781 else:
782 return False
783 if args.limit_to_tasks:
784 if str(tid) in args.limit_to_tasks or comm in args.limit_to_tasks:
785 return False
786 else:
787 return True
790 def _argument_filter_sanity_check():
791 if args.limit_to_tasks and args.filter_tasks:
792 sys.exit("Error: Filter and Limit at the same time active.")
793 if args.extended_times and args.summary_only:
794 sys.exit("Error: Summary only and extended times active.")
795 if args.time_limit and ":" not in args.time_limit:
796 sys.exit(
797 "Error: No bound set for time limit. Please set bound by ':' e.g :123."
799 if args.time_limit and (args.summary or args.summary_only or args.summary_extended):
800 sys.exit("Error: Cannot set time limit and print summary")
801 if args.csv_summary:
802 args.summary = True
803 if args.csv == args.csv_summary:
804 sys.exit("Error: Chosen files for csv and csv summary are the same")
805 if args.csv and (args.summary_extended or args.summary) and not args.csv_summary:
806 sys.exit("Error: No file chosen to write summary to. Choose with --csv-summary "
807 "<file>")
808 if args.csv and args.summary_only:
809 sys.exit("Error: --csv chosen and --summary-only. Standard task would not be"
810 "written to csv file.")
812 def _argument_prepare_check():
813 global time_unit, fd_task, fd_sum
814 if args.filter_tasks:
815 args.filter_tasks = args.filter_tasks.split(",")
816 if args.limit_to_tasks:
817 args.limit_to_tasks = args.limit_to_tasks.split(",")
818 if args.time_limit:
819 args.time_limit = args.time_limit.split(":")
820 for rename_tuple in args.rename_comms_by_tids.split(","):
821 tid_name = rename_tuple.split(":")
822 if len(tid_name) != 2:
823 continue
824 args.tid_renames[int(tid_name[0])] = tid_name[1]
825 args.highlight_tasks_map = dict()
826 for highlight_tasks_tuple in args.highlight_tasks.split(","):
827 tasks_color_map = highlight_tasks_tuple.split(":")
828 # default highlight color to red if no color set by user
829 if len(tasks_color_map) == 1:
830 tasks_color_map.append("red")
831 if args.highlight_tasks and tasks_color_map[1].lower() not in _COLORS:
832 sys.exit(
833 "Error: Color not defined, please choose from grey,red,green,yellow,blue,"
834 "violet"
836 if len(tasks_color_map) != 2:
837 continue
838 args.highlight_tasks_map[tasks_color_map[0]] = tasks_color_map[1]
839 time_unit = "us"
840 if args.ns:
841 time_unit = "ns"
842 elif args.ms:
843 time_unit = "ms"
846 fd_task = sys.stdout
847 if args.csv:
848 args.stdio_color = "never"
849 fd_task = open(args.csv, "w")
850 print("generating csv at",args.csv,)
852 fd_sum = sys.stdout
853 if args.csv_summary:
854 args.stdio_color = "never"
855 fd_sum = open(args.csv_summary, "w")
856 print("generating csv summary at",args.csv_summary)
857 if not args.csv:
858 args.summary_only = True
861 def _is_within_timelimit(time):
863 Check if a time limit was given by parameter, if so ignore the rest. If not,
864 process the recorded trace in its entirety.
866 if not args.time_limit:
867 return True
868 lower_time_limit = args.time_limit[0]
869 upper_time_limit = args.time_limit[1]
870 # check for upper limit
871 if upper_time_limit == "":
872 if time >= decimal.Decimal(lower_time_limit):
873 return True
874 # check for lower limit
875 if lower_time_limit == "":
876 if time <= decimal.Decimal(upper_time_limit):
877 return True
878 # quit if time exceeds upper limit. Good for big datasets
879 else:
880 quit()
881 if lower_time_limit != "" and upper_time_limit != "":
882 if (time >= decimal.Decimal(lower_time_limit) and
883 time <= decimal.Decimal(upper_time_limit)):
884 return True
885 # quit if time exceeds upper limit. Good for big datasets
886 elif time > decimal.Decimal(upper_time_limit):
887 quit()
889 def _prepare_fmt_precision():
890 decimal_precision = 6
891 time_precision = 3
892 if args.ns:
893 decimal_precision = 9
894 time_precision = 0
895 return decimal_precision, time_precision
897 def _prepare_fmt_sep():
898 separator = " "
899 fix_csv_align = 1
900 if args.csv or args.csv_summary:
901 separator = ";"
902 fix_csv_align = 0
903 return separator, fix_csv_align
905 def trace_unhandled(event_name, context, event_fields_dict, perf_sample_dict):
906 pass
909 def trace_begin():
910 _parse_args()
911 _check_color()
912 _init_db()
913 if not args.summary_only:
914 _print_header()
916 def trace_end():
917 if args.summary or args.summary_extended or args.summary_only:
918 Summary().print()
920 def sched__sched_switch(event_name, context, common_cpu, common_secs, common_nsecs,
921 common_pid, common_comm, common_callchain, prev_comm,
922 prev_pid, prev_prio, prev_state, next_comm, next_pid,
923 next_prio, perf_sample_dict):
924 # ignore common_secs & common_nsecs cause we need
925 # high res timestamp anyway, using the raw value is
926 # faster
927 time = _time_to_internal(perf_sample_dict["sample"]["time"])
928 if not _is_within_timelimit(time):
929 # user specific --time-limit a:b set
930 return
932 next_comm = _filter_non_printable(next_comm)
933 _handle_task_finish(prev_pid, common_cpu, time, perf_sample_dict)
934 _handle_task_start(next_pid, common_cpu, next_comm, time)