v3d: add new flag dirty TMU cache at v3d_compiler
[mesa.git] / bin / perf-annotate-jit.py
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: