plot_sdcard.py revision 39c016f875b793296a121f41de5775b88f6fa1c9
1#!/usr/bin/python2.5 2# 3# Copyright 2009 Google Inc. All Rights Reserved. 4 5"""plot_sdcard: A module to plot the results of an sdcard perf test. 6 7Requires Gnuplot python v 1.8 8 9Typical usage: 10 11python 12>>> import plot_sdcard as p 13>>> (metadata, data) = p.parse('/tmp/data.txt') 14>>> p.plotIterations(metadata, data) 15>>> p.plotTimes(metadata, data) 16 17""" 18 19#TODO: provide a main so we can pipe the result from the run 20#TODO: more comments... 21 22import Gnuplot 23from numpy import * 24import sys 25import re 26from itertools import izip 27 28class DataSet(object): 29 def __init__(self, line): 30 res = re.search('# StopWatch ([\w]+) total/cumulative duration ([0-9.]+)\. Samples: ([0-9]+)', line) 31 self.time = [] 32 self.data = [] 33 self.name = res.group(1) 34 self.duration = float(res.group(2)) 35 self.iteration = int(res.group(3)) 36 print "Name: %s Duration: %f Iterations: %d" % (self.name, self.duration, self.iteration) 37 self.summary = re.match('([a-z_]+)_total', self.name) 38 39 def __repr__(self): 40 return str(zip(self.time, self.data)) 41 42 def add(self, time, value): 43 self.time.append(time) 44 self.data.append(value) 45 46 def rescaleTo(self, length): 47 factor = len(self.data) / length 48 49 if factor > 1: 50 new_time = [] 51 new_data = [] 52 accum = 0.0 53 idx = 1 54 for t,d in izip(self.time, self.data): 55 accum += d 56 if idx % factor == 0: 57 new_time.append(t) 58 new_data.append(accum / factor) 59 accum = 0 60 idx += 1 61 self.time = new_time 62 self.data = new_data 63 64 65class Metadata(object): 66 def __init__(self): 67 self.kernel = '' 68 self.command_line = '' 69 self.sched = '' 70 self.name = '' 71 self.fadvise = '' 72 self.iterations = 0 73 self.duration = 0.0 74 self.complete = False 75 76 def parse(self, line): 77 if line.startswith('# Kernel:'): 78 self.kernel = re.search('Linux version ([0-9.]+-[0-9]+)', line).group(1) 79 elif line.startswith('# Command:'): 80 self.command_line = re.search('# Command: [/\w_]+ (.*)', line).group(1) 81 self.command_line = self.command_line.replace(' --', '-') 82 self.command_line = self.command_line.replace(' -d', '') 83 self.command_line = self.command_line.replace('--test=', '') 84 elif line.startswith('# Iterations'): 85 self.iterations = int(re.search('# Iterations: ([0-9]+)', line).group(1)) 86 elif line.startswith('# Fadvise'): 87 self.fadvise = int(re.search('# Fadvise: ([\w]+)', line).group(1)) 88 elif line.startswith("# Sched"): 89 self.sched = re.search('# Sched features: ([\w]+)', line).group(1) 90 self.complete = True 91 92 def asTitle(self): 93 return "%s-duration:%f\\n-%s\\n%s" % (self.kernel, self.duration, self.command_line, self.sched) 94 95 def updateWith(self, dataset): 96 self.duration = max(self.duration, dataset.duration) 97 self.name = dataset.name 98 99 100def plotIterations(metadata, data): 101 gp = Gnuplot.Gnuplot(persist = 1) 102 gp('set data style lines') 103 gp.clear() 104 gp.xlabel("iterations") 105 gp.ylabel("duration in second") 106 gp.title(metadata.asTitle()) 107 styles = {} 108 line_style = 1 109 110 for dataset in data: 111 dataset.rescaleTo(metadata.iterations) 112 x = arange(len(dataset.data), dtype='int_') 113 if not dataset.name in styles: 114 styles[dataset.name] = line_style 115 line_style += 1 116 d = Gnuplot.Data(x, dataset.data, 117 title=dataset.name, 118 with_='lines ls %d' % styles[dataset.name]) 119 else: # no need to repeat a title that exists already. 120 d = Gnuplot.Data(x, dataset.data, 121 with_='lines ls %d' % styles[dataset.name]) 122 123 gp.replot(d) 124 gp.hardcopy('/tmp/%s-%s-%f.png' % (metadata.name, metadata.kernel, metadata.duration), terminal='png') 125 126def plotTimes(metadata, data): 127 gp = Gnuplot.Gnuplot(persist = 1) 128 gp('set data style impulses') 129 gp('set xtics 1') 130 gp.clear() 131 gp.xlabel("seconds") 132 gp.ylabel("duration in second") 133 gp.title(metadata.asTitle()) 134 styles = {} 135 line_style = 1 136 137 for dataset in data: 138 #dataset.rescaleTo(metadata.iterations) 139 x = array(dataset.time, dtype='float_') 140 if not dataset.name in styles: 141 styles[dataset.name] = line_style 142 line_style += 1 143 d = Gnuplot.Data(x, dataset.data, 144 title=dataset.name, 145 with_='impulses ls %d' % styles[dataset.name]) 146 else: # no need to repeat a title that exists already. 147 d = Gnuplot.Data(x, dataset.data, 148 with_='impulses ls %d' % styles[dataset.name]) 149 150 gp.replot(d) 151 gp.hardcopy('/tmp/%s-%s-%f.png' % (metadata.name, metadata.kernel, metadata.duration), terminal='png') 152 153 154def parse(filename): 155 f = open(filename, 'r') 156 157 metadata = Metadata() 158 data = [] # array of dataset 159 dataset = None 160 161 for num, line in enumerate(f): 162 try: 163 line = line.strip() 164 if not line: continue 165 166 if not metadata.complete: 167 metadata.parse(line) 168 continue 169 170 if re.match('[a-z_]', line): 171 continue 172 173 if line.startswith('# StopWatch'): # Start of a new dataset 174 if dataset: 175 if dataset.summary: 176 metadata.updateWith(dataset) 177 else: 178 data.append(dataset) 179 180 dataset = DataSet(line) 181 continue 182 183 if line.startswith('#'): 184 continue 185 186 # must be data at this stage 187 try: 188 (time, value) = line.split(None, 1) 189 except ValueError: 190 print "skipping line %d: %s" % (num, line) 191 continue 192 193 if dataset and not dataset.summary: 194 dataset.add(float(time), float(value)) 195 196 except Exception, e: 197 print "Error parsing line %d" % num, sys.exc_info()[0] 198 raise 199 data.append(dataset) 200 return (metadata, data) 201