summaryrefslogtreecommitdiffstats
path: root/scripts/pybootchartgui/pybootchartgui
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/pybootchartgui/pybootchartgui')
-rw-r--r--scripts/pybootchartgui/pybootchartgui/draw.py158
-rw-r--r--scripts/pybootchartgui/pybootchartgui/parsing.py35
-rw-r--r--scripts/pybootchartgui/pybootchartgui/samples.py25
3 files changed, 208 insertions, 10 deletions
diff --git a/scripts/pybootchartgui/pybootchartgui/draw.py b/scripts/pybootchartgui/pybootchartgui/draw.py
index 53324b9f8b..c6e67833ab 100644
--- a/scripts/pybootchartgui/pybootchartgui/draw.py
+++ b/scripts/pybootchartgui/pybootchartgui/draw.py
@@ -80,6 +80,22 @@ 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# avg10 memory pressure color
92MEM_PRESSURE_AVG10_COLOR = (0.0, 0.0, 0.0, 1.0)
93# delta total memory pressure color
94MEM_PRESSURE_TOTAL_COLOR = DISK_TPUT_COLOR
95
96
97
98
83# Process border color. 99# Process border color.
84PROC_BORDER_COLOR = (0.71, 0.71, 0.71, 1.0) 100PROC_BORDER_COLOR = (0.71, 0.71, 0.71, 1.0)
85# Waiting process color. 101# Waiting process color.
@@ -267,11 +283,14 @@ def draw_chart(ctx, color, fill, chart_bounds, data, proc_tree, data_range):
267 # avoid divide by zero 283 # avoid divide by zero
268 if max_y == 0: 284 if max_y == 0:
269 max_y = 1.0 285 max_y = 1.0
270 xscale = float (chart_bounds[2]) / (max_x - x_shift) 286 if (max_x - x_shift):
287 xscale = float (chart_bounds[2]) / (max_x - x_shift)
288 else:
289 xscale = float (chart_bounds[2])
271 # If data_range is given, scale the chart so that the value range in 290 # If data_range is given, scale the chart so that the value range in
272 # data_range matches the chart bounds exactly. 291 # data_range matches the chart bounds exactly.
273 # Otherwise, scale so that the actual data matches the chart bounds. 292 # Otherwise, scale so that the actual data matches the chart bounds.
274 if data_range: 293 if data_range and (data_range[1] - data_range[0]):
275 yscale = float(chart_bounds[3]) / (data_range[1] - data_range[0]) 294 yscale = float(chart_bounds[3]) / (data_range[1] - data_range[0])
276 ybase = data_range[0] 295 ybase = data_range[0]
277 else: 296 else:
@@ -337,6 +356,12 @@ def extents(options, xscale, trace):
337 h += 30 + bar_h 356 h += 30 + bar_h
338 if trace.disk_stats: 357 if trace.disk_stats:
339 h += 30 + bar_h 358 h += 30 + bar_h
359 if trace.cpu_pressure:
360 h += 30 + bar_h
361 if trace.io_pressure:
362 h += 30 + bar_h
363 if trace.mem_pressure:
364 h += 30 + bar_h
340 if trace.monitor_disk: 365 if trace.monitor_disk:
341 h += 30 + bar_h 366 h += 30 + bar_h
342 if trace.mem_stats: 367 if trace.mem_stats:
@@ -412,6 +437,108 @@ def render_charts(ctx, options, clip, trace, curr_y, w, h, sec_w):
412 437
413 curr_y = curr_y + 30 + bar_h 438 curr_y = curr_y + 30 + bar_h
414 439
440 # render CPU pressure chart
441 if trace.cpu_pressure:
442 max_sample_avg = max (trace.cpu_pressure, key = lambda s: s.avg10)
443 max_sample_total = max (trace.cpu_pressure, key = lambda s: s.deltaTotal)
444 draw_legend_line(ctx, "avg10 CPU Pressure (max %d%%)" % (max_sample_avg.avg10), CPU_PRESSURE_AVG10_COLOR, off_x, curr_y+20, leg_s)
445 draw_legend_box(ctx, "delta total CPU Pressure (max %d)" % (max_sample_total.deltaTotal), CPU_PRESSURE_TOTAL_COLOR, off_x + 240, curr_y+20, leg_s)
446
447 # render delta total cpu
448 chart_rect = (off_x, curr_y+30, w, bar_h)
449 if clip_visible (clip, chart_rect):
450 draw_box_ticks (ctx, chart_rect, sec_w)
451 draw_annotations (ctx, proc_tree, trace.times, chart_rect)
452 draw_chart (ctx, CPU_PRESSURE_TOTAL_COLOR, True, chart_rect, \
453 [(sample.time, sample.deltaTotal) for sample in trace.cpu_pressure], \
454 proc_tree, None)
455
456 # render avg10 cpu
457 if clip_visible (clip, chart_rect):
458 draw_chart (ctx, CPU_PRESSURE_AVG10_COLOR, False, chart_rect, \
459 [(sample.time, sample.avg10) for sample in trace.cpu_pressure], \
460 proc_tree, None)
461
462 pos_x = off_x + ((max_sample_avg.time - proc_tree.start_time) * w / proc_tree.duration)
463
464 shift_x, shift_y = -20, 20
465 if (pos_x < off_x + 245):
466 shift_x, shift_y = 5, 40
467
468
469 label = "%d%%" % (max_sample_avg.avg10)
470 draw_text (ctx, label, CPU_PRESSURE_AVG10_COLOR, pos_x + shift_x, curr_y + shift_y)
471
472 curr_y = curr_y + 30 + bar_h
473
474 # render I/O pressure chart
475 if trace.io_pressure:
476 max_sample_avg = max (trace.io_pressure, key = lambda s: s.avg10)
477 max_sample_total = max (trace.io_pressure, key = lambda s: s.deltaTotal)
478 draw_legend_line(ctx, "avg10 I/O Pressure (max %d%%)" % (max_sample_avg.avg10), IO_PRESSURE_AVG10_COLOR, off_x, curr_y+20, leg_s)
479 draw_legend_box(ctx, "delta total I/O Pressure (max %d)" % (max_sample_total.deltaTotal), IO_PRESSURE_TOTAL_COLOR, off_x + 240, curr_y+20, leg_s)
480
481 # render delta total io
482 chart_rect = (off_x, curr_y+30, w, bar_h)
483 if clip_visible (clip, chart_rect):
484 draw_box_ticks (ctx, chart_rect, sec_w)
485 draw_annotations (ctx, proc_tree, trace.times, chart_rect)
486 draw_chart (ctx, IO_PRESSURE_TOTAL_COLOR, True, chart_rect, \
487 [(sample.time, sample.deltaTotal) for sample in trace.io_pressure], \
488 proc_tree, None)
489
490 # render avg10 io
491 if clip_visible (clip, chart_rect):
492 draw_chart (ctx, IO_PRESSURE_AVG10_COLOR, False, chart_rect, \
493 [(sample.time, sample.avg10) for sample in trace.io_pressure], \
494 proc_tree, None)
495
496 pos_x = off_x + ((max_sample_avg.time - proc_tree.start_time) * w / proc_tree.duration)
497
498 shift_x, shift_y = -20, 20
499 if (pos_x < off_x + 245):
500 shift_x, shift_y = 5, 40
501
502
503 label = "%d%%" % (max_sample_avg.avg10)
504 draw_text (ctx, label, IO_PRESSURE_AVG10_COLOR, pos_x + shift_x, curr_y + shift_y)
505
506 curr_y = curr_y + 30 + bar_h
507
508 # render MEM pressure chart
509 if trace.mem_pressure:
510 max_sample_avg = max (trace.mem_pressure, key = lambda s: s.avg10)
511 max_sample_total = max (trace.mem_pressure, key = lambda s: s.deltaTotal)
512 draw_legend_line(ctx, "avg10 MEM Pressure (max %d%%)" % (max_sample_avg.avg10), MEM_PRESSURE_AVG10_COLOR, off_x, curr_y+20, leg_s)
513 draw_legend_box(ctx, "delta total MEM Pressure (max %d)" % (max_sample_total.deltaTotal), MEM_PRESSURE_TOTAL_COLOR, off_x + 240, curr_y+20, leg_s)
514
515 # render delta total mem
516 chart_rect = (off_x, curr_y+30, w, bar_h)
517 if clip_visible (clip, chart_rect):
518 draw_box_ticks (ctx, chart_rect, sec_w)
519 draw_annotations (ctx, proc_tree, trace.times, chart_rect)
520 draw_chart (ctx, MEM_PRESSURE_TOTAL_COLOR, True, chart_rect, \
521 [(sample.time, sample.deltaTotal) for sample in trace.mem_pressure], \
522 proc_tree, None)
523
524 # render avg10 mem
525 if clip_visible (clip, chart_rect):
526 draw_chart (ctx, MEM_PRESSURE_AVG10_COLOR, False, chart_rect, \
527 [(sample.time, sample.avg10) for sample in trace.mem_pressure], \
528 proc_tree, None)
529
530 pos_x = off_x + ((max_sample_avg.time - proc_tree.start_time) * w / proc_tree.duration)
531
532 shift_x, shift_y = -20, 20
533 if (pos_x < off_x + 245):
534 shift_x, shift_y = 5, 40
535
536
537 label = "%d%%" % (max_sample_avg.avg10)
538 draw_text (ctx, label, MEM_PRESSURE_AVG10_COLOR, pos_x + shift_x, curr_y + shift_y)
539
540 curr_y = curr_y + 30 + bar_h
541
415 # render disk space usage 542 # render disk space usage
416 # 543 #
417 # Draws the amount of disk space used on each volume relative to the 544 # Draws the amount of disk space used on each volume relative to the
@@ -493,8 +620,8 @@ def render_charts(ctx, options, clip, trace, curr_y, w, h, sec_w):
493 620
494 return curr_y 621 return curr_y
495 622
496def render_processes_chart(ctx, options, trace, curr_y, w, h, sec_w): 623def render_processes_chart(ctx, options, trace, curr_y, width, h, sec_w):
497 chart_rect = [off_x, curr_y+header_h, w, h - curr_y - 1 * off_y - header_h ] 624 chart_rect = [off_x, curr_y+header_h, width, h - curr_y - 1 * off_y - header_h ]
498 625
499 draw_legend_box (ctx, "Configure", \ 626 draw_legend_box (ctx, "Configure", \
500 TASK_COLOR_CONFIGURE, off_x , curr_y + 45, leg_s) 627 TASK_COLOR_CONFIGURE, off_x , curr_y + 45, leg_s)
@@ -519,8 +646,9 @@ def render_processes_chart(ctx, options, trace, curr_y, w, h, sec_w):
519 offset = trace.min or min(trace.start.keys()) 646 offset = trace.min or min(trace.start.keys())
520 for start in sorted(trace.start.keys()): 647 for start in sorted(trace.start.keys()):
521 for process in sorted(trace.start[start]): 648 for process in sorted(trace.start[start]):
649 elapsed_time = trace.processes[process][1] - start
522 if not options.app_options.show_all and \ 650 if not options.app_options.show_all and \
523 trace.processes[process][1] - start < options.app_options.mintime: 651 elapsed_time < options.app_options.mintime:
524 continue 652 continue
525 task = process.split(":")[1] 653 task = process.split(":")[1]
526 654
@@ -529,14 +657,23 @@ def render_processes_chart(ctx, options, trace, curr_y, w, h, sec_w):
529 #print(s) 657 #print(s)
530 658
531 x = chart_rect[0] + (start - offset) * sec_w 659 x = chart_rect[0] + (start - offset) * sec_w
532 w = ((trace.processes[process][1] - start) * sec_w) 660 w = elapsed_time * sec_w
661
662 def set_alfa(color, alfa):
663 clist = list(color)
664 clist[-1] = alfa
665 return tuple(clist)
533 666
534 #print("proc at %s %s %s %s" % (x, y, w, proc_h)) 667 #print("proc at %s %s %s %s" % (x, y, w, proc_h))
535 col = None 668 col = None
536 if task == "do_compile": 669 if task == "do_compile":
537 col = TASK_COLOR_COMPILE 670 col = TASK_COLOR_COMPILE
671 elif "do_compile" in task:
672 col = set_alfa(TASK_COLOR_COMPILE, 0.25)
538 elif task == "do_configure": 673 elif task == "do_configure":
539 col = TASK_COLOR_CONFIGURE 674 col = TASK_COLOR_CONFIGURE
675 elif "do_configure" in task:
676 col = set_alfa(TASK_COLOR_CONFIGURE, 0.25)
540 elif task == "do_install": 677 elif task == "do_install":
541 col = TASK_COLOR_INSTALL 678 col = TASK_COLOR_INSTALL
542 elif task == "do_populate_sysroot": 679 elif task == "do_populate_sysroot":
@@ -554,7 +691,10 @@ def render_processes_chart(ctx, options, trace, curr_y, w, h, sec_w):
554 draw_fill_rect(ctx, col, (x, y, w, proc_h)) 691 draw_fill_rect(ctx, col, (x, y, w, proc_h))
555 draw_rect(ctx, PROC_BORDER_COLOR, (x, y, w, proc_h)) 692 draw_rect(ctx, PROC_BORDER_COLOR, (x, y, w, proc_h))
556 693
557 draw_label_in_box(ctx, PROC_TEXT_COLOR, process, x, y + proc_h - 4, w, proc_h) 694 # Show elapsed time for each task
695 process = "%ds %s" % (elapsed_time, process)
696 draw_label_in_box(ctx, PROC_TEXT_COLOR, process, x, y + proc_h - 4, w, width)
697
558 y = y + proc_h 698 y = y + proc_h
559 699
560 return curr_y 700 return curr_y
@@ -695,7 +835,7 @@ def draw_processes_recursively(ctx, proc, proc_tree, y, proc_h, rect, clip) :
695 cmdString = proc.cmd 835 cmdString = proc.cmd
696 else: 836 else:
697 cmdString = '' 837 cmdString = ''
698 if (OPTIONS.show_pid or OPTIONS.show_all) and ipid is not 0: 838 if (OPTIONS.show_pid or OPTIONS.show_all) and ipid != 0:
699 cmdString = cmdString + " [" + str(ipid // 1000) + "]" 839 cmdString = cmdString + " [" + str(ipid // 1000) + "]"
700 if OPTIONS.show_all: 840 if OPTIONS.show_all:
701 if proc.args: 841 if proc.args:
@@ -793,7 +933,7 @@ class CumlSample:
793 if self.color is None: 933 if self.color is None:
794 i = self.next() % HSV_MAX_MOD 934 i = self.next() % HSV_MAX_MOD
795 h = 0.0 935 h = 0.0
796 if i is not 0: 936 if i != 0:
797 h = (1.0 * i) / HSV_MAX_MOD 937 h = (1.0 * i) / HSV_MAX_MOD
798 s = 0.5 938 s = 0.5
799 v = 1.0 939 v = 1.0
diff --git a/scripts/pybootchartgui/pybootchartgui/parsing.py b/scripts/pybootchartgui/pybootchartgui/parsing.py
index b42dac6b88..63a53b6b88 100644
--- a/scripts/pybootchartgui/pybootchartgui/parsing.py
+++ b/scripts/pybootchartgui/pybootchartgui/parsing.py
@@ -49,6 +49,9 @@ 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 = []
54 self.mem_pressure = []
52 self.times = [] # Always empty, but expected by draw.py when drawing system charts. 55 self.times = [] # Always empty, but expected by draw.py when drawing system charts.
53 56
54 if len(paths): 57 if len(paths):
@@ -128,7 +131,7 @@ class Trace:
128 def compile(self, writer): 131 def compile(self, writer):
129 132
130 def find_parent_id_for(pid): 133 def find_parent_id_for(pid):
131 if pid is 0: 134 if pid == 0:
132 return 0 135 return 0
133 ppid = self.parent_map.get(pid) 136 ppid = self.parent_map.get(pid)
134 if ppid: 137 if ppid:
@@ -554,6 +557,29 @@ def _parse_monitor_disk_log(file):
554 557
555 return disk_stats 558 return disk_stats
556 559
560def _parse_pressure_logs(file, filename):
561 """
562 Parse file for "some" pressure with 'avg10', 'avg60' 'avg300' and delta total values
563 (in that order) directly stored on one line for both CPU and IO, based on filename.
564 """
565 pressure_stats = []
566 if filename == "cpu.log":
567 SamplingClass = CPUPressureSample
568 elif filename == "memory.log":
569 SamplingClass = MemPressureSample
570 else:
571 SamplingClass = IOPressureSample
572 for time, lines in _parse_timed_blocks(file):
573 for line in lines:
574 if not line: continue
575 tokens = line.split()
576 avg10 = float(tokens[0])
577 avg60 = float(tokens[1])
578 avg300 = float(tokens[2])
579 delta = float(tokens[3])
580 pressure_stats.append(SamplingClass(time, avg10, avg60, avg300, delta))
581
582 return pressure_stats
557 583
558# if we boot the kernel with: initcall_debug printk.time=1 we can 584# if we boot the kernel with: initcall_debug printk.time=1 we can
559# get all manner of interesting data from the dmesg output 585# get all manner of interesting data from the dmesg output
@@ -741,6 +767,13 @@ def _do_parse(writer, state, filename, file):
741 state.cmdline = _parse_cmdline_log(writer, file) 767 state.cmdline = _parse_cmdline_log(writer, file)
742 elif name == "monitor_disk.log": 768 elif name == "monitor_disk.log":
743 state.monitor_disk = _parse_monitor_disk_log(file) 769 state.monitor_disk = _parse_monitor_disk_log(file)
770 #pressure logs are in a subdirectory
771 elif name == "cpu.log":
772 state.cpu_pressure = _parse_pressure_logs(file, name)
773 elif name == "io.log":
774 state.io_pressure = _parse_pressure_logs(file, name)
775 elif name == "memory.log":
776 state.mem_pressure = _parse_pressure_logs(file, name)
744 elif not filename.endswith('.log'): 777 elif not filename.endswith('.log'):
745 _parse_bitbake_buildstats(writer, state, filename, file) 778 _parse_bitbake_buildstats(writer, state, filename, file)
746 t2 = time.process_time() 779 t2 = time.process_time()
diff --git a/scripts/pybootchartgui/pybootchartgui/samples.py b/scripts/pybootchartgui/pybootchartgui/samples.py
index 9fc309b3ab..a70d8a5a28 100644
--- a/scripts/pybootchartgui/pybootchartgui/samples.py
+++ b/scripts/pybootchartgui/pybootchartgui/samples.py
@@ -37,6 +37,31 @@ 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
56class MemPressureSample:
57 def __init__(self, time, avg10, avg60, avg300, deltaTotal):
58 self.time = time
59 self.avg10 = avg10
60 self.avg60 = avg60
61 self.avg300 = avg300
62 self.deltaTotal = deltaTotal
63
64
40class MemSample: 65class MemSample:
41 used_values = ('MemTotal', 'MemFree', 'Buffers', 'Cached', 'SwapTotal', 'SwapFree',) 66 used_values = ('MemTotal', 'MemFree', 'Buffers', 'Cached', 'SwapTotal', 'SwapFree',)
42 67