diff options
author | Aryaman Gupta <aryaman.gupta@windriver.com> | 2022-06-22 15:21:04 -0400 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2022-06-29 16:16:56 +0100 |
commit | 45f1e9d95320a45d7c38a0db34a643272e3add23 (patch) | |
tree | 1b29749b4d8ddeecbbf1bd7025d3ba58fc057135 /scripts | |
parent | ac162116b365900925bd753ae264847f751ce799 (diff) | |
download | poky-45f1e9d95320a45d7c38a0db34a643272e3add23.tar.gz |
pybootchartgui: render cpu and io pressure
Add two new, separate charts showing the avg10 and delta
total pressure over time for the CPU and IO resources. The height of
the avg10 data in each chart represents the percentage of time "some"
task was delayed over the specific resource during the last 10
seconds of the build. The height of the delta total data in each chart
represents the total time "some" task was delayed since the last sample
was collected. If the reduced_proc_pressure data is not present in the
buildstats log, then the new charts are not shown at all rather than
being present but unpopulated.
Note that the delta total graphs may appear "spikey",
oscillating from high values to low. This behaviour is fixed in a
subsequent commit.
(From OE-Core rev: fb9ff46dc3059cb3f4c8df8e4654184c3eab1571)
Signed-off-by: Aryaman Gupta <aryaman.gupta@windriver.com>
Signed-off-by: Randy MacLeod <randy.macleod@windriver.com>
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/pybootchartgui/pybootchartgui/draw.py | 77 | ||||
-rw-r--r-- | scripts/pybootchartgui/pybootchartgui/parsing.py | 28 | ||||
-rw-r--r-- | scripts/pybootchartgui/pybootchartgui/samples.py | 17 |
3 files changed, 122 insertions, 0 deletions
diff --git a/scripts/pybootchartgui/pybootchartgui/draw.py b/scripts/pybootchartgui/pybootchartgui/draw.py index fc708b55c3..a13df3a3fa 100644 --- a/scripts/pybootchartgui/pybootchartgui/draw.py +++ b/scripts/pybootchartgui/pybootchartgui/draw.py | |||
@@ -80,6 +80,18 @@ MEM_BUFFERS_COLOR = (0.4, 0.4, 0.4, 0.3) | |||
80 | # Swap color | 80 | # Swap color |
81 | MEM_SWAP_COLOR = DISK_TPUT_COLOR | 81 | MEM_SWAP_COLOR = DISK_TPUT_COLOR |
82 | 82 | ||
83 | # avg10 CPU pressure color | ||
84 | CPU_PRESSURE_AVG10_COLOR = (0.0, 0.0, 0.0, 1.0) | ||
85 | # delta total CPU pressure color | ||
86 | CPU_PRESSURE_TOTAL_COLOR = CPU_COLOR | ||
87 | # avg10 IO pressure color | ||
88 | IO_PRESSURE_AVG10_COLOR = (0.0, 0.0, 0.0, 1.0) | ||
89 | # delta total IO pressure color | ||
90 | IO_PRESSURE_TOTAL_COLOR = IO_COLOR | ||
91 | |||
92 | |||
93 | |||
94 | |||
83 | # Process border color. | 95 | # Process border color. |
84 | PROC_BORDER_COLOR = (0.71, 0.71, 0.71, 1.0) | 96 | PROC_BORDER_COLOR = (0.71, 0.71, 0.71, 1.0) |
85 | # Waiting process color. | 97 | # Waiting process color. |
@@ -415,6 +427,71 @@ def render_charts(ctx, options, clip, trace, curr_y, w, h, sec_w): | |||
415 | 427 | ||
416 | curr_y = curr_y + 30 + bar_h | 428 | curr_y = curr_y + 30 + bar_h |
417 | 429 | ||
430 | # render CPU pressure chart | ||
431 | if trace.cpu_pressure: | ||
432 | draw_legend_line(ctx, "avg10 CPU Pressure", CPU_PRESSURE_AVG10_COLOR, off_x, curr_y+20, leg_s) | ||
433 | draw_legend_box(ctx, "delta total CPU Pressure", CPU_PRESSURE_TOTAL_COLOR, off_x + 140, curr_y+20, leg_s) | ||
434 | |||
435 | # render delta total cpu | ||
436 | chart_rect = (off_x, curr_y+30, w, bar_h) | ||
437 | if clip_visible (clip, chart_rect): | ||
438 | draw_box_ticks (ctx, chart_rect, sec_w) | ||
439 | draw_annotations (ctx, proc_tree, trace.times, chart_rect) | ||
440 | draw_chart (ctx, CPU_PRESSURE_TOTAL_COLOR, True, chart_rect, \ | ||
441 | [(sample.time, sample.deltaTotal) for sample in trace.cpu_pressure], \ | ||
442 | proc_tree, None) | ||
443 | |||
444 | # render avg10 cpu | ||
445 | max_sample = max (trace.cpu_pressure, key = lambda s: s.avg10) | ||
446 | if clip_visible (clip, chart_rect): | ||
447 | draw_chart (ctx, CPU_PRESSURE_AVG10_COLOR, False, chart_rect, \ | ||
448 | [(sample.time, sample.avg10) for sample in trace.cpu_pressure], \ | ||
449 | proc_tree, None) | ||
450 | |||
451 | pos_x = off_x + ((max_sample.time - proc_tree.start_time) * w / proc_tree.duration) | ||
452 | |||
453 | shift_x, shift_y = -20, 20 | ||
454 | if (pos_x < off_x + 245): | ||
455 | shift_x, shift_y = 5, 40 | ||
456 | |||
457 | |||
458 | label = "%d%%" % (max_sample.avg10) | ||
459 | draw_text (ctx, label, CPU_PRESSURE_AVG10_COLOR, pos_x + shift_x, curr_y + shift_y) | ||
460 | |||
461 | curr_y = curr_y + 30 + bar_h | ||
462 | |||
463 | # render delta total io | ||
464 | if trace.io_pressure: | ||
465 | draw_legend_line(ctx, "avg10 I/O Pressure", IO_PRESSURE_AVG10_COLOR, off_x, curr_y+20, leg_s) | ||
466 | draw_legend_box(ctx, "delta total I/O Pressure", IO_PRESSURE_TOTAL_COLOR, off_x + 140, curr_y+20, leg_s) | ||
467 | |||
468 | # render avg10 io | ||
469 | chart_rect = (off_x, curr_y+30, w, bar_h) | ||
470 | if clip_visible (clip, chart_rect): | ||
471 | draw_box_ticks (ctx, chart_rect, sec_w) | ||
472 | draw_annotations (ctx, proc_tree, trace.times, chart_rect) | ||
473 | draw_chart (ctx, IO_PRESSURE_TOTAL_COLOR, True, chart_rect, \ | ||
474 | [(sample.time, sample.deltaTotal) for sample in trace.io_pressure], \ | ||
475 | proc_tree, None) | ||
476 | |||
477 | # render io pressure | ||
478 | max_sample = max (trace.io_pressure, key = lambda s: s.avg10) | ||
479 | if clip_visible (clip, chart_rect): | ||
480 | draw_chart (ctx, IO_PRESSURE_AVG10_COLOR, False, chart_rect, \ | ||
481 | [(sample.time, sample.avg10) for sample in trace.io_pressure], \ | ||
482 | proc_tree, None) | ||
483 | |||
484 | pos_x = off_x + ((max_sample.time - proc_tree.start_time) * w / proc_tree.duration) | ||
485 | |||
486 | shift_x, shift_y = -20, 20 | ||
487 | if (pos_x < off_x + 245): | ||
488 | shift_x, shift_y = 5, 40 | ||
489 | |||
490 | label = "%d%%" % (max_sample.avg10) | ||
491 | draw_text (ctx, label, IO_PRESSURE_AVG10_COLOR, pos_x + shift_x, curr_y + shift_y) | ||
492 | |||
493 | curr_y = curr_y + 30 + bar_h | ||
494 | |||
418 | # render disk space usage | 495 | # render disk space usage |
419 | # | 496 | # |
420 | # Draws the amount of disk space used on each volume relative to the | 497 | # Draws the amount of disk space used on each volume relative to the |
diff --git a/scripts/pybootchartgui/pybootchartgui/parsing.py b/scripts/pybootchartgui/pybootchartgui/parsing.py index b42dac6b88..004d6fb953 100644 --- a/scripts/pybootchartgui/pybootchartgui/parsing.py +++ b/scripts/pybootchartgui/pybootchartgui/parsing.py | |||
@@ -49,6 +49,8 @@ class Trace: | |||
49 | self.parent_map = None | 49 | self.parent_map = None |
50 | self.mem_stats = [] | 50 | self.mem_stats = [] |
51 | self.monitor_disk = None | 51 | self.monitor_disk = None |
52 | self.cpu_pressure = [] | ||
53 | self.io_pressure = [] | ||
52 | self.times = [] # Always empty, but expected by draw.py when drawing system charts. | 54 | self.times = [] # Always empty, but expected by draw.py when drawing system charts. |
53 | 55 | ||
54 | if len(paths): | 56 | if len(paths): |
@@ -554,6 +556,27 @@ def _parse_monitor_disk_log(file): | |||
554 | 556 | ||
555 | return disk_stats | 557 | return disk_stats |
556 | 558 | ||
559 | def _parse_pressure_logs(file, filename): | ||
560 | """ | ||
561 | Parse file for "some" pressure with 'avg10', 'avg60' 'avg300' and delta total values | ||
562 | (in that order) directly stored on one line for both CPU and IO, based on filename. | ||
563 | """ | ||
564 | pressure_stats = [] | ||
565 | if filename == "cpu.log": | ||
566 | SamplingClass = CPUPressureSample | ||
567 | else: | ||
568 | SamplingClass = IOPressureSample | ||
569 | for time, lines in _parse_timed_blocks(file): | ||
570 | for line in lines: | ||
571 | if not line: continue | ||
572 | tokens = line.split() | ||
573 | avg10 = float(tokens[0]) | ||
574 | avg60 = float(tokens[1]) | ||
575 | avg300 = float(tokens[2]) | ||
576 | delta = float(tokens[3]) | ||
577 | pressure_stats.append(SamplingClass(time, avg10, avg60, avg300, delta)) | ||
578 | |||
579 | return pressure_stats | ||
557 | 580 | ||
558 | # if we boot the kernel with: initcall_debug printk.time=1 we can | 581 | # if we boot the kernel with: initcall_debug printk.time=1 we can |
559 | # get all manner of interesting data from the dmesg output | 582 | # get all manner of interesting data from the dmesg output |
@@ -741,6 +764,11 @@ def _do_parse(writer, state, filename, file): | |||
741 | state.cmdline = _parse_cmdline_log(writer, file) | 764 | state.cmdline = _parse_cmdline_log(writer, file) |
742 | elif name == "monitor_disk.log": | 765 | elif name == "monitor_disk.log": |
743 | state.monitor_disk = _parse_monitor_disk_log(file) | 766 | state.monitor_disk = _parse_monitor_disk_log(file) |
767 | #pressure logs are in a subdirectory | ||
768 | elif name == "cpu.log": | ||
769 | state.cpu_pressure = _parse_pressure_logs(file, name) | ||
770 | elif name == "io.log": | ||
771 | state.io_pressure = _parse_pressure_logs(file, name) | ||
744 | elif not filename.endswith('.log'): | 772 | elif not filename.endswith('.log'): |
745 | _parse_bitbake_buildstats(writer, state, filename, file) | 773 | _parse_bitbake_buildstats(writer, state, filename, file) |
746 | t2 = time.process_time() | 774 | t2 = time.process_time() |
diff --git a/scripts/pybootchartgui/pybootchartgui/samples.py b/scripts/pybootchartgui/pybootchartgui/samples.py index 9fc309b3ab..472dc27be0 100644 --- a/scripts/pybootchartgui/pybootchartgui/samples.py +++ b/scripts/pybootchartgui/pybootchartgui/samples.py | |||
@@ -37,6 +37,23 @@ class CPUSample: | |||
37 | return str(self.time) + "\t" + str(self.user) + "\t" + \ | 37 | return str(self.time) + "\t" + str(self.user) + "\t" + \ |
38 | str(self.sys) + "\t" + str(self.io) + "\t" + str (self.swap) | 38 | str(self.sys) + "\t" + str(self.io) + "\t" + str (self.swap) |
39 | 39 | ||
40 | class CPUPressureSample: | ||
41 | def __init__(self, time, avg10, avg60, avg300, deltaTotal): | ||
42 | self.time = time | ||
43 | self.avg10 = avg10 | ||
44 | self.avg60 = avg60 | ||
45 | self.avg300 = avg300 | ||
46 | self.deltaTotal = deltaTotal | ||
47 | |||
48 | class IOPressureSample: | ||
49 | def __init__(self, time, avg10, avg60, avg300, deltaTotal): | ||
50 | self.time = time | ||
51 | self.avg10 = avg10 | ||
52 | self.avg60 = avg60 | ||
53 | self.avg300 = avg300 | ||
54 | self.deltaTotal = deltaTotal | ||
55 | |||
56 | |||
40 | class MemSample: | 57 | class MemSample: |
41 | used_values = ('MemTotal', 'MemFree', 'Buffers', 'Cached', 'SwapTotal', 'SwapFree',) | 58 | used_values = ('MemTotal', 'MemFree', 'Buffers', 'Cached', 'SwapTotal', 'SwapFree',) |
42 | 59 | ||