1bf215546Sopenharmony_ci#!/usr/bin/env python
2bf215546Sopenharmony_ci#
3bf215546Sopenharmony_ci# Copyright 2012 VMware Inc
4bf215546Sopenharmony_ci# Copyright 2008-2009 Jose Fonseca
5bf215546Sopenharmony_ci#
6bf215546Sopenharmony_ci# Permission is hereby granted, free of charge, to any person obtaining a copy
7bf215546Sopenharmony_ci# of this software and associated documentation files (the "Software"), to deal
8bf215546Sopenharmony_ci# in the Software without restriction, including without limitation the rights
9bf215546Sopenharmony_ci# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10bf215546Sopenharmony_ci# copies of the Software, and to permit persons to whom the Software is
11bf215546Sopenharmony_ci# furnished to do so, subject to the following conditions:
12bf215546Sopenharmony_ci#
13bf215546Sopenharmony_ci# The above copyright notice and this permission notice shall be included in
14bf215546Sopenharmony_ci# all copies or substantial portions of the Software.
15bf215546Sopenharmony_ci#
16bf215546Sopenharmony_ci# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17bf215546Sopenharmony_ci# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18bf215546Sopenharmony_ci# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19bf215546Sopenharmony_ci# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20bf215546Sopenharmony_ci# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21bf215546Sopenharmony_ci# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22bf215546Sopenharmony_ci# THE SOFTWARE.
23bf215546Sopenharmony_ci#
24bf215546Sopenharmony_ci
25bf215546Sopenharmony_ci"""Perf annotate for JIT code.
26bf215546Sopenharmony_ci
27bf215546Sopenharmony_ciLinux `perf annotate` does not work with JIT code.  This script takes the data
28bf215546Sopenharmony_ciproduced by `perf script` command, plus the diassemblies outputed by gallivm
29bf215546Sopenharmony_ciinto /tmp/perf-XXXXX.map.asm and produces output similar to `perf annotate`.
30bf215546Sopenharmony_ci
31bf215546Sopenharmony_ciSee docs/llvmpipe.rst for usage instructions.
32bf215546Sopenharmony_ci
33bf215546Sopenharmony_ciThe `perf script` output parser was derived from the gprof2dot.py script.
34bf215546Sopenharmony_ci"""
35bf215546Sopenharmony_ci
36bf215546Sopenharmony_ci
37bf215546Sopenharmony_ciimport sys
38bf215546Sopenharmony_ciimport os.path
39bf215546Sopenharmony_ciimport re
40bf215546Sopenharmony_ciimport optparse
41bf215546Sopenharmony_ciimport subprocess
42bf215546Sopenharmony_ci
43bf215546Sopenharmony_ci
44bf215546Sopenharmony_ciclass Parser:
45bf215546Sopenharmony_ci    """Parser interface."""
46bf215546Sopenharmony_ci
47bf215546Sopenharmony_ci    def __init__(self):
48bf215546Sopenharmony_ci        pass
49bf215546Sopenharmony_ci
50bf215546Sopenharmony_ci    def parse(self):
51bf215546Sopenharmony_ci        raise NotImplementedError
52bf215546Sopenharmony_ci
53bf215546Sopenharmony_ci
54bf215546Sopenharmony_ciclass LineParser(Parser):
55bf215546Sopenharmony_ci    """Base class for parsers that read line-based formats."""
56bf215546Sopenharmony_ci
57bf215546Sopenharmony_ci    def __init__(self, file):
58bf215546Sopenharmony_ci        Parser.__init__(self)
59bf215546Sopenharmony_ci        self._file = file
60bf215546Sopenharmony_ci        self.__line = None
61bf215546Sopenharmony_ci        self.__eof = False
62bf215546Sopenharmony_ci        self.line_no = 0
63bf215546Sopenharmony_ci
64bf215546Sopenharmony_ci    def readline(self):
65bf215546Sopenharmony_ci        line = self._file.readline()
66bf215546Sopenharmony_ci        if not line:
67bf215546Sopenharmony_ci            self.__line = ''
68bf215546Sopenharmony_ci            self.__eof = True
69bf215546Sopenharmony_ci        else:
70bf215546Sopenharmony_ci            self.line_no += 1
71bf215546Sopenharmony_ci        self.__line = line.rstrip('\r\n')
72bf215546Sopenharmony_ci
73bf215546Sopenharmony_ci    def lookahead(self):
74bf215546Sopenharmony_ci        assert self.__line is not None
75bf215546Sopenharmony_ci        return self.__line
76bf215546Sopenharmony_ci
77bf215546Sopenharmony_ci    def consume(self):
78bf215546Sopenharmony_ci        assert self.__line is not None
79bf215546Sopenharmony_ci        line = self.__line
80bf215546Sopenharmony_ci        self.readline()
81bf215546Sopenharmony_ci        return line
82bf215546Sopenharmony_ci
83bf215546Sopenharmony_ci    def eof(self):
84bf215546Sopenharmony_ci        assert self.__line is not None
85bf215546Sopenharmony_ci        return self.__eof
86bf215546Sopenharmony_ci
87bf215546Sopenharmony_ci
88bf215546Sopenharmony_cimapFile = None
89bf215546Sopenharmony_ci
90bf215546Sopenharmony_cidef lookupMap(filename, matchSymbol):
91bf215546Sopenharmony_ci    global mapFile
92bf215546Sopenharmony_ci    mapFile = filename
93bf215546Sopenharmony_ci    stream = open(filename, 'rt')
94bf215546Sopenharmony_ci    for line in stream:
95bf215546Sopenharmony_ci        start, length, symbol = line.split()
96bf215546Sopenharmony_ci
97bf215546Sopenharmony_ci        start = int(start, 16)
98bf215546Sopenharmony_ci        length = int(length,16)
99bf215546Sopenharmony_ci
100bf215546Sopenharmony_ci        if symbol == matchSymbol:
101bf215546Sopenharmony_ci            return start
102bf215546Sopenharmony_ci
103bf215546Sopenharmony_ci    return None
104bf215546Sopenharmony_ci
105bf215546Sopenharmony_cidef lookupAsm(filename, desiredFunction):
106bf215546Sopenharmony_ci    stream = open(filename + '.asm', 'rt')
107bf215546Sopenharmony_ci    while stream.readline() != desiredFunction + ':\n':
108bf215546Sopenharmony_ci        pass
109bf215546Sopenharmony_ci
110bf215546Sopenharmony_ci    asm = []
111bf215546Sopenharmony_ci    line = stream.readline().strip()
112bf215546Sopenharmony_ci    while line:
113bf215546Sopenharmony_ci        addr, instr = line.split(':', 1)
114bf215546Sopenharmony_ci        addr = int(addr)
115bf215546Sopenharmony_ci        asm.append((addr, instr))
116bf215546Sopenharmony_ci        line = stream.readline().strip()
117bf215546Sopenharmony_ci
118bf215546Sopenharmony_ci    return asm
119bf215546Sopenharmony_ci
120bf215546Sopenharmony_ci
121bf215546Sopenharmony_ci
122bf215546Sopenharmony_cisamples = {}
123bf215546Sopenharmony_ci
124bf215546Sopenharmony_ci
125bf215546Sopenharmony_ciclass PerfParser(LineParser):
126bf215546Sopenharmony_ci    """Parser for linux perf callgraph output.
127bf215546Sopenharmony_ci
128bf215546Sopenharmony_ci    It expects output generated with
129bf215546Sopenharmony_ci
130bf215546Sopenharmony_ci        perf record -g
131bf215546Sopenharmony_ci        perf script
132bf215546Sopenharmony_ci    """
133bf215546Sopenharmony_ci
134bf215546Sopenharmony_ci    def __init__(self, infile, symbol):
135bf215546Sopenharmony_ci        LineParser.__init__(self, infile)
136bf215546Sopenharmony_ci        self.symbol = symbol
137bf215546Sopenharmony_ci
138bf215546Sopenharmony_ci    def readline(self):
139bf215546Sopenharmony_ci        # Override LineParser.readline to ignore comment lines
140bf215546Sopenharmony_ci        while True:
141bf215546Sopenharmony_ci            LineParser.readline(self)
142bf215546Sopenharmony_ci            if self.eof() or not self.lookahead().startswith('#'):
143bf215546Sopenharmony_ci                break
144bf215546Sopenharmony_ci
145bf215546Sopenharmony_ci    def parse(self):
146bf215546Sopenharmony_ci        # read lookahead
147bf215546Sopenharmony_ci        self.readline()
148bf215546Sopenharmony_ci
149bf215546Sopenharmony_ci        while not self.eof():
150bf215546Sopenharmony_ci            self.parse_event()
151bf215546Sopenharmony_ci
152bf215546Sopenharmony_ci        asm = lookupAsm(mapFile, self.symbol)
153bf215546Sopenharmony_ci
154bf215546Sopenharmony_ci        addresses = samples.keys()
155bf215546Sopenharmony_ci        addresses.sort()
156bf215546Sopenharmony_ci        total_samples = 0
157bf215546Sopenharmony_ci
158bf215546Sopenharmony_ci        sys.stdout.write('%s:\n' % self.symbol)
159bf215546Sopenharmony_ci        for address, instr in asm:
160bf215546Sopenharmony_ci            try:
161bf215546Sopenharmony_ci                sample = samples.pop(address)
162bf215546Sopenharmony_ci            except KeyError:
163bf215546Sopenharmony_ci                sys.stdout.write(6*' ')
164bf215546Sopenharmony_ci            else:
165bf215546Sopenharmony_ci                sys.stdout.write('%6u' % (sample))
166bf215546Sopenharmony_ci                total_samples += sample
167bf215546Sopenharmony_ci            sys.stdout.write('%6u: %s\n' % (address, instr))
168bf215546Sopenharmony_ci        print 'total:', total_samples
169bf215546Sopenharmony_ci        assert len(samples) == 0
170bf215546Sopenharmony_ci
171bf215546Sopenharmony_ci        sys.exit(0)
172bf215546Sopenharmony_ci
173bf215546Sopenharmony_ci    def parse_event(self):
174bf215546Sopenharmony_ci        if self.eof():
175bf215546Sopenharmony_ci            return
176bf215546Sopenharmony_ci
177bf215546Sopenharmony_ci        line = self.consume()
178bf215546Sopenharmony_ci        assert line
179bf215546Sopenharmony_ci
180bf215546Sopenharmony_ci        callchain = self.parse_callchain()
181bf215546Sopenharmony_ci        if not callchain:
182bf215546Sopenharmony_ci            return
183bf215546Sopenharmony_ci
184bf215546Sopenharmony_ci    def parse_callchain(self):
185bf215546Sopenharmony_ci        callchain = []
186bf215546Sopenharmony_ci        while self.lookahead():
187bf215546Sopenharmony_ci            function = self.parse_call(len(callchain) == 0)
188bf215546Sopenharmony_ci            if function is None:
189bf215546Sopenharmony_ci                break
190bf215546Sopenharmony_ci            callchain.append(function)
191bf215546Sopenharmony_ci        if self.lookahead() == '':
192bf215546Sopenharmony_ci            self.consume()
193bf215546Sopenharmony_ci        return callchain
194bf215546Sopenharmony_ci
195bf215546Sopenharmony_ci    call_re = re.compile(r'^\s+(?P<address>[0-9a-fA-F]+)\s+(?P<symbol>.*)\s+\((?P<module>[^)]*)\)$')
196bf215546Sopenharmony_ci
197bf215546Sopenharmony_ci    def parse_call(self, first):
198bf215546Sopenharmony_ci        line = self.consume()
199bf215546Sopenharmony_ci        mo = self.call_re.match(line)
200bf215546Sopenharmony_ci        assert mo
201bf215546Sopenharmony_ci        if not mo:
202bf215546Sopenharmony_ci            return None
203bf215546Sopenharmony_ci
204bf215546Sopenharmony_ci        if not first:
205bf215546Sopenharmony_ci            return None
206bf215546Sopenharmony_ci
207bf215546Sopenharmony_ci        function_name = mo.group('symbol')
208bf215546Sopenharmony_ci        if not function_name:
209bf215546Sopenharmony_ci            function_name = mo.group('address')
210bf215546Sopenharmony_ci
211bf215546Sopenharmony_ci        module = mo.group('module')
212bf215546Sopenharmony_ci
213bf215546Sopenharmony_ci        function_id = function_name + ':' + module
214bf215546Sopenharmony_ci
215bf215546Sopenharmony_ci        address = mo.group('address')
216bf215546Sopenharmony_ci        address = int(address, 16)
217bf215546Sopenharmony_ci
218bf215546Sopenharmony_ci        if function_name != self.symbol:
219bf215546Sopenharmony_ci            return None
220bf215546Sopenharmony_ci
221bf215546Sopenharmony_ci        start_address = lookupMap(module, function_name)
222bf215546Sopenharmony_ci        address -= start_address
223bf215546Sopenharmony_ci
224bf215546Sopenharmony_ci        #print function_name, module, address
225bf215546Sopenharmony_ci
226bf215546Sopenharmony_ci        samples[address] = samples.get(address, 0) + 1
227bf215546Sopenharmony_ci
228bf215546Sopenharmony_ci        return True
229bf215546Sopenharmony_ci
230bf215546Sopenharmony_ci
231bf215546Sopenharmony_cidef main():
232bf215546Sopenharmony_ci    """Main program."""
233bf215546Sopenharmony_ci
234bf215546Sopenharmony_ci    optparser = optparse.OptionParser(
235bf215546Sopenharmony_ci        usage="\n\t%prog [options] symbol_name")
236bf215546Sopenharmony_ci    (options, args) = optparser.parse_args(sys.argv[1:])
237bf215546Sopenharmony_ci    if len(args) != 1:
238bf215546Sopenharmony_ci        optparser.error('wrong number of arguments')
239bf215546Sopenharmony_ci
240bf215546Sopenharmony_ci    symbol = args[0]
241bf215546Sopenharmony_ci
242bf215546Sopenharmony_ci    p = subprocess.Popen(['perf', 'script'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
243bf215546Sopenharmony_ci    parser = PerfParser(p.stdout, symbol)
244bf215546Sopenharmony_ci    parser.parse()
245bf215546Sopenharmony_ci
246bf215546Sopenharmony_ci
247bf215546Sopenharmony_ciif __name__ == '__main__':
248bf215546Sopenharmony_ci    main()
249bf215546Sopenharmony_ci
250bf215546Sopenharmony_ci
251bf215546Sopenharmony_ci# vim: set sw=4 et:
252