Blame bin/perf-annotate-jit.py

Packit Service 5cb53b
#! /usr/libexec/platform-python
Packit 8f2243
#
Packit 8f2243
# Copyright 2012 VMware Inc
Packit 8f2243
# Copyright 2008-2009 Jose Fonseca
Packit 8f2243
#
Packit 8f2243
# Permission is hereby granted, free of charge, to any person obtaining a copy
Packit 8f2243
# of this software and associated documentation files (the "Software"), to deal
Packit 8f2243
# in the Software without restriction, including without limitation the rights
Packit 8f2243
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
Packit 8f2243
# copies of the Software, and to permit persons to whom the Software is
Packit 8f2243
# furnished to do so, subject to the following conditions:
Packit 8f2243
#
Packit 8f2243
# The above copyright notice and this permission notice shall be included in
Packit 8f2243
# all copies or substantial portions of the Software.
Packit 8f2243
#
Packit 8f2243
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Packit 8f2243
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Packit 8f2243
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Packit 8f2243
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Packit 8f2243
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
Packit 8f2243
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
Packit 8f2243
# THE SOFTWARE.
Packit 8f2243
#
Packit 8f2243
Packit 8f2243
"""Perf annotate for JIT code.
Packit 8f2243
Packit 8f2243
Linux `perf annotate` does not work with JIT code.  This script takes the data
Packit 8f2243
produced by `perf script` command, plus the diassemblies outputed by gallivm
Packit 8f2243
into /tmp/perf-XXXXX.map.asm and produces output similar to `perf annotate`.
Packit 8f2243
Packit Service 0f8cfe
See docs/llvmpipe.rst for usage instructions.
Packit 8f2243
Packit 8f2243
The `perf script` output parser was derived from the gprof2dot.py script.
Packit 8f2243
"""
Packit 8f2243
Packit 8f2243
Packit 8f2243
import sys
Packit 8f2243
import os.path
Packit 8f2243
import re
Packit 8f2243
import optparse
Packit 8f2243
import subprocess
Packit 8f2243
Packit 8f2243
Packit 8f2243
class Parser:
Packit 8f2243
    """Parser interface."""
Packit 8f2243
Packit 8f2243
    def __init__(self):
Packit 8f2243
        pass
Packit 8f2243
Packit 8f2243
    def parse(self):
Packit 8f2243
        raise NotImplementedError
Packit 8f2243
Packit 8f2243
Packit 8f2243
class LineParser(Parser):
Packit 8f2243
    """Base class for parsers that read line-based formats."""
Packit 8f2243
Packit 8f2243
    def __init__(self, file):
Packit 8f2243
        Parser.__init__(self)
Packit 8f2243
        self._file = file
Packit 8f2243
        self.__line = None
Packit 8f2243
        self.__eof = False
Packit 8f2243
        self.line_no = 0
Packit 8f2243
Packit 8f2243
    def readline(self):
Packit 8f2243
        line = self._file.readline()
Packit 8f2243
        if not line:
Packit 8f2243
            self.__line = ''
Packit 8f2243
            self.__eof = True
Packit 8f2243
        else:
Packit 8f2243
            self.line_no += 1
Packit 8f2243
        self.__line = line.rstrip('\r\n')
Packit 8f2243
Packit 8f2243
    def lookahead(self):
Packit 8f2243
        assert self.__line is not None
Packit 8f2243
        return self.__line
Packit 8f2243
Packit 8f2243
    def consume(self):
Packit 8f2243
        assert self.__line is not None
Packit 8f2243
        line = self.__line
Packit 8f2243
        self.readline()
Packit 8f2243
        return line
Packit 8f2243
Packit 8f2243
    def eof(self):
Packit 8f2243
        assert self.__line is not None
Packit 8f2243
        return self.__eof
Packit 8f2243
Packit 8f2243
Packit 8f2243
mapFile = None
Packit 8f2243
Packit 8f2243
def lookupMap(filename, matchSymbol):
Packit 8f2243
    global mapFile
Packit 8f2243
    mapFile = filename
Packit 8f2243
    stream = open(filename, 'rt')
Packit 8f2243
    for line in stream:
Packit 8f2243
        start, length, symbol = line.split()
Packit 8f2243
Packit 8f2243
        start = int(start, 16)
Packit 8f2243
        length = int(length,16)
Packit 8f2243
Packit 8f2243
        if symbol == matchSymbol:
Packit 8f2243
            return start
Packit 8f2243
Packit 8f2243
    return None
Packit 8f2243
Packit 8f2243
def lookupAsm(filename, desiredFunction):
Packit 8f2243
    stream = open(filename + '.asm', 'rt')
Packit 8f2243
    while stream.readline() != desiredFunction + ':\n':
Packit 8f2243
        pass
Packit 8f2243
Packit 8f2243
    asm = []
Packit 8f2243
    line = stream.readline().strip()
Packit 8f2243
    while line:
Packit 8f2243
        addr, instr = line.split(':', 1)
Packit 8f2243
        addr = int(addr)
Packit 8f2243
        asm.append((addr, instr))
Packit 8f2243
        line = stream.readline().strip()
Packit 8f2243
Packit 8f2243
    return asm
Packit 8f2243
Packit 8f2243
Packit 8f2243
Packit 8f2243
samples = {}
Packit 8f2243
Packit 8f2243
Packit 8f2243
class PerfParser(LineParser):
Packit 8f2243
    """Parser for linux perf callgraph output.
Packit 8f2243
Packit 8f2243
    It expects output generated with
Packit 8f2243
Packit 8f2243
        perf record -g
Packit 8f2243
        perf script
Packit 8f2243
    """
