summaryrefslogtreecommitdiffstats
path: root/scripts/pybootchartgui
diff options
context:
space:
mode:
authorAryaman Gupta <aryaman.gupta@windriver.com>2022-06-22 15:21:04 -0400
committerRichard Purdie <richard.purdie@linuxfoundation.org>2022-06-29 16:16:56 +0100
commit45f1e9d95320a45d7c38a0db34a643272e3add23 (patch)
tree1b29749b4d8ddeecbbf1bd7025d3ba58fc057135 /scripts/pybootchartgui
parentac162116b365900925bd753ae264847f751ce799 (diff)
downloadpoky-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/pybootchartgui')
-rw-r--r--scripts/pybootchartgui/pybootchartgui/draw.py77
-rw-r--r--scripts/pybootchartgui/pybootchartgui/parsing.py28
-rw-r--r--scripts/pybootchartgui/pybootchartgui/samples.py17
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
81MEM_SWAP_COLOR = DISK_TPUT_COLOR 81MEM_SWAP_COLOR = DISK_TPUT_COLOR
82 82
83# avg10 CPU pressure color
84CPU_PRESSURE_AVG10_COLOR = (0.0, 0.0, 0.0, 1.0)
85# delta total CPU pressure color
86CPU_PRESSURE_TOTAL_COLOR = CPU_COLOR
87# avg10 IO pressure color
88IO_PRESSURE_AVG10_COLOR = (0.0, 0.0, 0.0, 1.0)
89# delta total IO pressure color
90IO_PRESSURE_TOTAL_COLOR = IO_COLOR
91
92
93
94
83# Process border color. 95# Process border color.
84PROC_BORDER_COLOR = (0.71, 0.71, 0.71, 1.0) 96PROC_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
559def _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
40class 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
48class 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
40class MemSample: 57class MemSample:
41 used_values = ('MemTotal', 'MemFree', 'Buffers', 'Cached', 'SwapTotal', 'SwapFree',) 58 used_values = ('MemTotal', 'MemFree', 'Buffers', 'Cached', 'SwapTotal', 'SwapFree',)
42 59