diff options
Diffstat (limited to 'tests/sdcard/plot_sdcard.py')
-rwxr-xr-x | tests/sdcard/plot_sdcard.py | 295 |
1 files changed, 213 insertions, 82 deletions
diff --git a/tests/sdcard/plot_sdcard.py b/tests/sdcard/plot_sdcard.py index 10ee00ba..19b83c3c 100755 --- a/tests/sdcard/plot_sdcard.py +++ b/tests/sdcard/plot_sdcard.py @@ -1,49 +1,70 @@ #!/usr/bin/python2.5 # -# Copyright 2009 Google Inc. All Rights Reserved. +# Copyright 2009, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """plot_sdcard: A module to plot the results of an sdcard perf test. Requires Gnuplot python v 1.8 Typical usage: + -t x axis is time + -i x axis is iteration + -p profile data generated by profile_sdcard.sh + +./plot_sdcard.py -t /tmp/data.txt +./plot_sdcard.py -i /tmp/data.txt +./plot_sdcard.py -p -python +python interpreter >>> import plot_sdcard as p ->>> (metadata, data) = p.parse('/tmp/data.txt') ->>> p.plotIterations(metadata, data) ->>> p.plotTimes(metadata, data) +>>> (metadata, data) = p.Parse('/tmp/data.txt') +>>> p.PlotIterations(metadata, data) +>>> p.PlotTimes(metadata, data) """ -#TODO: provide a main so we can pipe the result from the run -#TODO: more comments... - -import Gnuplot -from numpy import * -import sys -import re +import getopt from itertools import izip +import re +import sys +import Gnuplot +import numpy + class DataSet(object): + """Dataset holds the summary and data (time,value pairs).""" + def __init__(self, line): - res = re.search('# StopWatch ([\w]+) total/cumulative duration ([0-9.]+)\. Samples: ([0-9]+)', line) + res = re.search(('# StopWatch ([\w]+) total/cumulative ' + 'duration ([0-9.]+). Samples: ([0-9]+)'), line) self.time = [] self.data = [] self.name = res.group(1) self.duration = float(res.group(2)) self.iteration = int(res.group(3)) - print "Name: %s Duration: %f Iterations: %d" % (self.name, self.duration, self.iteration) self.summary = re.match('([a-z_]+)_total', self.name) def __repr__(self): return str(zip(self.time, self.data)) - def add(self, time, value): + def Add(self, time, value): self.time.append(time) self.data.append(value) - def rescaleTo(self, length): + def RescaleTo(self, length): factor = len(self.data) / length if factor > 1: @@ -51,7 +72,7 @@ class DataSet(object): new_data = [] accum = 0.0 idx = 1 - for t,d in izip(self.time, self.data): + for t, d in izip(self.time, self.data): accum += d if idx % factor == 0: new_time.append(t) @@ -73,7 +94,7 @@ class Metadata(object): self.duration = 0.0 self.complete = False - def parse(self, line): + def Parse(self, line): if line.startswith('# Kernel:'): self.kernel = re.search('Linux version ([0-9.]+-[0-9]+)', line).group(1) elif line.startswith('# Command:'): @@ -84,74 +105,29 @@ class Metadata(object): elif line.startswith('# Iterations'): self.iterations = int(re.search('# Iterations: ([0-9]+)', line).group(1)) elif line.startswith('# Fadvise'): - self.fadvise = int(re.search('# Fadvise: ([\w]+)', line).group(1)) - elif line.startswith("# Sched"): + self.fadvise = re.search('# Fadvise: ([\w]+)', line).group(1) + elif line.startswith('# Sched'): self.sched = re.search('# Sched features: ([\w]+)', line).group(1) self.complete = True - def asTitle(self): - return "%s-duration:%f\\n-%s\\n%s" % (self.kernel, self.duration, self.command_line, self.sched) + def AsTitle(self): + return '%s-duration:%f\\n-%s\\n%s' % ( + self.kernel, self.duration, self.command_line, self.sched) - def updateWith(self, dataset): + def UpdateWith(self, dataset): self.duration = max(self.duration, dataset.duration) self.name = dataset.name -def plotIterations(metadata, data): - gp = Gnuplot.Gnuplot(persist = 1) - gp('set data style lines') - gp.clear() - gp.xlabel("iterations") - gp.ylabel("duration in second") - gp.title(metadata.asTitle()) - styles = {} - line_style = 1 - - for dataset in data: - dataset.rescaleTo(metadata.iterations) - x = arange(len(dataset.data), dtype='int_') - if not dataset.name in styles: - styles[dataset.name] = line_style - line_style += 1 - d = Gnuplot.Data(x, dataset.data, - title=dataset.name, - with_='lines ls %d' % styles[dataset.name]) - else: # no need to repeat a title that exists already. - d = Gnuplot.Data(x, dataset.data, - with_='lines ls %d' % styles[dataset.name]) - - gp.replot(d) - gp.hardcopy('/tmp/%s-%s-%f.png' % (metadata.name, metadata.kernel, metadata.duration), terminal='png') - -def plotTimes(metadata, data): - gp = Gnuplot.Gnuplot(persist = 1) - gp('set data style impulses') - gp('set xtics 1') - gp.clear() - gp.xlabel("seconds") - gp.ylabel("duration in second") - gp.title(metadata.asTitle()) - styles = {} - line_style = 1 - - for dataset in data: - #dataset.rescaleTo(metadata.iterations) - x = array(dataset.time, dtype='float_') - if not dataset.name in styles: - styles[dataset.name] = line_style - line_style += 1 - d = Gnuplot.Data(x, dataset.data, - title=dataset.name, - with_='impulses ls %d' % styles[dataset.name]) - else: # no need to repeat a title that exists already. - d = Gnuplot.Data(x, dataset.data, - with_='impulses ls %d' % styles[dataset.name]) +def Parse(filename): + """Parse a file with the collected data. - gp.replot(d) - gp.hardcopy('/tmp/%s-%s-%f.png' % (metadata.name, metadata.kernel, metadata.duration), terminal='png') + The data must be in 2 rows (x,y). + Args: + filename: Full path to the file. + """ -def parse(filename): f = open(filename, 'r') metadata = Metadata() @@ -164,16 +140,16 @@ def parse(filename): if not line: continue if not metadata.complete: - metadata.parse(line) + metadata.Parse(line) continue if re.match('[a-z_]', line): continue - if line.startswith('# StopWatch'): # Start of a new dataset + if line.startswith('# StopWatch'): # Start of a new dataset if dataset: if dataset.summary: - metadata.updateWith(dataset) + metadata.UpdateWith(dataset) else: data.append(dataset) @@ -187,14 +163,169 @@ def parse(filename): try: (time, value) = line.split(None, 1) except ValueError: - print "skipping line %d: %s" % (num, line) + print 'skipping line %d: %s' % (num, line) continue if dataset and not dataset.summary: - dataset.add(float(time), float(value)) + dataset.Add(float(time), float(value)) - except Exception, e: - print "Error parsing line %d" % num, sys.exc_info()[0] + except Exception: + print 'Error parsing line %d' % num, sys.exc_info()[0] raise data.append(dataset) + if not metadata.complete: + print """Error missing metadata. Did you mount debugfs? + [adb shell mount -t debugfs none /sys/kernel/debug]""" + sys.exit(1) return (metadata, data) + + +def PlotIterations(metadata, data): + """Plot the duration of the ops against iteration. + + If you are plotting data with widely different runtimes you probably want to + use PlotTimes instead. + + For instance when readers and writers are in the same mix, the + readers will go thru 100 iterations much faster than the + writers. The load test tries to be smart about that but the final + iterations of the writers will likely be done w/o any influence from + the readers. + + Args: + metadata: For the graph's title. + data: pair of to be plotted. + """ + + gp = Gnuplot.Gnuplot(persist=1) + gp('set data style lines') + gp.clear() + gp.xlabel('iterations') + gp.ylabel('duration in second') + gp.title(metadata.AsTitle()) + styles = {} + line_style = 1 + + for dataset in data: + dataset.RescaleTo(metadata.iterations) + x = numpy.arange(len(dataset.data), dtype='int_') + if not dataset.name in styles: + styles[dataset.name] = line_style + line_style += 1 + d = Gnuplot.Data(x, dataset.data, + title=dataset.name, + with_='lines ls %d' % styles[dataset.name]) + else: # no need to repeat a title that exists already. + d = Gnuplot.Data(x, dataset.data, + with_='lines ls %d' % styles[dataset.name]) + + gp.replot(d) + gp.hardcopy('/tmp/%s-%s-%f.png' % + (metadata.name, metadata.kernel, metadata.duration), + terminal='png') + + +def PlotTimes(metadata, data): + """Plot the duration of the ops against time elapsed. + + Args: + metadata: For the graph's title. + data: pair of to be plotted. + """ + + gp = Gnuplot.Gnuplot(persist=1) + gp('set data style impulses') + gp('set xtics 1') + gp.clear() + gp.xlabel('seconds') + gp.ylabel('duration in second') + gp.title(metadata.AsTitle()) + styles = {} + line_style = 1 + + for dataset in data: + x = numpy.array(dataset.time, dtype='float_') + if not dataset.name in styles: + styles[dataset.name] = line_style + line_style += 1 + d = Gnuplot.Data(x, dataset.data, + title=dataset.name, + with_='impulses ls %d' % styles[dataset.name]) + else: # no need to repeat a title that exists already. + d = Gnuplot.Data(x, dataset.data, + with_='impulses ls %d' % styles[dataset.name]) + + gp.replot(d) + gp.hardcopy('/tmp/%s-%s-%f.png' % + (metadata.name, metadata.kernel, metadata.duration), + terminal='png') + + +def PlotProfile(): + """Plot the time of a run against the number of processes.""" + (metadata, data) = Parse('/tmp/sdcard-scalability.txt') + gp = Gnuplot.Gnuplot(persist=1) + gp('set data style impulses') + gp('set xtics 1') + gp('set pointsize 2') + gp.clear() + gp.xlabel('writer process') + gp.ylabel('duration in second') + gp.title(metadata.AsTitle()) + + dataset = data[0] + x = numpy.array(dataset.time, dtype='int_') + d = Gnuplot.Data(x, dataset.data, + title=dataset.name, + with_='linespoints') + gp.replot(d) + gp.hardcopy('/tmp/%s-%s-%f.png' % + (metadata.name, metadata.kernel, metadata.duration), + terminal='png') + + +def Usage(): + """Print this module's usage.""" + print """ + To plot the result using the iter number of the x axis: + + plot_sdcard.py -i /tmp/data.txt + + To plot the result using time for the x axis: + + plot_sdcard.py -t /tmp/data.txt + + To plot the result from the profiler: + + profile_sdcard.sh + plot_sdcard.py -p + + """ + sys.exit(2) + + +def main(argv): + try: + (optlist, args) = getopt.getopt(argv[1:], + 'itp', ['iteration', 'time', 'profile']) + except getopt.GetoptError, err: + print str(err) + Usage() + + for flag, val in optlist: + if flag in ('-i', '--iteration'): + (metadata, data) = Parse(args[0]) + PlotIterations(metadata, data) + sys.exit(0) + elif flag in ('-t', '--time'): + (metadata, data) = Parse(args[0]) + PlotTimes(metadata, data) + sys.exit(0) + elif flag in ('-p', '--profile'): + PlotProfile() + sys.exit(0) + Usage() + + +if __name__ == '__main__': + main(sys.argv) |