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 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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. __version__ = '0.1'
  33. verbose = False
  34. class ParseError(Exception):
  35. pass
  36. class MsvcDemangler:
  37. # http://www.kegel.com/mangle.html
  38. def __init__(self, symbol):
  39. self._symbol = symbol
  40. self._pos = 0
  41. def lookahead(self):
  42. return self._symbol[self._pos]
  43. def consume(self):
  44. ret = self.lookahead()
  45. self._pos += 1
  46. return ret
  47. def match(self, c):
  48. if self.lookahead() != c:
  49. raise ParseError
  50. self.consume()
  51. def parse(self):
  52. self.match('?')
  53. name = self.parse_name()
  54. qualifications = self.parse_qualifications()
  55. return '::'.join(qualifications + [name])
  56. def parse_name(self):
  57. if self.lookahead() == '?':
  58. return self.consume() + self.consume()
  59. else:
  60. name = self.parse_id()
  61. self.match('@')
  62. return name
  63. def parse_qualifications(self):
  64. qualifications = []
  65. while self.lookahead() != '@':
  66. name = self.parse_id()
  67. qualifications.append(name)
  68. self.match('@')
  69. return qualifications
  70. def parse_id(self):
  71. s = ''
  72. while True:
  73. c = self.lookahead()
  74. if c.isalnum() or c in '_':
  75. s += c
  76. self.consume()
  77. else:
  78. break
  79. return s
  80. def demangle(name):
  81. if name.startswith('_'):
  82. name = name[1:]
  83. idx = name.rfind('@')
  84. if idx != -1 and name[idx+1:].isdigit():
  85. name = name[:idx]
  86. return name
  87. if name.startswith('?'):
  88. demangler = MsvcDemangler(name)
  89. return demangler.parse()
  90. return name
  91. return name
  92. class Profile:
  93. def __init__(self):
  94. self.symbols = []
  95. self.symbol_cache = {}
  96. self.base_addr = None
  97. self.functions = {}
  98. self.last_stamp = 0
  99. self.stamp_base = 0
  100. def unwrap_stamp(self, stamp):
  101. if stamp < self.last_stamp:
  102. self.stamp_base += 1 << 32
  103. self.last_stamp = stamp
  104. return self.stamp_base + stamp
  105. def read_map(self, mapfile):
  106. # See http://msdn.microsoft.com/en-us/library/k7xkk3e2.aspx
  107. last_addr = 0
  108. last_name = 0
  109. for line in file(mapfile, "rt"):
  110. fields = line.split()
  111. try:
  112. section_offset, name, addr, type, lib_object = fields
  113. except ValueError:
  114. continue
  115. if type != 'f':
  116. continue
  117. addr = int(addr, 16)
  118. name = demangle(name)
  119. if last_addr == addr:
  120. # TODO: handle collapsed functions
  121. #assert last_name == name
  122. continue
  123. self.symbols.append((addr, name))
  124. last_addr = addr
  125. last_name = name
  126. # sort symbols
  127. self.symbols.sort(key = lambda (addr, name): addr)
  128. def lookup_addr(self, addr):
  129. try:
  130. return self.symbol_cache[addr]
  131. except KeyError:
  132. pass
  133. tolerance = 4196
  134. s, e = 0, len(self.symbols)
  135. while s != e:
  136. i = (s + e)//2
  137. start_addr, name = self.symbols[i]
  138. try:
  139. end_addr, next_name = self.symbols[i + 1]
  140. except IndexError:
  141. end_addr = start_addr + tolerance
  142. if addr < start_addr:
  143. e = i
  144. continue
  145. if addr == end_addr:
  146. return next_name
  147. if addr > end_addr:
  148. s = i
  149. continue
  150. return name
  151. return "0x%08x" % addr
  152. def lookup_symbol(self, name):
  153. for symbol_addr, symbol_name in self.symbols:
  154. if name == symbol_name:
  155. return symbol_addr
  156. return 0
  157. def read_data(self, data):
  158. # TODO: compute these automatically
  159. caller_overhead = 672 - 2*144 # __debug_profile_reference2 - 2*__debug_profile_reference1
  160. callee_overhead = 144 # __debug_profile_reference1
  161. callee_overhead -= 48 # tolerance
  162. caller_overhead = callee_overhead
  163. fp = file(data, "rb")
  164. entry_format = "II"
  165. entry_size = struct.calcsize(entry_format)
  166. stack = []
  167. last_stamp = 0
  168. delta = 0
  169. while True:
  170. entry = fp.read(entry_size)
  171. if len(entry) < entry_size:
  172. break
  173. addr_exit, stamp = struct.unpack(entry_format, entry)
  174. if addr_exit == 0 and stamp == 0:
  175. break
  176. addr = addr_exit & 0xfffffffe
  177. exit = addr_exit & 0x00000001
  178. if self.base_addr is None:
  179. ref_addr = self.lookup_symbol('__debug_profile_reference2')
  180. if ref_addr:
  181. self.base_addr = addr - ref_addr
  182. else:
  183. self.base_addr = 0
  184. #print hex(self.base_addr)
  185. rel_addr = addr - self.base_addr
  186. #print hex(addr - self.base_addr)
  187. name = self.lookup_addr(rel_addr)
  188. stamp = self.unwrap_stamp(stamp)
  189. delta += stamp - last_stamp
  190. if not exit:
  191. if verbose >= 2:
  192. print "%10u >> 0x%08x" % (stamp, addr)
  193. if verbose:
  194. print "%10u >> %s" % (stamp, name)
  195. delta -= caller_overhead
  196. stack.append((name, stamp, delta))
  197. delta = 0
  198. else:
  199. if verbose >= 2:
  200. print "%10u << 0x%08x" % (stamp, addr)
  201. if len(stack):
  202. self_time = delta - callee_overhead
  203. entry_name, entry_stamp, delta = stack.pop()
  204. if entry_name != name:
  205. if verbose:
  206. print "%10u << %s" % (stamp, name)
  207. #assert entry_name == name
  208. break
  209. total_time = stamp - entry_stamp
  210. self.functions[entry_name] = self.functions.get(entry_name, 0) + self_time
  211. if verbose:
  212. print "%10u << %s %+u" % (stamp, name, self_time)
  213. else:
  214. delta = 0
  215. last_stamp = stamp
  216. def write_report(self):
  217. total = sum(self.functions.values())
  218. results = self.functions.items()
  219. results.sort(key = lambda (name, time): -time)
  220. for name, time in results:
  221. perc = float(time)/float(total)*100.0
  222. print "%6.03f %s" % (perc, name)
  223. def main():
  224. parser = optparse.OptionParser(
  225. usage="\n\t%prog [options] [file] ...",
  226. version="%%prog %s" % __version__)
  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. '-v', '--verbose',
  237. action="count",
  238. dest="verbose", default=0,
  239. help="verbose output")
  240. (options, args) = parser.parse_args(sys.argv[1:])
  241. global verbose
  242. verbose = options.verbose
  243. profile = Profile()
  244. if options.base is not None:
  245. profile.base_addr = int(options.base, 16)
  246. if options.map is not None:
  247. profile.read_map(options.map)
  248. for arg in args:
  249. profile.read_data(arg)
  250. profile.write_report()
  251. if __name__ == '__main__':
  252. main()