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.

win32kprof.py 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. #!/usr/bin/env python
  2. ##########################################################################
  3. #
  4. # Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
  5. # All Rights Reserved.
  6. #
  7. # Permission is hereby granted, free of charge, to any person obtaining a
  8. # copy of this software and associated documentation files (the
  9. # "Software"), to deal in the Software without restriction, including
  10. # without limitation the rights to use, copy, modify, merge, publish,
  11. # distribute, sub license, and/or sell copies of the Software, and to
  12. # permit persons to whom the Software is furnished to do so, subject to
  13. # the following conditions:
  14. #
  15. # The above copyright notice and this permission notice (including the
  16. # next paragraph) shall be included in all copies or substantial portions
  17. # of the Software.
  18. #
  19. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  20. # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  21. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
  22. # IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
  23. # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  24. # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  25. # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  26. #
  27. ##########################################################################
  28. import sys
  29. import optparse
  30. import re
  31. import struct
  32. from gprof2dot import Call, Function, Profile
  33. from gprof2dot import CALLS, SAMPLES, TIME, TIME_RATIO, TOTAL_TIME, TOTAL_TIME_RATIO
  34. from gprof2dot import DotWriter, TEMPERATURE_COLORMAP
  35. __version__ = '0.1'
  36. class ParseError(Exception):
  37. pass
  38. class MsvcDemangler:
  39. # http://www.kegel.com/mangle.html
  40. def __init__(self, symbol):
  41. self._symbol = symbol
  42. self._pos = 0
  43. def lookahead(self):
  44. return self._symbol[self._pos]
  45. def consume(self):
  46. ret = self.lookahead()
  47. self._pos += 1
  48. return ret
  49. def match(self, c):
  50. if self.lookahead() != c:
  51. raise ParseError
  52. self.consume()
  53. def parse(self):
  54. self.match('?')
  55. name = self.parse_name()
  56. qualifications = self.parse_qualifications()
  57. return '::'.join(qualifications + [name])
  58. def parse_name(self):
  59. if self.lookahead() == '?':
  60. return self.consume() + self.consume()
  61. else:
  62. name = self.parse_id()
  63. self.match('@')
  64. return name
  65. def parse_qualifications(self):
  66. qualifications = []
  67. while self.lookahead() != '@':
  68. name = self.parse_id()
  69. qualifications.append(name)
  70. self.match('@')
  71. return qualifications
  72. def parse_id(self):
  73. s = ''
  74. while True:
  75. c = self.lookahead()
  76. if c.isalnum() or c in '_':
  77. s += c
  78. self.consume()
  79. else:
  80. break
  81. return s
  82. def demangle(name):
  83. if name.startswith('_'):
  84. name = name[1:]
  85. idx = name.rfind('@')
  86. if idx != -1 and name[idx+1:].isdigit():
  87. name = name[:idx]
  88. return name
  89. if name.startswith('?'):
  90. demangler = MsvcDemangler(name)
  91. return demangler.parse()
  92. return name
  93. class Reader:
  94. def __init__(self):
  95. self.symbols = []
  96. self.symbol_cache = {}
  97. self.base_addr = None
  98. def read_map(self, mapfile):
  99. # See http://msdn.microsoft.com/en-us/library/k7xkk3e2.aspx
  100. last_addr = 0
  101. last_name = 0
  102. for line in file(mapfile, "rt"):
  103. fields = line.split()
  104. try:
  105. section_offset, name, addr, type, lib_object = fields
  106. except ValueError:
  107. continue
  108. if type != 'f':
  109. continue
  110. section, offset = section_offset.split(':')
  111. addr = int(offset, 16)
  112. self.symbols.append((addr, name))
  113. last_addr = addr
  114. last_name = name
  115. # sort symbols
  116. self.symbols.sort(key = lambda (addr, name): addr)
  117. def lookup_addr(self, addr):
  118. try:
  119. return self.symbol_cache[addr]
  120. except KeyError:
  121. pass
  122. tolerance = 4196
  123. s, e = 0, len(self.symbols)
  124. while s != e:
  125. i = (s + e)//2
  126. start_addr, name = self.symbols[i]
  127. try:
  128. end_addr, next_name = self.symbols[i + 1]
  129. except IndexError:
  130. end_addr = start_addr + tolerance
  131. if addr < start_addr:
  132. e = i
  133. continue
  134. if addr == end_addr:
  135. return next_name, addr - start_addr
  136. if addr > end_addr:
  137. s = i
  138. continue
  139. return name, addr - start_addr
  140. raise ValueError
  141. def lookup_symbol(self, name):
  142. for symbol_addr, symbol_name in self.symbols:
  143. if name == symbol_name:
  144. return symbol_addr
  145. return 0
  146. def read_data(self, data):
  147. profile = Profile()
  148. fp = file(data, "rb")
  149. entry_format = "IIII"
  150. entry_size = struct.calcsize(entry_format)
  151. caller = None
  152. caller_stack = []
  153. while True:
  154. entry = fp.read(entry_size)
  155. if len(entry) < entry_size:
  156. break
  157. caller_addr, callee_addr, samples_lo, samples_hi = struct.unpack(entry_format, entry)
  158. if caller_addr == 0 and callee_addr == 0:
  159. continue
  160. if self.base_addr is None:
  161. ref_addr = self.lookup_symbol('___debug_profile_reference@0')
  162. if ref_addr:
  163. self.base_addr = (caller_addr - ref_addr) & ~(options.align - 1)
  164. else:
  165. self.base_addr = 0
  166. sys.stderr.write('Base addr: %08x\n' % self.base_addr)
  167. samples = (samples_hi << 32) | samples_lo
  168. try:
  169. caller_raddr = caller_addr - self.base_addr
  170. caller_sym, caller_ofs = self.lookup_addr(caller_raddr)
  171. try:
  172. caller = profile.functions[caller_sym]
  173. except KeyError:
  174. caller_name = demangle(caller_sym)
  175. caller = Function(caller_sym, caller_name)
  176. profile.add_function(caller)
  177. caller[CALLS] = 0
  178. caller[SAMPLES] = 0
  179. except ValueError:
  180. caller = None
  181. if not callee_addr:
  182. if caller:
  183. caller[SAMPLES] += samples
  184. else:
  185. callee_raddr = callee_addr - self.base_addr
  186. callee_sym, callee_ofs = self.lookup_addr(callee_raddr)
  187. try:
  188. callee = profile.functions[callee_sym]
  189. except KeyError:
  190. callee_name = demangle(callee_sym)
  191. callee = Function(callee_sym, callee_name)
  192. profile.add_function(callee)
  193. callee[CALLS] = samples
  194. callee[SAMPLES] = 0
  195. else:
  196. callee[CALLS] += samples
  197. if caller is not None:
  198. try:
  199. call = caller.calls[callee.id]
  200. except KeyError:
  201. call = Call(callee.id)
  202. call[CALLS] = samples
  203. caller.add_call(call)
  204. else:
  205. call[CALLS] += samples
  206. if options.verbose:
  207. if not callee_addr:
  208. sys.stderr.write('%s+%u: %u\n' % (caller_sym, caller_ofs, samples))
  209. else:
  210. sys.stderr.write('%s+%u -> %s+%u: %u\n' % (caller_sym, caller_ofs, callee_sym, callee_ofs, samples))
  211. # compute derived data
  212. profile.validate()
  213. profile.find_cycles()
  214. profile.aggregate(SAMPLES)
  215. profile.ratio(TIME_RATIO, SAMPLES)
  216. profile.call_ratios(CALLS)
  217. profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO)
  218. return profile
  219. def main():
  220. parser = optparse.OptionParser(
  221. usage="\n\t%prog [options] [file] ...",
  222. version="%%prog %s" % __version__)
  223. parser.add_option(
  224. '-a', '--align', metavar='NUMBER',
  225. type="int", dest="align", default=16,
  226. help="section alignment")
  227. parser.add_option(
  228. '-m', '--map', metavar='FILE',
  229. type="string", dest="map",
  230. help="map file")
  231. parser.add_option(
  232. '-b', '--base', metavar='FILE',
  233. type="string", dest="base",
  234. help="base addr")
  235. parser.add_option(
  236. '-n', '--node-thres', metavar='PERCENTAGE',
  237. type="float", dest="node_thres", default=0.5,
  238. help="eliminate nodes below this threshold [default: %default]")
  239. parser.add_option(
  240. '-e', '--edge-thres', metavar='PERCENTAGE',
  241. type="float", dest="edge_thres", default=0.1,
  242. help="eliminate edges below this threshold [default: %default]")
  243. parser.add_option(
  244. '-v', '--verbose',
  245. action="count",
  246. dest="verbose", default=0,
  247. help="verbose output")
  248. global options
  249. (options, args) = parser.parse_args(sys.argv[1:])
  250. reader = Reader()
  251. if options.base is not None:
  252. reader.base_addr = int(options.base, 16)
  253. if options.map is not None:
  254. reader.read_map(options.map)
  255. for arg in args:
  256. profile = reader.read_data(arg)
  257. profile.prune(options.node_thres/100.0, options.edge_thres/100.0)
  258. output = sys.stdout
  259. dot = DotWriter(output)
  260. colormap = TEMPERATURE_COLORMAP
  261. dot.graph(profile, colormap)
  262. if __name__ == '__main__':
  263. main()