Clone of mesa.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

perf-annotate-jit 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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. """Perf annotate for JIT code.
  25. Linux `perf annotate` does not work with JIT code. This script takes the data
  26. produced by `perf script` command, plus the diassemblies outputed by gallivm
  27. into /tmp/perf-XXXXX.map.asm and produces output similar to `perf annotate`.
  28. See docs/llvmpipe.html for usage instructions.
  29. The `perf script` output parser was derived from the gprof2dot.py script.
  30. """
  31. import sys
  32. import os.path
  33. import re
  34. import optparse
  35. import subprocess
  36. class Parser:
  37. """Parser interface."""
  38. def __init__(self):
  39. pass
  40. def parse(self):
  41. raise NotImplementedError
  42. class LineParser(Parser):
  43. """Base class for parsers that read line-based formats."""
  44. def __init__(self, file):
  45. Parser.__init__(self)
  46. self._file = file
  47. self.__line = None
  48. self.__eof = False
  49. self.line_no = 0
  50. def readline(self):
  51. line = self._file.readline()
  52. if not line:
  53. self.__line = ''
  54. self.__eof = True
  55. else:
  56. self.line_no += 1
  57. self.__line = line.rstrip('\r\n')
  58. def lookahead(self):
  59. assert self.__line is not None
  60. return self.__line
  61. def consume(self):
  62. assert self.__line is not None
  63. line = self.__line
  64. self.readline()
  65. return line
  66. def eof(self):
  67. assert self.__line is not None
  68. return self.__eof
  69. mapFile = None
  70. def lookupMap(filename, matchSymbol):
  71. global mapFile
  72. mapFile = filename
  73. stream = open(filename, 'rt')
  74. for line in stream:
  75. start, length, symbol = line.split()
  76. start = int(start, 16)
  77. length = int(length,16)
  78. if symbol == matchSymbol:
  79. return start
  80. return None
  81. def lookupAsm(filename, desiredFunction):
  82. stream = open(filename + '.asm', 'rt')
  83. while stream.readline() != desiredFunction + ':\n':
  84. pass
  85. asm = []
  86. line = stream.readline().strip()
  87. while line:
  88. addr, instr = line.split(':', 1)
  89. addr = int(addr)
  90. asm.append((addr, instr))
  91. line = stream.readline().strip()
  92. return asm
  93. samples = {}
  94. class PerfParser(LineParser):
  95. """Parser for linux perf callgraph output.
  96. It expects output generated with
  97. perf record -g
  98. perf script
  99. """
  100. def __init__(self, infile, symbol):
  101. LineParser.__init__(self, infile)
  102. self.symbol = symbol
  103. def readline(self):
  104. # Override LineParser.readline to ignore comment lines
  105. while True:
  106. LineParser.readline(self)
  107. if self.eof() or not self.lookahead().startswith('#'):
  108. break
  109. def parse(self):
  110. # read lookahead
  111. self.readline()
  112. while not self.eof():
  113. self.parse_event()
  114. asm = lookupAsm(mapFile, self.symbol)
  115. addresses = samples.keys()
  116. addresses.sort()
  117. total_samples = 0
  118. sys.stdout.write('%s:\n' % self.symbol)
  119. for address, instr in asm:
  120. try:
  121. sample = samples.pop(address)
  122. except KeyError:
  123. sys.stdout.write(6*' ')
  124. else:
  125. sys.stdout.write('%6u' % (sample))
  126. total_samples += sample
  127. sys.stdout.write('%6u: %s\n' % (address, instr))
  128. print 'total:', total_samples
  129. assert len(samples) == 0
  130. sys.exit(0)
  131. def parse_event(self):
  132. if self.eof():
  133. return
  134. line = self.consume()
  135. assert line
  136. callchain = self.parse_callchain()
  137. if not callchain:
  138. return
  139. def parse_callchain(self):
  140. callchain = []
  141. while self.lookahead():
  142. function = self.parse_call(len(callchain) == 0)
  143. if function is None:
  144. break
  145. callchain.append(function)
  146. if self.lookahead() == '':
  147. self.consume()
  148. return callchain
  149. call_re = re.compile(r'^\s+(?P<address>[0-9a-fA-F]+)\s+(?P<symbol>.*)\s+\((?P<module>[^)]*)\)$')
  150. def parse_call(self, first):
  151. line = self.consume()
  152. mo = self.call_re.match(line)
  153. assert mo
  154. if not mo:
  155. return None
  156. if not first:
  157. return None
  158. function_name = mo.group('symbol')
  159. if not function_name:
  160. function_name = mo.group('address')
  161. module = mo.group('module')
  162. function_id = function_name + ':' + module
  163. address = mo.group('address')
  164. address = int(address, 16)
  165. if function_name != self.symbol:
  166. return None
  167. start_address = lookupMap(module, function_name)
  168. address -= start_address
  169. #print function_name, module, address
  170. samples[address] = samples.get(address, 0) + 1
  171. return True
  172. def main():
  173. """Main program."""
  174. optparser = optparse.OptionParser(
  175. usage="\n\t%prog [options] symbol_name")
  176. (options, args) = optparser.parse_args(sys.argv[1:])
  177. if len(args) != 1:
  178. optparser.error('wrong number of arguments')
  179. symbol = args[0]
  180. p = subprocess.Popen(['perf', 'script'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  181. parser = PerfParser(p.stdout, symbol)
  182. parser.parse()
  183. if __name__ == '__main__':
  184. main()
  185. # vim: set sw=4 et: