# 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. from orderdict import orderdict import output class RunData(dict): def __init__(self, filename=None): self.filename = filename def __getattr__(self, attr): if attr == 'total': total = 0.0 for value in self.itervalues(): total += value return total if attr == 'maxsymlen': return max([ len(sym) for sym in self.iterkeys() ]) def display(self, output=None, limit=None, maxsymlen=None): if not output: import sys output = sys.stdout elif isinstance(output, str): output = file(output, 'w') total = float(self.total) # swap (string,count) order so we can sort on count symbols = [ (count,name) for name,count in self.iteritems() ] symbols.sort(reverse=True) if limit is not None: symbols = symbols[:limit] if not maxsymlen: maxsymlen = self.maxsymlen symbolf = "%-" + str(maxsymlen + 1) + "s %.2f%%" 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 (symbol, count) = line.split() if symbol == "0x0": continue count = int(count) if categorize is not None: category = categorize(symbol) if category is None: category = 'other' elif category == 'idle' and not showidle: continue self[category] = count fd.close() class FuncNode(object): def __new__(cls, filename = None): if filename 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) node = FuncNode() node.symbol = data[1] node.count = int(data[2]) node.children = [ int(child, 16) for child in data[3:] ] nodes[node_id] = node for node in nodes.itervalues(): children = [] for cid in node.children: child = nodes[cid] children.append(child) child.parent = node node.children = tuple(children) if not nodes: print filename print nodes return nodes[0] def __init__(self, filename=None): pass def total(self): total = self.count for child in self.children: total += child.total() return total def aggregate(self, dict, categorize, incategory): category = None if categorize: category = categorize(self.symbol) total = self.count for child in self.children: total += child.aggregate(dict, categorize, category or incategory) if category: dict[category] = dict.get(category, 0) + total return 0 elif not incategory: dict[self.symbol] = dict.get(self.symbol, 0) + total return total def dump(self): kids = [ child.symbol for child in self.children] print '%s %d <%s>' % (self.symbol, self.count, ', '.join(kids)) for child in self.children: child.dump() def _dot(self, dot, threshold, categorize, total): from pydot import Dot, Edge, Node self.dot_node = None value = self.total() * 100.0 / total if value < threshold: return if categorize: category = categorize(self.symbol) if category and category != 'other': return label = '%s %.2f%%' % (self.symbol, value) self.dot_node = Node(self, label=label) dot.add_node(self.dot_node) for child in self.children: child._dot(dot, threshold, categorize, total) if child.dot_node is not None: dot.add_edge(Edge(self, child)) def _cleandot(self): for child in self.children: child._cleandot() self.dot_node = None del self.__dict__['dot_node'] def dot(self, dot, threshold=0.1, categorize=None): self._dot(dot, threshold, categorize, self.total()) self._cleandot() 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() def displayx(self, output=None, maxcount=None): if output is None: import sys output = sys.stdout items = [ (val,key) for key,val in self.iteritems() ] items.sort(reverse=True) for val,key in items: if maxcount is not None: if maxcount == 0: return maxcount -= 1 percent = val * 100.0 / self.total print >>output, '%-30s %8s' % (key, '%3.2f%%' % percent) class Profile(object): # This list controls the order of values in stacked bar data output default_categories = [ 'interrupt', 'driver', 'stack', 'buffer', 'copy', 'syscall', 'user', 'other', 'idle'] def __init__(self, datatype, categorize=None): categories = Profile.default_categories self.datatype = datatype self.categorize = categorize self.data = {} self.categories = categories[:] self.rcategories = categories[:] self.rcategories.reverse() self.cpu = 0 # Read in files def inputdir(self, directory): import os, os.path, re from os.path import expanduser, join as joinpath directory = expanduser(directory) label_ex = re.compile(r'profile\.(.*).dat') for root,dirs,files in os.walk(directory): for name in files: match = label_ex.match(name) if not match: continue filename = joinpath(root, name) prefix = os.path.commonprefix([root, directory]) dirname = root[len(prefix)+1:] data = self.datatype(filename, self.categorize) self.setdata(dirname, match.group(1), data) def setdata(self, run, cpu, data): if run not in self.data: self.data[run] = {} if cpu in self.data[run]: raise AttributeError, \ 'data already stored for run %s and cpu %s' % (run, cpu) self.data[run][cpu] = data def getdata(self, run, cpu): try: return self.data[run][cpu] except KeyError: return None def alldata(self): for run,cpus in self.data.iteritems(): for cpu,data in cpus.iteritems(): yield run,cpu,data def get(self, job, stat): if job.system is None: raise AttributeError, 'The job must have a system set' data = self.getdata(job.name, '%s.full%d' % (job.system, self.cpu)) if not data: return [ 0.0 for c in self.categories ] values = [] for category in self.categories: values.append(data.get(category, 0.0)) return values def dump(self): for run,cpu,data in self.alldata(): print 'run %s, cpu %s' % (run, cpu) data.dump() print def write_dot(self, threshold, jobfile=None, jobs=None): import pydot if jobs is None: jobs = [ job for job in jobfile.jobs() ] for job in jobs: cpu = '%s.full%d' % (job.system, self.cpu) symbols = self.getdata(job.name, cpu) if not symbols: continue dot = pydot.Dot() symbols.tree.dot(dot, threshold=threshold) dot.write(symbols.filename[:-3] + 'dot') def write_txt(self, jobfile=None, jobs=None, limit=None): if jobs is None: jobs = [ job for job in jobfile.jobs() ] for job in jobs: cpu = '%s.full%d' % (job.system, self.cpu) symbols = self.getdata(job.name, cpu) if not symbols: continue output = file(symbols.filename[:-3] + 'txt', 'w') symbols.display(output, limit) def display(self, jobfile=None, jobs=None, limit=None): if jobs is None: jobs = [ job for job in jobfile.jobs() ] maxsymlen = 0 thejobs = [] for job in jobs: cpu = '%s.full%d' % (job.system, self.cpu) symbols = self.getdata(job.name, cpu) if symbols: thejobs.append(job) maxsymlen = max(maxsymlen, symbols.maxsymlen) for job in thejobs: cpu = '%s.full%d' % (job.system, self.cpu) symbols = self.getdata(job.name, cpu) print job.name symbols.display(limit=limit, maxsymlen=maxsymlen) print from categories import func_categorize, pc_categorize class PCProfile(Profile): def __init__(self, categorize=pc_categorize): super(PCProfile, self).__init__(PCData, categorize) class FuncProfile(Profile): def __init__(self, categorize=func_categorize): super(FuncProfile, self).__init__(FuncData, categorize) def usage(exitcode = None): print '''\ Usage: %s [-bc] [-g