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 /scripts/pybootchartgui | |
| 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>
Diffstat (limited to 'scripts/pybootchartgui')
| -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 |
