summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick Ohly <patrick.ohly@intel.com>2016-11-30 10:50:04 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-12-07 10:36:10 +0000
commit233d3e50b361feea07803a9c0f2a691e687c6cd5 (patch)
treed1f122f8c29f8f30e08a4f1eb5d0fe3fc3f5ed19
parent166f8f9aaa1f01fc6d6a5451f8f06b815c51ffae (diff)
downloadopenembedded-core-contrib-233d3e50b361feea07803a9c0f2a691e687c6cd5.tar.gz
openembedded-core-contrib-233d3e50b361feea07803a9c0f2a691e687c6cd5.tar.bz2
openembedded-core-contrib-233d3e50b361feea07803a9c0f2a691e687c6cd5.zip
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 <patrick.ohly@intel.com> Signed-off-by: Ross Burton <ross.burton@intel.com>
-rw-r--r--scripts/pybootchartgui/pybootchartgui/draw.py16
-rw-r--r--scripts/pybootchartgui/pybootchartgui/parsing.py61
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