Edit

IABSD.fr/xenocara/lib/mesa/bin/perf-annotate-jit.py

Branch :

  • Show log

    Commit

  • Author : jsg
    Date : 2025-06-05 11:23:11
    Hash : 67d6f117
    Message : Import Mesa 25.0.7

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