+++ /dev/null
-#!/usr/bin/env python
-
-import subprocess
-import re
-import shutil
-import os
-import socket
-from xml.dom.minidom import parse as parseXML
-from jinja2 import Environment, FileSystemLoader
-
-b_id = 0
-s_id = 0
-
-try:
- __version__ = __import__('pkg_resources') \
- .get_distribution('hiprofile').version
-except Exception:
- __version__ = 'unknown'
-
-def _get_count(node):
- """ Utility function: return the number in a 'count' element contained in
- the current node"""
- countnode = node.getElementsByTagName('count')
- return int(countnode.item(0).firstChild.data)
-
-def _threshold_list(list, fn, threshold):
- """ Utility function: given a list and an ordering function, return the
- elements of the list that are above the threshold.
-
- If threshold is a percentage (ie, ends with '%'), then the items
- that fn returns above that percentage of the total. If it's an absolute
- number, then that number of items is returned"""
-
- if threshold.endswith('%'):
- percentage = float(threshold[0:-1]) / 100
- total = reduce(int.__add__, map(fn, list))
- list = [ x for x in list if fn(x) > (total * percentage) ]
- list.sort(key = fn, reverse = True)
-
- else:
- count = int(threshold)
- list.sort(key = fn, reverse = True)
- list = list[0:count]
-
- return list
-
-class Connection(object):
- def __init__(self, host):
- self.host = host
-
- def execute(self, command, input = None):
-
- if self.host:
- command = ['ssh', self.host] + command
-
- if input:
- stdin = subprocess.PIPE
- else:
- stdin = None
-
- null_fd = open('/dev/null', 'w')
- proc = subprocess.Popen(command, stdin = stdin,
- stdout = subprocess.PIPE, stderr = null_fd)
-
- if input:
- proc.stdin.write(input)
-
- return proc.stdout
-
-class SymbolInstruction(object):
- def __init__(self, addr, asm, source = None):
- self.addr = addr
- self.asm = asm
- self.percentage = 0
-
- if source is None:
- source = ''
- self.source = source
-
- def set_samples(self, samples, total):
- self.samples = samples
- self.percentage = 100 * float(samples) / total
-
- def colour(self):
- if not self.percentage:
- return '#ffc000'
-
- if self.percentage * 40 > 0xc0:
- return '#ff0000'
-
- return '#ff%02x00' % (0xc0 - self.percentage * 40)
-
-
-# 13283 0.7260 :1031b6e0: stwu r1,-48(r1)
-sampled_asm_re = re.compile( \
- '^\s*(?P<samples>\d+)\s+\S+\s+:\s*(?P<addr>[0-9a-f]+):\s*(?P<asm>.*)\s*$')
-
-# :1031b72c: addi r0,r9,4
-unsampled_asm_re = re.compile( \
- '^\s*:\s*(?P<addr>[0-9a-f]+):\s*(?P<asm>.*)\s*$')
-
-# :AllocSetAlloc(MemoryContext context, Size size)
-source_re = re.compile( \
- '^\s*:(?P<source>.*)$')
-
-class SymbolReference(object):
-
- def __init__(self, id, name, module):
- self.id = id
- self.name = name
- self.count = 0;
- self.module = module
- self.annotations = None
-
- # annotation parsing buf
- self.source_buf = ''
- self.insns = []
-
- def module_name(self):
- return self.module.split('/')[-1]
-
- def filename(self):
- return 'symbol-%s.html' % self.id
-
- def annotate(self, line):
- match = None
-
- sampled_match = sampled_asm_re.match(line)
- if sampled_match:
- match = sampled_match
-
- unsampled_match = unsampled_asm_re.match(line)
- if unsampled_match:
- match = unsampled_match
-
- if match:
- insn = SymbolInstruction(match.group('addr'), match.group('asm'),
- self.source_buf)
- if sampled_match:
- insn.set_samples(int(match.group('samples')), self.count)
- self.insns.append(insn)
- self.source_buf = ''
- return
-
- match = source_re.match(line)
- if match:
- self.source_buf += match.group('source') + '\n'
-
- @staticmethod
- def parse(report, node, module):
- id = int(node.getAttribute('idref'))
- ref = SymbolReference(id, report.symtab[id], module)
-
- ref.count = _get_count(node)
- return ref
-
-class Binary(object):
-
- def __init__(self, name, count):
- global b_id
- self.name = name
- self.count = count
- self.references = []
- self.id = b_id = b_id + 1
-
- def __str__(self):
- s = '%s: %d' % (self.name, self.count)
- for ref in self.references:
- s += '\n' + str(ref)
- return s
-
- def shortname(self):
- return self.name.split('/')[-1]
-
- def filename(self):
- return 'binary-%d.html' % self.id
-
- def threshold(self, thresholds):
- self.references = _threshold_list(self.references,
- lambda r: r.count, thresholds['symbol'])
- self.reference_dict = dict([ (r.name, r) for r in self.references ])
-
- def annotate(self, report, conn, options):
- fn_re = re.compile('^[0-9a-f]+\s+<[^>]+>: /\* (\S+) total:')
-
- symbols = [ s for s in self.references if s.name != '(no symbols)' ]
-
- if not symbols:
- return
-
- command = [options.opannotate, '--source', '--assembly',
- '--include-file=' + self.name,
- '-i', ','.join([ s.name for s in symbols ])]
-
- fd = conn.execute(command)
-
- symbol = None
-
- for line in fd.readlines():
- match = fn_re.match(line)
- if match:
- if symbol:
- symbol.annotate(line)
- symname = match.group(1)
- if symname in self.reference_dict:
- symbol = self.reference_dict[symname]
- else:
- symbol = None
- if symbol:
- symbol.annotate(line)
-
- def parse_symbol(self, report, node, module = None):
- if module is None:
- module = self.name
-
- ref = SymbolReference.parse(report, node, module)
- ref.percentage = 100 * float(ref.count) / self.count
- self.references.append(ref)
-
-
- @staticmethod
- def parse(report, node):
- name = node.getAttribute('name')
-
- binary = Binary(name, _get_count(node))
-
- for child_node in node.childNodes:
- if child_node.nodeType != node.ELEMENT_NODE:
- continue
-
- if child_node.nodeName == 'symbol':
- binary.parse_symbol(report, child_node, None)
-
- elif child_node.nodeName == 'module':
- module_name = child_node.getAttribute('name')
- for child_sym_node in child_node.getElementsByTagName('symbol'):
- binary.parse_symbol(report, child_sym_node, module_name)
-
- return binary
-
-class Report(object):
- def __init__(self, host, arch, cpu):
- self.host = host
- self.arch = arch
- self.cpu = cpu
- self.binaries = []
- self.symtab = []
- self.total_samples = 0
-
- def add_binary(self, binary):
- self.binaries.append(binary)
- self.total_samples += binary.count
-
- def threshold(self, thresholds):
- self.binaries = _threshold_list(self.binaries,
- lambda b: b.count, thresholds['binary'])
-
- for binary in self.binaries:
- binary.threshold(thresholds)
-
- def annotate(self, conn, options):
- for binary in self.binaries:
- binary.annotate(self, conn, options)
-
- @staticmethod
- def parse(doc, hostname):
- node = doc.documentElement
-
- cpu = '%s (%s MHz)' % (\
- node.getAttribute('processor'),
- node.getAttribute('mhz'))
-
- report = Report(hostname, node.getAttribute('cputype'), cpu)
-
- # parse symbol table
- symtab_node = doc.getElementsByTagName('symboltable').item(0)
-
- for node in symtab_node.childNodes:
- if node.nodeType != node.ELEMENT_NODE:
- continue
- report.symtab.insert(int(node.getAttribute('id')),
- node.getAttribute('name'))
-
-
- # parse each binary node
- for node in doc.getElementsByTagName('binary'):
- binary = Binary.parse(report, node)
- report.add_binary(binary)
-
- # calculate percentages
- for binary in report.binaries:
- binary.percentage = 100 * float(binary.count) / report.total_samples
-
- return report
-
- @staticmethod
- def extract(connection, options):
- fd = connection.execute([options.opreport, '--xml'])
- doc = parseXML(fd)
-
- if connection.host:
- hostname = connection.host
- else:
- hostname = socket.gethostname()
-
- return Report.parse(doc, hostname)
-
- def __str__(self):
- return self.machine + '\n' + '\n'.join(map(str, self.binaries))
-
-def write_report(report, resourcedir, outdir):
-
- os.mkdir(outdir)
-
- # set up template engine
- env = Environment(loader = FileSystemLoader(resourcedir),
- autoescape = True)
- templates = {}
- for name in ['report', 'binary', 'symbol']:
- templates[name] = env.get_template('%s.html' % name)
-
- # copy required files over
- files = ['style.css', 'hiprofile.js', 'bar.png', 'jquery-1.3.1.min.js']
- for file in files:
- shutil.copy(os.path.join(resourcedir, file), outdir)
-
- reportfile = os.path.join(outdir, 'index.html')
- templates['report'].stream(version = __version__,
- report = report).dump(reportfile)
-
- for binary in report.binaries:
- binaryfile = os.path.join(outdir, binary.filename())
- templates['binary'].stream(version = __version__,
- report = report,
- binary = binary) \
- .dump(binaryfile)
-
- for symbol in binary.references:
- symbolfile = os.path.join(outdir, symbol.filename())
- templates['symbol'].stream(version = __version__,
- report = report, binary = binary,
- symbol = symbol).dump(symbolfile)