X-Git-Url: https://git.ozlabs.org/?p=hiprofile;a=blobdiff_plain;f=hiprofile.py;fp=hiprofile.py;h=0000000000000000000000000000000000000000;hp=6f99d3c3e176338653b45736d4fc1be34d5a04f4;hb=c700523f0bfe517fafdacf1596c1fc0ca895ccff;hpb=572198ed6c5d9582d0e406704e37d0f88365ec1f diff --git a/hiprofile.py b/hiprofile.py deleted file mode 100644 index 6f99d3c..0000000 --- a/hiprofile.py +++ /dev/null @@ -1,342 +0,0 @@ -#!/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\d+)\s+\S+\s+:\s*(?P[0-9a-f]+):\s*(?P.*)\s*$') - -# :1031b72c: addi r0,r9,4 -unsampled_asm_re = re.compile( \ - '^\s*:\s*(?P[0-9a-f]+):\s*(?P.*)\s*$') - -# :AllocSetAlloc(MemoryContext context, Size size) -source_re = re.compile( \ - '^\s*:(?P.*)$') - -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)