Packit 8f2243
Packit 8f2243
    def __init__(self, infile, symbol):
Packit 8f2243
        LineParser.__init__(self, infile)
Packit 8f2243
        self.symbol = symbol
Packit 8f2243
Packit 8f2243
    def readline(self):
Packit 8f2243
        # Override LineParser.readline to ignore comment lines
Packit 8f2243
        while True:
Packit 8f2243
            LineParser.readline(self)
Packit 8f2243
            if self.eof() or not self.lookahead().startswith('#'):
Packit 8f2243
                break
Packit 8f2243
Packit 8f2243
    def parse(self):
Packit 8f2243
        # read lookahead
Packit 8f2243
        self.readline()
Packit 8f2243
Packit 8f2243
        while not self.eof():
Packit 8f2243
            self.parse_event()
Packit 8f2243
Packit 8f2243
        asm = lookupAsm(mapFile, self.symbol)
Packit 8f2243
Packit 8f2243
        addresses = samples.keys()
Packit 8f2243
        addresses.sort()
Packit 8f2243
        total_samples = 0
Packit 8f2243
Packit 8f2243
        sys.stdout.write('%s:\n' % self.symbol)
Packit 8f2243
        for address, instr in asm:
Packit 8f2243
            try:
Packit 8f2243
                sample = samples.pop(address)
Packit 8f2243
            except KeyError:
Packit 8f2243
                sys.stdout.write(6*' ')
Packit 8f2243
            else:
Packit 8f2243
                sys.stdout.write('%6u' % (sample))
Packit 8f2243
                total_samples += sample
Packit 8f2243
            sys.stdout.write('%6u: %s\n' % (address, instr))
Packit 8f2243
        print 'total:', total_samples
Packit 8f2243
        assert len(samples) == 0
Packit 8f2243
Packit 8f2243
        sys.exit(0)
Packit 8f2243
Packit 8f2243
    def parse_event(self):
Packit 8f2243
        if self.eof():
Packit 8f2243
            return
Packit 8f2243
Packit 8f2243
        line = self.consume()
Packit 8f2243
        assert line
Packit 8f2243
Packit 8f2243
        callchain = self.parse_callchain()
Packit 8f2243
        if not callchain:
Packit 8f2243
            return
Packit 8f2243
Packit 8f2243
    def parse_callchain(self):
Packit 8f2243
        callchain = []
Packit 8f2243
        while self.lookahead():
Packit 8f2243
            function = self.parse_call(len(callchain) == 0)
Packit 8f2243
            if function is None:
Packit 8f2243
                break
Packit 8f2243
            callchain.append(function)
Packit 8f2243
        if self.lookahead() == '':
Packit 8f2243
            self.consume()
Packit 8f2243
        return callchain
Packit 8f2243
Packit 8f2243
    call_re = re.compile(r'^\s+(?P<address>[0-9a-fA-F]+)\s+(?P<symbol>.*)\s+\((?P<module>[^)]*)\)$')
Packit 8f2243
Packit 8f2243
    def parse_call(self, first):
Packit 8f2243
        line = self.consume()
Packit 8f2243
        mo = self.call_re.match(line)
Packit 8f2243
        assert mo
Packit 8f2243
        if not mo:
Packit 8f2243
            return None
Packit 8f2243
Packit 8f2243
        if not first:
Packit 8f2243
            return None
Packit 8f2243
Packit 8f2243
        function_name = mo.group('symbol')
Packit 8f2243
        if not function_name:
Packit 8f2243
            function_name = mo.group('address')
Packit 8f2243
Packit 8f2243
        module = mo.group('module')
Packit 8f2243
Packit 8f2243
        function_id = function_name + ':' + module
Packit 8f2243
Packit 8f2243
        address = mo.group('address')
Packit 8f2243
        address = int(address, 16)
Packit 8f2243
Packit 8f2243
        if function_name != self.symbol:
Packit 8f2243
            return None
Packit 8f2243
Packit 8f2243
        start_address = lookupMap(module, function_name)
Packit 8f2243
        address -= start_address
Packit 8f2243
Packit 8f2243
        #print function_name, module, address
Packit 8f2243
Packit 8f2243
        samples[address] = samples.get(address, 0) + 1
Packit 8f2243
Packit 8f2243
        return True
Packit 8f2243
Packit 8f2243
Packit 8f2243
def main():
Packit 8f2243
    """Main program."""
Packit 8f2243
Packit 8f2243
    optparser = optparse.OptionParser(
Packit 8f2243
        usage="\n\t%prog [options] symbol_name")
Packit 8f2243
    (options, args) = optparser.parse_args(sys.argv[1:])
Packit 8f2243
    if len(args) != 1:
Packit 8f2243
        optparser.error('wrong number of arguments')
Packit 8f2243
Packit 8f2243
    symbol = args[0]
Packit 8f2243
Packit 8f2243
    p = subprocess.Popen(['perf', 'script'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Packit 8f2243
    parser = PerfParser(p.stdout, symbol)
Packit 8f2243
    parser.parse()
Packit 8f2243
Packit 8f2243
Packit 8f2243
if __name__ == '__main__':
Packit 8f2243
    main()
Packit 8f2243
Packit 8f2243
Packit 8f2243
# vim: set sw=4 et: