summaryrefslogtreecommitdiff
path: root/util/stats
diff options
context:
space:
mode:
authorNathan Binkert <binkertn@umich.edu>2005-11-22 21:50:34 -0500
committerNathan Binkert <binkertn@umich.edu>2005-11-22 21:50:34 -0500
commitc0a4836077425e03cc39dfba88bec7da21af950b (patch)
tree1e609627fbc2cc30df64a0698fc150a7ad3e3fd3 /util/stats
parent7819ca6b97f96f1f5e5aeb66b33aa9a764e649ae (diff)
downloadgem5-c0a4836077425e03cc39dfba88bec7da21af950b.tar.xz
Major improvements in the graph output code. Mostly adding more
options, making existing options more visible and dealing with holes in data better. util/stats/barchart.py: - move the options for BarChart to a base class ChartOptions so they can be more easily set and copied. - add an option to set the chart size (so you can adjust the aspect ratio) - don't do the add_subplot thing, use add_axes directly so we can affect the size of the figure itself to make room for the legend - make the initial array bottom floating point so we don't lose precision - add an option to set the limits on the y axis - use a figure legend instead of an axes legend so we can put the legend outside of the actual chart. Also add an option to set the fontsize of the legend. - initial hack at outputting csv files util/stats/db.py: don't print out an error when the run is missing from the database just return None, the error will be print elsewhere. util/stats/output.py: - make StatOutput derive from ChartOptions so that it's easier to set default chart options. - make the various output functions (graph, display, etc.) take the name of the data as a parameter instead of making it a parameter to __init__. This allows me to create the StatOutput object with generic parameters while still being able to specialize the name after the fact - add support for graph_group and graph_bars to be applied to multiple configuration groups. This results in a cross product of the groups to be generated and used. - flush the html file output as we go so that we can load the file while graphs are still being generated. - make the proxy a parameter to the graph function so the proper system's data can be graphed - for any groups or bars that are completely missing, remove them from the graph. This way, if we decide not to do a set of runs, there won't be holes in the data. - output eps and ps by default in addition to the png. util/stats/profile.py: - clean up the data structures that are used to store the function profile information and try our best to avoid keeping extra data around that isn't used. - make get() return None if a job is missing so we know it was missing rather than the all zeroes thing. - make the function profile categorization stuff total up to 100% - Fixup the x-axis and y-axis labels. - fix the dot file output stuff. util/stats/stats.py: support the new options stuff for StatOutput --HG-- extra : convert_revision : fae35df8c57a36257ea93bc3e0a0e617edc46bb7
Diffstat (limited to 'util/stats')
-rw-r--r--util/stats/barchart.py65
-rw-r--r--util/stats/chart.py84
-rw-r--r--util/stats/db.py1
-rw-r--r--util/stats/output.py128
-rw-r--r--util/stats/profile.py116
-rwxr-xr-xutil/stats/stats.py51
6 files changed, 298 insertions, 147 deletions
diff --git a/util/stats/barchart.py b/util/stats/barchart.py
index a2cbea816..19cccb58a 100644
--- a/util/stats/barchart.py
+++ b/util/stats/barchart.py
@@ -28,28 +28,19 @@
# Lisa Hsu
import matplotlib, pylab
+from matplotlib.font_manager import FontProperties
from matplotlib.numerix import array, arange, reshape, shape, transpose, zeros
from matplotlib.numerix import Float
matplotlib.interactive(False)
-class BarChart(object):
- def __init__(self, **kwargs):
- self.init(**kwargs)
+from chart import ChartOptions
- def init(self, **kwargs):
- self.colormap = 'jet'
+class BarChart(ChartOptions):
+ def __init__(self, default=None, **kwargs):
+ super(BarChart, self).__init__(default, **kwargs)
self.inputdata = None
self.chartdata = None
- self.xlabel = None
- self.ylabel = None
- self.legend = None
- self.xticks = None
- self.yticks = None
- self.title = None
-
- for key,value in kwargs.iteritems():
- self.__setattr__(key, value)
def gen_colors(self, count):
cmap = matplotlib.cm.get_cmap(self.colormap)
@@ -129,8 +120,8 @@ class BarChart(object):
if self.chartdata is None:
raise AttributeError, "Data not set for bar chart!"
- self.figure = pylab.figure()
- self.axes = self.figure.add_subplot(111)
+ self.figure = pylab.figure(figsize=self.chart_size)
+ self.axes = self.figure.add_axes(self.figure_size)
dim = len(shape(self.inputdata))
cshape = shape(self.chartdata)
@@ -158,7 +149,7 @@ class BarChart(object):
bars = []
for i,stackdata in enumerate(self.chartdata):
- bottom = array([0] * len(stackdata[0]))
+ bottom = array([0.0] * len(stackdata[0]), Float)
stack = []
for j,bardata in enumerate(stackdata):
bardata = array(bardata)
@@ -181,6 +172,8 @@ class BarChart(object):
ticks = arange(nticks) / (nticks - 1) * (ymax - ymin) + ymin
self.axes.set_yticks(ticks)
self.axes.set_yticklabels(self.yticks)
+ elif self.ylim is not None:
+ self.axes.set_ylim(self.ylim)
if self.xticks is not None:
self.axes.set_xticks(arange(cshape[2]) + .5)
@@ -195,7 +188,8 @@ class BarChart(object):
number = len(bars[0])
lbars = [ bars[0][number - j - 1][0] for j in xrange(number)]
- self.axes.legend(lbars, self.legend, loc='best')
+ self.figure.legend(lbars, self.legend, self.legend_loc,
+ prop=FontProperties(size=self.legend_size))
if self.title is not None:
self.axes.set_title(self.title)
@@ -203,7 +197,32 @@ class BarChart(object):
def savefig(self, name):
self.figure.savefig(name)
+ def savecsv(self, name):
+ f = file(name, 'w')
+ data = array(self.inputdata)
+ dim = len(data.shape)
+
+ if dim == 1:
+ #if self.xlabel:
+ # f.write(', '.join(list(self.xlabel)) + '\n')
+ f.write(', '.join([ '%f' % val for val in data]) + '\n')
+ if dim == 2:
+ #if self.xlabel:
+ # f.write(', '.join([''] + list(self.xlabel)) + '\n')
+ for i,row in enumerate(data):
+ ylabel = []
+ #if self.ylabel:
+ # ylabel = [ self.ylabel[i] ]
+ f.write(', '.join(ylabel + [ '%f' % val for val in row]) + '\n')
+ if dim == 3:
+ f.write("don't do 3D csv files\n")
+ pass
+
+ f.close()
+
+
if __name__ == '__main__':
+ from random import randrange
import random, sys
dim = 3
@@ -234,13 +253,17 @@ if __name__ == '__main__':
chart1.xticks = [ 'xtick%d' % x for x in xrange(myshape[0]) ]
chart1.title = 'this is the title'
chart1.graph()
- #chart1.savefig('/tmp/test1.png')
+ chart1.savefig('/tmp/test1.png')
+ chart1.savefig('/tmp/test1.ps')
+ chart1.savefig('/tmp/test1.eps')
+ chart1.savecsv('/tmp/test1.csv')
if False:
chart2 = BarChart()
chart2.data = data
chart2.colormap = 'gray'
chart2.graph()
- #chart2.savefig('/tmp/test2.png')
+ chart2.savefig('/tmp/test2.png')
+ chart2.savefig('/tmp/test2.ps')
- pylab.show()
+ #pylab.show()
diff --git a/util/stats/chart.py b/util/stats/chart.py
new file mode 100644
index 000000000..1e301cb58
--- /dev/null
+++ b/util/stats/chart.py
@@ -0,0 +1,84 @@
+# Copyright (c) 2005 The Regents of The University of Michigan
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Authors: Nathan Binkert
+# Lisa Hsu
+
+class ChartOptions(object):
+ defaults = { 'chart_size' : (8, 4),
+ 'figure_size' : [0.1, 0.1, 0.6, 0.85],
+ 'title' : None,
+ 'legend' : None,
+ 'legend_loc' : 'upper right',
+ 'legend_size' : 6,
+ 'colormap' : 'jet',
+ 'xlabel' : None,
+ 'ylabel' : None,
+ 'xticks' : None,
+ 'yticks' : None,
+ 'ylim' : None,
+ }
+
+ def __init__(self, options=None, **kwargs):
+ self.init(options, **kwargs)
+
+ def clear(self):
+ self.options = {}
+
+ def init(self, options=None, **kwargs):
+ self.clear()
+ self.update(options, **kwargs)
+
+ def update(self, options=None, **kwargs):
+ if options is not None:
+ if not isinstance(options, ChartOptions):
+ raise AttributeError, \
+ 'attribute options of type %s should be %s' % \
+ (type(options), ChartOptions)
+ self.options.update(options.options)
+
+ for key,value in kwargs.iteritems():
+ if key not in ChartOptions.defaults:
+ raise AttributeError, \
+ "%s instance has no attribute '%s'" % (type(self), key)
+ self.options[key] = value
+
+ def __getattr__(self, attr):
+ if attr in self.options:
+ return self.options[attr]
+
+ if attr in ChartOptions.defaults:
+ return ChartOptions.defaults[attr]
+
+ raise AttributeError, \
+ "%s instance has no attribute '%s'" % (type(self), attr)
+
+ def __setattr__(self, attr, value):
+ if attr in ChartOptions.defaults:
+ self.options[attr] = value
+ else:
+ super(ChartOptions, self).__setattr__(attr, value)
+
diff --git a/util/stats/db.py b/util/stats/db.py
index 1ece6df88..8e57f9043 100644
--- a/util/stats/db.py
+++ b/util/stats/db.py
@@ -155,7 +155,6 @@ class Database(object):
def get(self, job, stat):
run = self.allRunNames.get(job.name, None)
if run is None:
- print 'run "%s" not found' % job
return None
from info import scalar, vector, value, values, total, len
diff --git a/util/stats/output.py b/util/stats/output.py
index cf76f291e..e67751bbc 100644
--- a/util/stats/output.py
+++ b/util/stats/output.py
@@ -26,21 +26,22 @@
#
# Authors: Nathan Binkert
-class StatOutput(object):
- def __init__(self, name, jobfile, info, stat=None, binstats=None):
- self.name = name
+from chart import ChartOptions
+
+class StatOutput(ChartOptions):
+ def __init__(self, jobfile, info, stat=None, binstats=None):
+ super(StatOutput, self).__init__()
self.jobfile = jobfile
self.stat = stat
self.binstats = None
- self.label = self.name
self.invert = False
self.info = info
- def printdata(self, bin = None, printmode = 'G'):
+ def printdata(self, name, bin = None, printmode = 'G'):
import info
if bin:
- print '%s %s stats' % (self.name, bin)
+ print '%s %s stats' % (name, bin)
if self.binstats:
for stat in self.binstats:
@@ -69,70 +70,78 @@ class StatOutput(object):
valstring = ', '.join([ valformat % val for val in value ])
print '%-50s %s' % (job.name + ':', valstring)
- def display(self, binned = False, printmode = 'G'):
+ def display(self, name, binned = False, printmode = 'G'):
if binned and self.binstats:
- self.printdata('kernel', printmode)
- self.printdata('idle', printmode)
- self.printdata('user', printmode)
- self.printdata('interrupt', printmode)
+ self.printdata(name, 'kernel', printmode)
+ self.printdata(name, 'idle', printmode)
+ self.printdata(name, 'user', printmode)
+ self.printdata(name, 'interrupt', printmode)
- print '%s total stats' % self.name
- self.printdata(printmode=printmode)
+ print '%s total stats' % name
+ self.printdata(name, printmode=printmode)
- def graph(self, graphdir):
+ def graph(self, name, graphdir, proxy=None):
from os.path import expanduser, isdir, join as joinpath
from barchart import BarChart
from matplotlib.numerix import Float, array, zeros
- import os, re
+ import os, re, urllib
+ from jobfile import crossproduct
confgroups = self.jobfile.groups()
ngroups = len(confgroups)
skiplist = [ False ] * ngroups
- groupopts = None
- baropts = None
+ groupopts = []
+ baropts = []
groups = []
for i,group in enumerate(confgroups):
if group.flags.graph_group:
- if groupopts is not None:
- raise AttributeError, \
- 'Two groups selected for graph group'
- groupopts = group.subopts()
+ groupopts.append(group.subopts())
skiplist[i] = True
elif group.flags.graph_bars:
- if baropts is not None:
- raise AttributeError, \
- 'Two groups selected for graph bars'
- baropts = group.subopts()
+ baropts.append(group.subopts())
skiplist[i] = True
else:
groups.append(group)
- if groupopts is None:
+ if not groupopts:
raise AttributeError, 'No group selected for graph group'
- if baropts is None:
+ if not baropts:
raise AttributeError, 'No group selected for graph bars'
+ groupopts = [ group for group in crossproduct(groupopts) ]
+ baropts = [ bar for bar in crossproduct(baropts) ]
+
directory = expanduser(graphdir)
if not isdir(directory):
os.mkdir(directory)
- html = file(joinpath(directory, '%s.html' % self.name), 'w')
+ html = file(joinpath(directory, '%s.html' % name), 'w')
print >>html, '<html>'
- print >>html, '<title>Graphs for %s</title>' % self.name
+ print >>html, '<title>Graphs for %s</title>' % name
print >>html, '<body>'
+ html.flush()
for options in self.jobfile.options(groups):
+ chart = BarChart(self)
+
data = zeros((len(groupopts), len(baropts)), Float)
data = [ [ None ] * len(baropts) for i in xrange(len(groupopts)) ]
enabled = False
stacked = 0
for g,gopt in enumerate(groupopts):
for b,bopt in enumerate(baropts):
- job = self.jobfile.job(options + [ gopt, bopt ])
+ job = self.jobfile.job(options + gopt + bopt)
if not job:
continue
+ if proxy:
+ import db
+ proxy.dict['system'] = self.info[job.system]
val = self.info.get(job, self.stat)
+ if val is None:
+ print 'stat "%s" for job "%s" not found' % \
+ (self.stat, job)
+
if isinstance(val, (list, tuple)):
if len(val) == 1:
val = val[0]
@@ -151,7 +160,7 @@ class StatOutput(object):
for j in xrange(len(baropts)):
val = data[i][j]
if val is None:
- data[i][j] = [] * stacked
+ data[i][j] = [ 0.0 ] * stacked
elif len(val) != stacked:
raise ValueError, "some stats stacked, some not"
@@ -159,29 +168,52 @@ class StatOutput(object):
if data.sum() == 0:
continue
- bar_descs = [ opt.desc for opt in baropts ]
- group_descs = [ opt.desc for opt in groupopts ]
- if stacked:
- try:
- legend = self.info.rcategories
- except:
- legend = [ str(i) for i in xrange(stacked) ]
- else:
- legend = bar_descs
-
- chart = BarChart(data=data, xlabel='Benchmark', ylabel=self.label,
- legend=legend, xticks=group_descs)
+ x = data.shape[0]
+ y = data.shape[1]
+ xkeep = [ i for i in xrange(x) if data[i].sum() != 0 ]
+ ykeep = [ i for i in xrange(y) if data[:,i].sum() != 0 ]
+ data = data.take(xkeep, axis=0)
+ data = data.take(ykeep, axis=1)
+ chart.data = data
+
+ gopts = [ groupopts[i] for i in xkeep ]
+ bopts = [ baropts[i] for i in ykeep ]
+
+ bdescs = [ ' '.join([o.desc for o in opt]) for opt in bopts]
+ gdescs = [ ' '.join([o.desc for o in opt]) for opt in gopts]
+
+ if chart.legend is None:
+ if stacked:
+ try:
+ chart.legend = self.info.rcategories
+ except:
+ chart.legend = [ str(i) for i in xrange(stacked) ]
+ else:
+ chart.legend = bdescs
+
+ if chart.xticks is None:
+ chart.xticks = gdescs
chart.graph()
names = [ opt.name for opt in options ]
descs = [ opt.desc for opt in options ]
- filename = '%s-%s.png' % (self.name, ':'.join(names))
+ if names[0] == 'run':
+ names = names[1:]
+ descs = descs[1:]
+
+ basename = '%s-%s' % (name, ':'.join(names))
desc = ' '.join(descs)
- filepath = joinpath(directory, filename)
- chart.savefig(filepath)
- filename = re.sub(':', '%3A', filename)
- print >>html, '''%s<br><img src="%s"><br>''' % (desc, filename)
+
+ pngname = '%s.png' % basename
+ psname = '%s.eps' % re.sub(':', '-', basename)
+ epsname = '%s.ps' % re.sub(':', '-', basename)
+ chart.savefig(joinpath(directory, pngname))
+ chart.savefig(joinpath(directory, epsname))
+ chart.savefig(joinpath(directory, psname))
+ html_name = urllib.quote(pngname)
+ print >>html, '''%s<br><img src="%s"><br>''' % (desc, html_name)
+ html.flush()
print >>html, '</body>'
print >>html, '</html>'
diff --git a/util/stats/profile.py b/util/stats/profile.py
index 0f51d643e..151170280 100644
--- a/util/stats/profile.py
+++ b/util/stats/profile.py
@@ -27,19 +27,39 @@
from orderdict import orderdict
import output
+class FileData(dict):
+ def __init__(self, filename):
+ self.filename = filename
+ fd = file(filename)
+ current = []
+ for line in fd:
+ line = line.strip()
+ if line.startswith('>>>'):
+ current = []
+ self[line[3:]] = current
+ else:
+ current.append(line)
+ fd.close()
+
class RunData(dict):
- def __init__(self, filename=None):
+ def __init__(self, filename):
self.filename = filename
- def __getattr__(self, attr):
+ def __getattribute__(self, attr):
if attr == 'total':
total = 0.0
for value in self.itervalues():
total += value
return total
+
+ if attr == 'filedata':
+ return FileData(self.filename)
+
if attr == 'maxsymlen':
return max([ len(sym) for sym in self.iterkeys() ])
+ return super(RunData, self).__getattribute__(attr)
+
def display(self, output=None, limit=None, maxsymlen=None):
if not output:
import sys
@@ -62,24 +82,12 @@ class RunData(dict):
for number,name in symbols:
print >>output, symbolf % (name, 100.0 * (float(number) / total))
-
-
class PCData(RunData):
def __init__(self, filename=None, categorize=None, showidle=True):
super(PCData, self).__init__(self, filename)
- if filename is None:
- return
-
- fd = file(filename)
-
- for line in fd:
- if line.strip() == '>>>PC data':
- break
-
- for line in fd:
- if line.startswith('>>>'):
- break
+ filedata = self.filedata['PC data']
+ for line in filedata:
(symbol, count) = line.split()
if symbol == "0x0":
continue
@@ -94,30 +102,21 @@ class PCData(RunData):
self[category] = count
- fd.close()
-
class FuncNode(object):
- def __new__(cls, filename = None):
- if filename is None:
+ def __new__(cls, filedata=None):
+ if filedata is None:
return super(FuncNode, cls).__new__(cls)
- fd = file(filename, 'r')
- fditer = iter(fd)
nodes = {}
- for line in fditer:
- if line.strip() == '>>>function data':
- break
-
- for line in fditer:
- if line.startswith('>>>'):
- break
-
- data = line.split()
- node_id = int(data[0], 16)
+ for line in filedata['function data']:
+ data = line.split(' ')
+ node_id = long(data[0], 16)
node = FuncNode()
node.symbol = data[1]
- node.count = int(data[2])
- node.children = [ int(child, 16) for child in data[3:] ]
+ if node.symbol == '':
+ node.symbol = 'unknown'
+ node.count = long(data[2])
+ node.children = [ long(child, 16) for child in data[3:] ]
nodes[node_id] = node
for node in nodes.itervalues():
@@ -128,13 +127,10 @@ class FuncNode(object):
child.parent = node
node.children = tuple(children)
if not nodes:
- print filename
+ print filedata.filename
print nodes
return nodes[0]
- def __init__(self, filename=None):
- pass
-
def total(self):
total = self.count
for child in self.children:
@@ -198,9 +194,14 @@ class FuncNode(object):
class FuncData(RunData):
def __init__(self, filename, categorize=None):
super(FuncData, self).__init__(filename)
- self.tree = FuncNode(filename)
- self.tree.aggregate(self, categorize, incategory=False)
- self.total = self.tree.total()
+ tree = self.tree
+ tree.aggregate(self, categorize, incategory=False)
+ self.total = tree.total()
+
+ def __getattribute__(self, attr):
+ if attr == 'tree':
+ return FuncNode(self.filedata)
+ return super(FuncData, self).__getattribute__(attr)
def displayx(self, output=None, maxcount=None):
if output is None:
@@ -274,6 +275,7 @@ class Profile(object):
try:
return self.data[run][cpu]
except KeyError:
+ print run, cpu
return None
def alldata(self):
@@ -289,12 +291,16 @@ class Profile(object):
cpu = '%s.run%d' % (job.system, self.cpu)
data = self.getdata(run, cpu)
if not data:
- return [ 0.0 for c in self.categories ]
+ return None
values = []
for category in self.categories:
- values.append(data.get(category, 0.0))
- return values
+ val = float(data.get(category, 0.0))
+ if val < 0.0:
+ raise ValueError, 'value is %f' % val
+ values.append(val)
+ total = sum(values)
+ return [ v / total * 100.0 for v in values ]
def dump(self):
for run,cpu,data in self.alldata():
@@ -382,7 +388,6 @@ if __name__ == '__main__':
import getopt, re, sys
from os.path import expanduser
from output import StatOutput
- from jobfile import JobFile
# default option values
numsyms = 10
@@ -393,7 +398,7 @@ if __name__ == '__main__':
funcdata = True
jobfilename = 'Test.py'
dodot = False
- dotformat = 'raw'
+ dotfile = None
textout = False
threshold = 0.01
inputfile = None
@@ -409,7 +414,7 @@ if __name__ == '__main__':
elif o == '-c':
categorize = True
elif o == '-D':
- dotformat = a
+ dotfile = a
elif o == '-d':
dodot = True
elif o == '-f':
@@ -434,20 +439,24 @@ if __name__ == '__main__':
usage(1)
if inputfile:
- data = FuncData(inputfile)
+ catfunc = None
+ if categorize:
+ catfunc = func_categorize
+ data = FuncData(inputfile, categorize=catfunc)
if dodot:
import pydot
dot = pydot.Dot()
- data.dot(dot, threshold=threshold)
+ data.tree.dot(dot, threshold=threshold)
#dot.orientation = 'landscape'
#dot.ranksep='equally'
#dot.rank='samerank'
- dot.write(dotfile, format=dotformat)
+ dot.write(dotfile, format='png')
else:
data.display(limit=numsyms)
else:
+ from jobfile import JobFile
jobfile = JobFile(jobfilename)
if funcdata:
@@ -466,8 +475,11 @@ if __name__ == '__main__':
name = 'funcstacks%d' % cpu
else:
name = 'stacks%d' % cpu
- output = StatOutput(name, jobfile, info=profile)
- output.graph(graph)
+ output = StatOutput(jobfile, info=profile)
+ output.xlabel = 'System Configuration'
+ output.ylabel = '% CPU utilization'
+ output.stat = name
+ output.graph(name, graph)
if dodot:
for cpu in cpus:
diff --git a/util/stats/stats.py b/util/stats/stats.py
index 2a24bf3fd..b75d9fec0 100755
--- a/util/stats/stats.py
+++ b/util/stats/stats.py
@@ -259,14 +259,9 @@ def commands(options, command, args):
print 'only displaying sample %s' % options.ticks
source.ticks = [ int(x) for x in options.ticks.split() ]
- import output
-
- def display():
- if options.graph:
- output.graph(options.graphdir)
- else:
- output.display(options.binned, options.printmode)
-
+ from output import StatOutput
+ output = StatOutput(options.jobfile, source)
+ output.xlabel = 'System Configuration'
if command == 'stat' or command == 'formula':
if len(args) != 1:
@@ -278,17 +273,18 @@ def commands(options, command, args):
stats = eval(args[0])
for stat in stats:
- output = output.StatOutput(stat.name, options.jobfile, source)
output.stat = stat
- output.label = stat.name
- display()
+ output.ylabel = stat.name
+ if options.graph:
+ output.graph(stat.name, options.graphdir)
+ else:
+ output.display(stat.name, options.binned, options.printmode)
return
if len(args):
raise CommandException
- system = source.__dict__[options.system]
from info import ProxyGroup
sim_seconds = source['sim_seconds']
proxy = ProxyGroup(system = source[options.system])
@@ -300,7 +296,11 @@ def commands(options, command, args):
packets = etherdev.rxPackets + etherdev.txPackets
bps = etherdev.rxBandwidth + etherdev.txBandwidth
- output = output.StatOutput(command, options.jobfile, source)
+ def display():
+ if options.graph:
+ output.graph(command, options.graphdir, proxy)
+ else:
+ output.display(command, options.binned, options.printmode)
if command == 'usertime':
import copy
@@ -308,7 +308,7 @@ def commands(options, command, args):
user.bins = 'user'
output.stat = user / system.run0.numCycles
- output.label = 'User Fraction'
+ output.ylabel = 'User Fraction'
display()
return
@@ -338,13 +338,13 @@ def commands(options, command, args):
if command == 'pps':
output.stat = packets / sim_seconds
- output.label = 'Packets/s'
+ output.ylabel = 'Packets/s'
display()
return
if command == 'bpt' or command == 'tpb':
output.stat = bytes / system.run0.numCycles * 8
- output.label = 'bps / Hz'
+ output.ylabel = 'bps / Hz'
output.invert = command == 'tpb'
display()
return
@@ -357,37 +357,38 @@ def commands(options, command, args):
if command == 'bps':
output.stat = bps / 1e9
- output.label = 'Bandwidth (Gbps)'
+ output.ylabel = 'Bandwidth (Gbps)'
+ output.ylim = [ 0.0, 10.0 ]
display()
return
if command == 'bpp':
output.stat = bytes / packets
- output.label = 'Bytes / Packet'
+ output.ylabel = 'Bytes / Packet'
display()
return
if command == 'rxbpp':
output.stat = etherdev.rxBytes / etherdev.rxPackets
- output.label = 'Receive Bytes / Packet'
+ output.ylabel = 'Receive Bytes / Packet'
display()
return
if command == 'txbpp':
output.stat = etherdev.txBytes / etherdev.txPackets
- output.label = 'Transmit Bytes / Packet'
+ output.ylabel = 'Transmit Bytes / Packet'
display()
return
if command == 'rtp':
output.stat = etherdev.rxPackets / etherdev.txPackets
- output.label = 'rxPackets / txPackets'
+ output.ylabel = 'rxPackets / txPackets'
display()
return
if command == 'rtb':
output.stat = etherdev.rxBytes / etherdev.txBytes
- output.label = 'rxBytes / txBytes'
+ output.ylabel = 'rxBytes / txBytes'
display()
return
@@ -395,14 +396,14 @@ def commands(options, command, args):
if command == 'misses':
output.stat = misses
- output.label = 'Overall MSHR Misses'
+ output.ylabel = 'Overall MSHR Misses'
display()
return
if command == 'mpkb':
output.stat = misses / (bytes / 1024)
output.binstats = [ misses ]
- output.label = 'Misses / KB'
+ output.ylabel = 'Misses / KB'
display()
return
@@ -410,7 +411,7 @@ def commands(options, command, args):
interrupts = system.run0.kern.faults[4]
output.stat = interrupts / kbytes
output.binstats = [ interrupts ]
- output.label = 'Interrupts / KB'
+ output.ylabel = 'Interrupts / KB'
display()
return