OSDN Git Service

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