diff options
author | Yongqin Liu <yongqin.liu@linaro.org> | 2017-03-31 22:19:30 +0800 |
---|---|---|
committer | Yongqin Liu <yongqin.liu@linaro.org> | 2017-03-31 22:19:30 +0800 |
commit | ee302247199b720dafc1cacd8d4585331ebd0e67 (patch) | |
tree | b1c349bc8cc0c05cf9ac356349ecbd38a3bd17a8 | |
parent | 417f77884dd0ba2d8196d38ff128578c5b18dd7a (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.py | 54 | ||||
-rw-r--r-- | parsing.py | 53 | ||||
-rw-r--r-- | samples.py | 168 |
3 files changed, 154 insertions, 121 deletions
@@ -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) @@ -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) \ @@ -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)]) |