3 # Copyright 2012 VMware Inc
4 # Copyright 2008-2009 Jose Fonseca
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:
13 # The above copyright notice and this permission notice shall be included in
14 # all copies or substantial portions of the Software.
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
25 """Perf annotate for JIT code.
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`.
31 See docs/llvmpipe.html for usage instructions.
33 The `perf script` output parser was derived from the gprof2dot.py script.
45 """Parser interface."""
51 raise NotImplementedError
54 class LineParser(Parser
):
55 """Base class for parsers that read line-based formats."""
57 def __init__(self
, file):
65 line
= self
._file
.readline()
71 self
.__line
= line
.rstrip('\r\n')
74 assert self
.__line
is not None
78 assert self
.__line
is not None
84 assert self
.__line
is not None
90 def lookupMap(filename
, matchSymbol
):
93 stream
= open(filename
, 'rt')
95 start
, length
, symbol
= line
.split()
97 start
= int(start
, 16)
98 length
= int(length
,16)
100 if symbol
== matchSymbol
:
105 def lookupAsm(filename
, desiredFunction
):
106 stream
= open(filename
+ '.asm', 'rt')
107 while stream
.readline() != desiredFunction
+ ':\n':
111 line
= stream
.readline().strip()
113 addr
, instr
= line
.split(':', 1)
115 asm
.append((addr
, instr
))
116 line
= stream
.readline().strip()
125 class PerfParser(LineParser
):
126 """Parser for linux perf callgraph output.
128 It expects output generated with
134 def __init__(self
, infile
, symbol
):
135 LineParser
.__init
__(self
, infile
)
139 # Override LineParser.readline to ignore comment lines
141 LineParser
.readline(self
)
142 if self
.eof() or not self
.lookahead().startswith('#'):
149 while not self
.eof():
152 asm
= lookupAsm(mapFile
, self
.symbol
)
154 addresses
= samples
.keys()
158 sys
.stdout
.write('%s:\n' % self
.symbol
)
159 for address
, instr
in asm
:
161 sample
= samples
.pop(address
)
163 sys
.stdout
.write(6*' ')
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
173 def parse_event(self
):
177 line
= self
.consume()
180 callchain
= self
.parse_callchain()
184 def parse_callchain(self
):
186 while self
.lookahead():
187 function
= self
.parse_call(len(callchain
) == 0)
190 callchain
.append(function
)
191 if self
.lookahead() == '':
195 call_re
= re
.compile(r
'^\s+(?P<address>[0-9a-fA-F]+)\s+(?P<symbol>.*)\s+\((?P<module>[^)]*)\)$')
197 def parse_call(self
, first
):
198 line
= self
.consume()
199 mo
= self
.call_re
.match(line
)
207 function_name
= mo
.group('symbol')
208 if not function_name
:
209 function_name
= mo
.group('address')
211 module
= mo
.group('module')
213 function_id
= function_name
+ ':' + module
215 address
= mo
.group('address')
216 address
= int(address
, 16)
218 if function_name
!= self
.symbol
:
221 start_address
= lookupMap(module
, function_name
)
222 address
-= start_address
224 #print function_name, module, address
226 samples
[address
] = samples
.get(address
, 0) + 1
234 optparser
= optparse
.OptionParser(
235 usage
="\n\t%prog [options] symbol_name")
236 (options
, args
) = optparser
.parse_args(sys
.argv
[1:])
238 optparser
.error('wrong number of arguments')
242 p
= subprocess
.Popen(['perf', 'script'], stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)
243 parser
= PerfParser(p
.stdout
, symbol
)
247 if __name__
== '__main__':