summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYongqin Liu <yongqin.liu@linaro.org>2017-03-31 22:19:30 +0800
committerYongqin Liu <yongqin.liu@linaro.org>2017-03-31 22:19:30 +0800
commitee302247199b720dafc1cacd8d4585331ebd0e67 (patch)
treeb1c349bc8cc0c05cf9ac356349ecbd38a3bd17a8
parent417f77884dd0ba2d8196d38ff128578c5b18dd7a (diff)
changes to print chart for partitions as well
also print the disk write throughout as well Signed-off-by: Yongqin Liu <yongqin.liu@linaro.org>
-rw-r--r--draw.py54
-rw-r--r--parsing.py53
-rw-r--r--samples.py168
3 files changed, 154 insertions, 121 deletions
diff --git a/draw.py b/draw.py
index d627cc8..399fecd 100644
--- a/draw.py
+++ b/draw.py
@@ -47,9 +47,11 @@ LEGEND_FONT_SIZE = 12
# CPU load chart color.
CPU_COLOR = (0.40, 0.55, 0.70, 1.0)
# IO wait chart color.
-IO_COLOR = (0.76, 0.48, 0.48, 0.5)
+IO_COLOR = (1, 0, 0, 1)
# Disk throughput color.
DISK_TPUT_COLOR = (0.20, 0.71, 0.20, 1.0)
+DISK_TPUT_READ_COLOR = (0.71, 0.71, 0.20, 1.0)
+DISK_TPUT_WRITE_COLOR = (0.71, 0.71, 0.71, 1.0)
# CPU load chart color.
FILE_OPEN_COLOR = (0.20, 0.71, 0.71, 1.0)
@@ -268,38 +270,56 @@ def render(ctx, options, headers, cpu_stats, disk_stats, proc_tree, times):
chart_rect = (off_x, curr_y+30, w, bar_h)
draw_box_ticks(ctx, chart_rect, sec_w)
draw_annotations(ctx, proc_tree, times, chart_rect, sec_w)
- draw_chart(ctx, IO_COLOR, True, chart_rect, [(sample.time, sample.user + sample.sys + sample.io) for sample in cpu_stats[cpu]], proc_tree)
# render CPU load
draw_chart(ctx, CPU_COLOR, True, chart_rect, [(sample.time, sample.user + sample.sys) for sample in cpu_stats[cpu]], proc_tree)
+ draw_chart(ctx, IO_COLOR, False, chart_rect, [(sample.time, sample.user + sample.sys + sample.io) for sample in cpu_stats[cpu]], proc_tree)
draw_text(ctx, cpu, DISK_TPUT_COLOR, off_x, curr_y + 50)
curr_y = curr_y + 30 + bar_h
-###############################
+ ###############################
# render second chart
draw_legend_line(ctx, "Disk throughput", DISK_TPUT_COLOR, off_x, curr_y+20, leg_s)
draw_legend_box(ctx, "Disk utilization", IO_COLOR, off_x + 120, curr_y+20, leg_s)
+ ##draw_legend_box(ctx, "Disk read", DISK_TPUT_READ_COLOR, off_x + 250, curr_y+20, leg_s)
+ draw_legend_box(ctx, "Disk write", DISK_TPUT_WRITE_COLOR, off_x + 250, curr_y+20, leg_s)
- # render I/O utilization
- chart_rect = (off_x, curr_y+30, w, bar_h)
- draw_box_ticks(ctx, chart_rect, sec_w)
- draw_annotations(ctx, proc_tree, times, chart_rect, sec_w)
- draw_chart(ctx, IO_COLOR, True, chart_rect, [(sample.time, sample.util) for sample in disk_stats], proc_tree)
+ disks = disk_stats.keys()
+ disks.sort()
+ for disk in disks:
+
+ max_util = max(sample.util for sample in disk_stats[disk])
+ if not max_util > 0:
+ continue
+
+ # render I/O utilization
+ chart_rect = (off_x, curr_y+30, w, bar_h)
+ draw_box_ticks(ctx, chart_rect, sec_w)
+ draw_annotations(ctx, proc_tree, times, chart_rect, sec_w)
- # render disk throughput
- max_sample = max(disk_stats, key=lambda s: s.tput)
- draw_chart(ctx, DISK_TPUT_COLOR, False, chart_rect, [(sample.time, sample.tput) for sample in disk_stats], proc_tree)
+ max_sample = max(disk_stats[disk], key=lambda s: s.tput)
+ draw_chart(ctx, DISK_TPUT_COLOR, True, chart_rect, [(sample.time, sample.tput) for sample in disk_stats[disk]], proc_tree)
- pos_x = off_x + ((max_sample.time - proc_tree.start_time) * w / proc_tree.duration)
+ pos_x = off_x + ((max_sample.time - proc_tree.start_time) * w / proc_tree.duration)
- shift_x, shift_y = -20, 20
- if (pos_x < off_x + 245):
- shift_x, shift_y = 5, 40
+ shift_x, shift_y = -20, 20
+ if (pos_x < off_x + 245):
+ shift_x, shift_y = 5, 40
- label = "%dMB/s" % round((max_sample.tput) / 1024.0)
- draw_text(ctx, label, DISK_TPUT_COLOR, pos_x + shift_x, curr_y + shift_y)
+ label = "%dMB/s %s" % (round((max_sample.tput) / 1024.0), disk)
+ draw_text(ctx, label, DISK_TPUT_COLOR, pos_x + shift_x, curr_y + shift_y)
+ #max_read = max(sample.read for sample in disk_stats[disk])
+ #if max_read > 0:
+ # draw_chart(ctx, DISK_TPUT_READ_COLOR, True, chart_rect, [(sample.time, sample.read) for sample in disk_stats[disk]], proc_tree)
+ max_write = max(sample.write for sample in disk_stats[disk])
+ if max_write > 0:
+ draw_chart(ctx, DISK_TPUT_WRITE_COLOR, True, chart_rect, [(sample.time, sample.write) for sample in disk_stats[disk]], proc_tree)
+ # render disk throughput
+ draw_chart(ctx, IO_COLOR, False, chart_rect, [(sample.time, sample.util) for sample in disk_stats[disk]], proc_tree)
+
+ curr_y = curr_y + 30 + bar_h
# draw process boxes
draw_process_bar_chart(ctx, proc_tree, times, curr_y + bar_h, w, h)
diff --git a/parsing.py b/parsing.py
index e99db90..1067a9b 100644
--- a/parsing.py
+++ b/parsing.py
@@ -138,32 +138,45 @@ def _parse_proc_disk_stat_log(file, numCpu):
not sda1, sda2 etc. The format of relevant lines should be:
{major minor name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq}
"""
- DISK_REGEX = 'hd.$|sd.$|vd.$|mmcblk.$|cciss/c0d0$'
+ DISK_REGEX = 'hd.$|sd.$|vd.$|mmcblk.+$|cciss/c0d0$'
def is_relevant_line(linetokens):
return len(linetokens) == 14 and re.match(DISK_REGEX, linetokens[2])
- disk_stat_samples = []
+ disk_stat_samples = {}
for time, lines in _parse_timed_blocks(file):
- sample = DiskStatSample(time)
+ samples = {}
relevant_tokens = [linetokens for linetokens in map(string.split,lines) if is_relevant_line(linetokens)]
for tokens in relevant_tokens:
+ ## rsect: the total number of sectors read successfully
+ ## wsect: the total number of sectors written successfully
+ ## use: millseconds spent doing I/Os
disk, rsect, wsect, use = tokens[2], int(tokens[5]), int(tokens[9]), int(tokens[12])
- sample.add_diskdata([rsect, wsect, use])
-
- disk_stat_samples.append(sample)
-
- disk_stats = []
- for sample1, sample2 in zip(disk_stat_samples[:-1], disk_stat_samples[1:]):
- interval = sample1.time - sample2.time
- 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 = max(0.0, min(1.0, util))
- disk_stats.append(DiskSample(sample2.time, readTput, writeTput, util))
+ samples[disk] = DiskStatSample(time)
+ samples[disk].add_diskdata([rsect, wsect, use])
+
+ if not disk_stat_samples.has_key(disk):
+ disk_stat_samples[disk] = []
+ disk_stat_samples[disk].append(samples[disk])
+
+ disk_stats = {}
+ disks = disk_stat_samples.keys()
+ disks.sort()
+ for disk in disks:
+ for sample1, sample2 in zip(disk_stat_samples[disk][:-1], disk_stat_samples[disk][1:]):
+ interval = sample1.time - sample2.time
+ 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
+ ##util = float( sums[2] ) / 10 / interval / numCpu
+ util = max(0.0, min(1.0, util))
+ if not disk_stats.has_key(disk):
+ disk_stats[disk] = []
+
+ disk_stats[disk].append(DiskSample(sample2.time, readTput, writeTput, util))
return disk_stats
@@ -310,7 +323,7 @@ def crop(writer, crop_after, state):
return False
cpu_util = [(sample.time, sample.user + sample.sys + sample.io) for sample in state.cpu_stats['cpu']]
- disk_util = [(sample.time, sample.util) for sample in state.disk_stats]
+ disk_util = [(sample.time, sample.util) for sample in state.disk_stats['mmcblk0']]
for i in range(0, len(cpu_util)):
if cpu_util[i][0] < proc.start_time:
@@ -328,9 +341,9 @@ def crop(writer, crop_after, state):
while len(state.cpu_stats['cpu']) \
and state.cpu_stats['cpu'][-1].time > crop:
state.cpu_stats['cpu'].pop()
- while len(state.disk_stats) \
- and state.disk_stats[-1].time > crop:
- state.disk_stats.pop()
+ while len(state.disk_stats['mmcblk0']) \
+ and state.disk_stats['mmcblk0'][-1].time > crop:
+ state.disk_stats['mmcblk0'].pop()
state.ps_stats.end_time = crop
while len(state.ps_stats.process_list) \
diff --git a/samples.py b/samples.py
index 75d3bb1..214a9fa 100644
--- a/samples.py
+++ b/samples.py
@@ -15,96 +15,96 @@
class DiskStatSample:
- def __init__(self, time):
- self.time = time
- self.diskdata = [0, 0, 0]
- def add_diskdata(self, new_diskdata):
- self.diskdata = [ a + b for a, b in zip(self.diskdata, new_diskdata) ]
+ def __init__(self, time):
+ self.time = time
+ self.diskdata = [0, 0, 0]
+ def add_diskdata(self, new_diskdata):
+ self.diskdata = [ a + b for a, b in zip(self.diskdata, new_diskdata) ]
class CPUSample:
- def __init__(self, time, user, sys, io):
- self.time = time
- self.user = user
- self.sys = sys
- self.io = io
-
- def __str__(self):
- return str(self.time) + "\t" + str(self.user) + "\t" + str(self.sys) + "\t" + str(self.io);
-
+ def __init__(self, time, user, sys, io):
+ self.time = time
+ self.user = user
+ self.sys = sys
+ self.io = io
+
+ def __str__(self):
+ return str(self.time) + "\t" + str(self.user) + "\t" + str(self.sys) + "\t" + str(self.io);
+
class ProcessSample:
- def __init__(self, time, state, cpu_sample):
- self.time = time
- self.state = state
- self.cpu_sample = cpu_sample
-
- def __str__(self):
- return str(self.time) + "\t" + str(self.state) + "\t" + str(self.cpu_sample);
+ def __init__(self, time, state, cpu_sample):
+ self.time = time
+ self.state = state
+ self.cpu_sample = cpu_sample
+
+ def __str__(self):
+ return str(self.time) + "\t" + str(self.state) + "\t" + str(self.cpu_sample);
class ProcessStats:
def __init__(self, process_list, sample_period, start_time, end_time):
- self.process_list = process_list
- self.sample_period = sample_period
- self.start_time = start_time
- self.end_time = end_time
-
-class Process:
- def __init__(self, writer, pid, cmd, ppid, start_time):
- self.writer = writer
- self.pid = pid
- self.cmd = cmd
- self.ppid = ppid
- self.start_time = start_time
- self.samples = []
- self.parent = None
- self.child_list = []
-
- self.duration = 0
- self.active = None
-
- self.last_user_cpu_time = None
- self.last_sys_cpu_time = None
-
- def __str__(self):
- return " ".join([str(self.pid), self.cmd, str(self.ppid), '[ ' + str(len(self.samples)) + ' samples ]' ])
-
- def calc_stats(self, samplePeriod):
- if self.samples:
- firstSample = self.samples[0]
- lastSample = self.samples[-1]
- self.start_time = min(firstSample.time, self.start_time)
- self.duration = lastSample.time - self.start_time + samplePeriod
-
- activeCount = sum( [1 for sample in self.samples if sample.cpu_sample and sample.cpu_sample.sys + sample.cpu_sample.user + sample.cpu_sample.io > 0.0] )
- activeCount = activeCount + sum( [1 for sample in self.samples if sample.state == 'D'] )
- self.active = (activeCount>2)
-
- def calc_load(self, userCpu, sysCpu, interval):
-
- userCpuLoad = float(userCpu - self.last_user_cpu_time) / interval
- sysCpuLoad = float(sysCpu - self.last_sys_cpu_time) / interval
- cpuLoad = userCpuLoad + sysCpuLoad
- # normalize
- if cpuLoad > 1.0:
- userCpuLoad = userCpuLoad / cpuLoad;
- sysCpuLoad = sysCpuLoad / cpuLoad;
- return (userCpuLoad, sysCpuLoad)
-
- def set_parent(self, processMap):
- if self.ppid != None:
- self.parent = processMap.get(self.ppid)
- if self.parent == None and self.pid > 1:
- self.writer.warn("warning: no parent for pid '%i' with ppid '%i'" % (self.pid,self.ppid))
- def get_end_time(self):
- return self.start_time + self.duration
+ self.process_list = process_list
+ self.sample_period = sample_period
+ self.start_time = start_time
+ self.end_time = end_time
+
+class Process:
+ def __init__(self, writer, pid, cmd, ppid, start_time):
+ self.writer = writer
+ self.pid = pid
+ self.cmd = cmd
+ self.ppid = ppid
+ self.start_time = start_time
+ self.samples = []
+ self.parent = None
+ self.child_list = []
+
+ self.duration = 0
+ self.active = None
+
+ self.last_user_cpu_time = None
+ self.last_sys_cpu_time = None
+
+ def __str__(self):
+ return " ".join([str(self.pid), self.cmd, str(self.ppid), '[ ' + str(len(self.samples)) + ' samples ]' ])
+
+ def calc_stats(self, samplePeriod):
+ if self.samples:
+ firstSample = self.samples[0]
+ lastSample = self.samples[-1]
+ self.start_time = min(firstSample.time, self.start_time)
+ self.duration = lastSample.time - self.start_time + samplePeriod
+
+ activeCount = sum( [1 for sample in self.samples if sample.cpu_sample and sample.cpu_sample.sys + sample.cpu_sample.user + sample.cpu_sample.io > 0.0] )
+ activeCount = activeCount + sum( [1 for sample in self.samples if sample.state == 'D'] )
+ self.active = (activeCount>2)
+
+ def calc_load(self, userCpu, sysCpu, interval):
+
+ userCpuLoad = float(userCpu - self.last_user_cpu_time) / interval
+ sysCpuLoad = float(sysCpu - self.last_sys_cpu_time) / interval
+ cpuLoad = userCpuLoad + sysCpuLoad
+ # normalize
+ if cpuLoad > 1.0:
+ userCpuLoad = userCpuLoad / cpuLoad;
+ sysCpuLoad = sysCpuLoad / cpuLoad;
+ return (userCpuLoad, sysCpuLoad)
+
+ def set_parent(self, processMap):
+ if self.ppid != None:
+ self.parent = processMap.get(self.ppid)
+ if self.parent == None and self.pid > 1:
+ self.writer.warn("warning: no parent for pid '%i' with ppid '%i'" % (self.pid,self.ppid))
+ def get_end_time(self):
+ return self.start_time + self.duration
class DiskSample:
- def __init__(self, time, read, write, util):
- self.time = time
- self.read = read
- self.write = write
- self.util = util
- self.tput = read + write
-
- def __str__(self):
- return "\t".join([str(self.time), str(self.read), str(self.write), str(self.util)])
+ def __init__(self, time, read, write, util):
+ self.time = time
+ self.read = read
+ self.write = write
+ self.util = util
+ self.tput = read + write
+
+ def __str__(self):
+ return "\t".join([str(self.time), str(self.read), str(self.write), str(self.util)])