diff options
author | Patrick Ohly <patrick.ohly@intel.com> | 2016-11-30 10:50:04 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-12-07 10:37:59 +0000 |
commit | 521887ea61b27b33023d6aac9df352d1fdef3ec0 (patch) | |
tree | 596664ce65044549cf11bf4ea0eef4cd90b69c13 | |
parent | a2c20921956bac82ba8df5e36243e9706639a4b2 (diff) | |
download | poky-521887ea61b27b33023d6aac9df352d1fdef3ec0.tar.gz |
pybootchartgui: show system utilization
This enables rendering of the original bootchart charts for CPU, disk
and memory usage. It depends on the /proc samples recorded by the
updated buildstats.bbclass. Currently, empty charts CPU and disk usage
charts are drawn if that data is not present; the memory chart already
gets skipped when there's no data, which will also have to be added
for the other two.
(From OE-Core rev: 233d3e50b361feea07803a9c0f2a691e687c6cd5)
Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r-- | scripts/pybootchartgui/pybootchartgui/draw.py | 16 | ||||
-rw-r--r-- | scripts/pybootchartgui/pybootchartgui/parsing.py | 61 |
2 files changed, 57 insertions, 20 deletions
diff --git a/scripts/pybootchartgui/pybootchartgui/draw.py b/scripts/pybootchartgui/pybootchartgui/draw.py index 925002d6e8..bddd8048c9 100644 --- a/scripts/pybootchartgui/pybootchartgui/draw.py +++ b/scripts/pybootchartgui/pybootchartgui/draw.py | |||
@@ -321,6 +321,16 @@ def extents(options, xscale, trace): | |||
321 | w = int ((end - start) * sec_w_base * xscale) + 2 * off_x | 321 | w = int ((end - start) * sec_w_base * xscale) + 2 * off_x |
322 | h = proc_h * processes + header_h + 2 * off_y | 322 | h = proc_h * processes + header_h + 2 * off_y |
323 | 323 | ||
324 | if options.charts: | ||
325 | if trace.cpu_stats: | ||
326 | h += 30 + bar_h | ||
327 | if trace.disk_stats: | ||
328 | h += 30 + bar_h | ||
329 | if trace.monitor_disk: | ||
330 | h += 30 + bar_h | ||
331 | if trace.mem_stats: | ||
332 | h += meminfo_bar_h | ||
333 | |||
324 | return (w, h) | 334 | return (w, h) |
325 | 335 | ||
326 | def clip_visible(clip, rect): | 336 | def clip_visible(clip, rect): |
@@ -496,6 +506,9 @@ def render(ctx, options, xscale, trace): | |||
496 | w -= 2*off_x | 506 | w -= 2*off_x |
497 | curr_y = off_y; | 507 | curr_y = off_y; |
498 | 508 | ||
509 | if options.charts: | ||
510 | curr_y = render_charts (ctx, options, clip, trace, curr_y, w, h, sec_w) | ||
511 | |||
499 | curr_y = render_processes_chart (ctx, options, trace, curr_y, w, h, sec_w) | 512 | curr_y = render_processes_chart (ctx, options, trace, curr_y, w, h, sec_w) |
500 | 513 | ||
501 | return | 514 | return |
@@ -513,9 +526,6 @@ def render(ctx, options, xscale, trace): | |||
513 | else: | 526 | else: |
514 | curr_y = off_y; | 527 | curr_y = off_y; |
515 | 528 | ||
516 | if options.charts: | ||
517 | curr_y = render_charts (ctx, options, clip, trace, curr_y, w, h, sec_w) | ||
518 | |||
519 | # draw process boxes | 529 | # draw process boxes |
520 | proc_height = h | 530 | proc_height = h |
521 | if proc_tree.taskstats and options.cumulative: | 531 | if proc_tree.taskstats and options.cumulative: |
diff --git a/scripts/pybootchartgui/pybootchartgui/parsing.py b/scripts/pybootchartgui/pybootchartgui/parsing.py index a3a0b0b339..af684353fd 100644 --- a/scripts/pybootchartgui/pybootchartgui/parsing.py +++ b/scripts/pybootchartgui/pybootchartgui/parsing.py | |||
@@ -38,16 +38,17 @@ class Trace: | |||
38 | self.min = None | 38 | self.min = None |
39 | self.max = None | 39 | self.max = None |
40 | self.headers = None | 40 | self.headers = None |
41 | self.disk_stats = None | 41 | self.disk_stats = [] |
42 | self.ps_stats = None | 42 | self.ps_stats = None |
43 | self.taskstats = None | 43 | self.taskstats = None |
44 | self.cpu_stats = None | 44 | self.cpu_stats = [] |
45 | self.cmdline = None | 45 | self.cmdline = None |
46 | self.kernel = None | 46 | self.kernel = None |
47 | self.kernel_tree = None | 47 | self.kernel_tree = None |
48 | self.filename = None | 48 | self.filename = None |
49 | self.parent_map = None | 49 | self.parent_map = None |
50 | self.mem_stats = None | 50 | self.mem_stats = [] |
51 | self.times = [] # Always empty, but expected by draw.py when drawing system charts. | ||
51 | 52 | ||
52 | if len(paths): | 53 | if len(paths): |
53 | parse_paths (writer, self, paths) | 54 | parse_paths (writer, self, paths) |
@@ -58,6 +59,19 @@ class Trace: | |||
58 | self.min = min(self.start.keys()) | 59 | self.min = min(self.start.keys()) |
59 | self.max = max(self.end.keys()) | 60 | self.max = max(self.end.keys()) |
60 | 61 | ||
62 | |||
63 | # Rendering system charts depends on start and end | ||
64 | # time. Provide them where the original drawing code expects | ||
65 | # them, i.e. in proc_tree. | ||
66 | class BitbakeProcessTree: | ||
67 | def __init__(self, start_time, end_time): | ||
68 | self.start_time = start_time | ||
69 | self.end_time = end_time | ||
70 | self.duration = self.end_time - self.start_time | ||
71 | self.proc_tree = BitbakeProcessTree(min(self.start.keys()), | ||
72 | max(self.end.keys())) | ||
73 | |||
74 | |||
61 | return | 75 | return |
62 | 76 | ||
63 | # Turn that parsed information into something more useful | 77 | # Turn that parsed information into something more useful |
@@ -427,7 +441,7 @@ def _parse_proc_stat_log(file): | |||
427 | # skip the rest of statistics lines | 441 | # skip the rest of statistics lines |
428 | return samples | 442 | return samples |
429 | 443 | ||
430 | def _parse_proc_disk_stat_log(file, numCpu): | 444 | def _parse_proc_disk_stat_log(file): |
431 | """ | 445 | """ |
432 | Parse file for disk stats, but only look at the whole device, eg. sda, | 446 | Parse file for disk stats, but only look at the whole device, eg. sda, |
433 | not sda1, sda2 etc. The format of relevant lines should be: | 447 | not sda1, sda2 etc. The format of relevant lines should be: |
@@ -462,7 +476,7 @@ def _parse_proc_disk_stat_log(file, numCpu): | |||
462 | sums = [ a - b for a, b in zip(sample1.diskdata, sample2.diskdata) ] | 476 | sums = [ a - b for a, b in zip(sample1.diskdata, sample2.diskdata) ] |
463 | readTput = sums[0] / 2.0 * 100.0 / interval | 477 | readTput = sums[0] / 2.0 * 100.0 / interval |
464 | writeTput = sums[1] / 2.0 * 100.0 / interval | 478 | writeTput = sums[1] / 2.0 * 100.0 / interval |
465 | util = float( sums[2] ) / 10 / interval / numCpu | 479 | util = float( sums[2] ) / 10 / interval |
466 | util = max(0.0, min(1.0, util)) | 480 | util = max(0.0, min(1.0, util)) |
467 | disk_stats.append(DiskSample(sample2.time, readTput, writeTput, util)) | 481 | disk_stats.append(DiskSample(sample2.time, readTput, writeTput, util)) |
468 | 482 | ||
@@ -628,6 +642,20 @@ def _parse_cmdline_log(writer, file): | |||
628 | cmdLines[pid] = values | 642 | cmdLines[pid] = values |
629 | return cmdLines | 643 | return cmdLines |
630 | 644 | ||
645 | def _parse_bitbake_buildstats(writer, state, filename, file): | ||
646 | paths = filename.split("/") | ||
647 | task = paths[-1] | ||
648 | pn = paths[-2] | ||
649 | start = None | ||
650 | end = None | ||
651 | for line in file: | ||
652 | if line.startswith("Started:"): | ||
653 | start = int(float(line.split()[-1])) | ||
654 | elif line.startswith("Ended:"): | ||
655 | end = int(float(line.split()[-1])) | ||
656 | if start and end: | ||
657 | state.add_process(pn + ":" + task, start, end) | ||
658 | |||
631 | def get_num_cpus(headers): | 659 | def get_num_cpus(headers): |
632 | """Get the number of CPUs from the system.cpu header property. As the | 660 | """Get the number of CPUs from the system.cpu header property. As the |
633 | CPU utilization graphs are relative, the number of CPUs currently makes | 661 | CPU utilization graphs are relative, the number of CPUs currently makes |
@@ -647,18 +675,17 @@ def get_num_cpus(headers): | |||
647 | def _do_parse(writer, state, filename, file): | 675 | def _do_parse(writer, state, filename, file): |
648 | writer.info("parsing '%s'" % filename) | 676 | writer.info("parsing '%s'" % filename) |
649 | t1 = clock() | 677 | t1 = clock() |
650 | paths = filename.split("/") | 678 | name = os.path.basename(filename) |
651 | task = paths[-1] | 679 | if name == "proc_diskstats.log": |
652 | pn = paths[-2] | 680 | state.disk_stats = _parse_proc_disk_stat_log(file) |
653 | start = None | 681 | elif name == "proc_stat.log": |
654 | end = None | 682 | state.cpu_stats = _parse_proc_stat_log(file) |
655 | for line in file: | 683 | elif name == "proc_meminfo.log": |
656 | if line.startswith("Started:"): | 684 | state.mem_stats = _parse_proc_meminfo_log(file) |
657 | start = int(float(line.split()[-1])) | 685 | elif name == "cmdline2.log": |
658 | elif line.startswith("Ended:"): | 686 | state.cmdline = _parse_cmdline_log(writer, file) |
659 | end = int(float(line.split()[-1])) | 687 | elif not filename.endswith('.log'): |
660 | if start and end: | 688 | _parse_bitbake_buildstats(writer, state, filename, file) |
661 | state.add_process(pn + ":" + task, start, end) | ||
662 | t2 = clock() | 689 | t2 = clock() |
663 | writer.info(" %s seconds" % str(t2-t1)) | 690 | writer.info(" %s seconds" % str(t2-t1)) |
664 | return state | 691 | return state |