From 233d3e50b361feea07803a9c0f2a691e687c6cd5 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Wed, 30 Nov 2016 10:50:04 +0100 Subject: 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. Signed-off-by: Patrick Ohly Signed-off-by: Ross Burton --- scripts/pybootchartgui/pybootchartgui/draw.py | 16 +++++-- 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): w = int ((end - start) * sec_w_base * xscale) + 2 * off_x h = proc_h * processes + header_h + 2 * off_y + if options.charts: + if trace.cpu_stats: + h += 30 + bar_h + if trace.disk_stats: + h += 30 + bar_h + if trace.monitor_disk: + h += 30 + bar_h + if trace.mem_stats: + h += meminfo_bar_h + return (w, h) def clip_visible(clip, rect): @@ -496,6 +506,9 @@ def render(ctx, options, xscale, trace): w -= 2*off_x curr_y = off_y; + if options.charts: + curr_y = render_charts (ctx, options, clip, trace, curr_y, w, h, sec_w) + curr_y = render_processes_chart (ctx, options, trace, curr_y, w, h, sec_w) return @@ -513,9 +526,6 @@ def render(ctx, options, xscale, trace): else: curr_y = off_y; - if options.charts: - curr_y = render_charts (ctx, options, clip, trace, curr_y, w, h, sec_w) - # draw process boxes proc_height = h 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: self.min = None self.max = None self.headers = None - self.disk_stats = None + self.disk_stats = [] self.ps_stats = None self.taskstats = None - self.cpu_stats = None + self.cpu_stats = [] self.cmdline = None self.kernel = None self.kernel_tree = None self.filename = None self.parent_map = None - self.mem_stats = None + self.mem_stats = [] + self.times = [] # Always empty, but expected by draw.py when drawing system charts. if len(paths): parse_paths (writer, self, paths) @@ -58,6 +59,19 @@ class Trace: self.min = min(self.start.keys()) self.max = max(self.end.keys()) + + # Rendering system charts depends on start and end + # time. Provide them where the original drawing code expects + # them, i.e. in proc_tree. + class BitbakeProcessTree: + def __init__(self, start_time, end_time): + self.start_time = start_time + self.end_time = end_time + self.duration = self.end_time - self.start_time + self.proc_tree = BitbakeProcessTree(min(self.start.keys()), + max(self.end.keys())) + + return # Turn that parsed information into something more useful @@ -427,7 +441,7 @@ def _parse_proc_stat_log(file): # skip the rest of statistics lines return samples -def _parse_proc_disk_stat_log(file, numCpu): +def _parse_proc_disk_stat_log(file): """ Parse file for disk stats, but only look at the whole device, eg. sda, not sda1, sda2 etc. The format of relevant lines should be: @@ -462,7 +476,7 @@ def _parse_proc_disk_stat_log(file, numCpu): sums = [ a - b for a, b in zip(sample1.diskdata, sample2.diskdata) ] readTput = sums[0] / 2.0 * 100.0 / interval writeTput = sums[1] / 2.0 * 100.0 / interval - util = float( sums[2] ) / 10 / interval / numCpu + util = float( sums[2] ) / 10 / interval util = max(0.0, min(1.0, util)) disk_stats.append(DiskSample(sample2.time, readTput, writeTput, util)) @@ -628,6 +642,20 @@ def _parse_cmdline_log(writer, file): cmdLines[pid] = values return cmdLines +def _parse_bitbake_buildstats(writer, state, filename, file): + paths = filename.split("/") + task = paths[-1] + pn = paths[-2] + start = None + end = None + for line in file: + if line.startswith("Started:"): + start = int(float(line.split()[-1])) + elif line.startswith("Ended:"): + end = int(float(line.split()[-1])) + if start and end: + state.add_process(pn + ":" + task, start, end) + def get_num_cpus(headers): """Get the number of CPUs from the system.cpu header property. As the CPU utilization graphs are relative, the number of CPUs currently makes @@ -647,18 +675,17 @@ def get_num_cpus(headers): def _do_parse(writer, state, filename, file): writer.info("parsing '%s'" % filename) t1 = clock() - paths = filename.split("/") - task = paths[-1] - pn = paths[-2] - start = None - end = None - for line in file: - if line.startswith("Started:"): - start = int(float(line.split()[-1])) - elif line.startswith("Ended:"): - end = int(float(line.split()[-1])) - if start and end: - state.add_process(pn + ":" + task, start, end) + name = os.path.basename(filename) + if name == "proc_diskstats.log": + state.disk_stats = _parse_proc_disk_stat_log(file) + elif name == "proc_stat.log": + state.cpu_stats = _parse_proc_stat_log(file) + elif name == "proc_meminfo.log": + state.mem_stats = _parse_proc_meminfo_log(file) + elif name == "cmdline2.log": + state.cmdline = _parse_cmdline_log(writer, file) + elif not filename.endswith('.log'): + _parse_bitbake_buildstats(writer, state, filename, file) t2 = clock() writer.info(" %s seconds" % str(t2-t1)) return state -- cgit 1.2.3-korg