| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- #!/usr/bin/env python
- ##########################################################################
- #
- # Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
- # All Rights Reserved.
- #
- # Permission is hereby granted, free of charge, to any person obtaining a
- # copy of this software and associated documentation files (the
- # "Software"), to deal in the Software without restriction, including
- # without limitation the rights to use, copy, modify, merge, publish,
- # distribute, sub license, and/or sell copies of the Software, and to
- # permit persons to whom the Software is furnished to do so, subject to
- # the following conditions:
- #
- # The above copyright notice and this permission notice (including the
- # next paragraph) shall be included in all copies or substantial portions
- # of the Software.
- #
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
- # IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
- # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- #
- ##########################################################################
-
-
- import sys
- import optparse
- import re
- import struct
-
-
- __version__ = '0.1'
-
- verbose = False
-
-
- class ParseError(Exception):
- pass
-
-
- class MsvcDemangler:
- # http://www.kegel.com/mangle.html
-
- def __init__(self, symbol):
- self._symbol = symbol
- self._pos = 0
-
- def lookahead(self):
- return self._symbol[self._pos]
-
- def consume(self):
- ret = self.lookahead()
- self._pos += 1
- return ret
-
- def match(self, c):
- if self.lookahead() != c:
- raise ParseError
- self.consume()
-
- def parse(self):
- self.match('?')
- name = self.parse_name()
- qualifications = self.parse_qualifications()
- return '::'.join(qualifications + [name])
-
- def parse_name(self):
- if self.lookahead() == '?':
- return self.consume() + self.consume()
- else:
- name = self.parse_id()
- self.match('@')
- return name
-
- def parse_qualifications(self):
- qualifications = []
- while self.lookahead() != '@':
- name = self.parse_id()
- qualifications.append(name)
- self.match('@')
- return qualifications
-
- def parse_id(self):
- s = ''
- while True:
- c = self.lookahead()
- if c.isalnum() or c in '_':
- s += c
- self.consume()
- else:
- break
- return s
-
-
- def demangle(name):
- if name.startswith('_'):
- name = name[1:]
- idx = name.rfind('@')
- if idx != -1 and name[idx+1:].isdigit():
- name = name[:idx]
- return name
- if name.startswith('?'):
- demangler = MsvcDemangler(name)
- return demangler.parse()
-
- return name
- return name
-
-
- class Profile:
-
- def __init__(self):
- self.symbols = []
- self.symbol_cache = {}
- self.base_addr = None
- self.functions = {}
- self.last_stamp = 0
- self.stamp_base = 0
-
- def unwrap_stamp(self, stamp):
- if stamp < self.last_stamp:
- self.stamp_base += 1 << 32
- self.last_stamp = stamp
- return self.stamp_base + stamp
-
- def read_map(self, mapfile):
- # See http://msdn.microsoft.com/en-us/library/k7xkk3e2.aspx
- last_addr = 0
- last_name = 0
- for line in file(mapfile, "rt"):
- fields = line.split()
- try:
- section_offset, name, addr, type, lib_object = fields
- except ValueError:
- continue
- if type != 'f':
- continue
- addr = int(addr, 16)
- name = demangle(name)
- if last_addr == addr:
- # TODO: handle collapsed functions
- #assert last_name == name
- continue
- self.symbols.append((addr, name))
- last_addr = addr
- last_name = name
-
- # sort symbols
- self.symbols.sort(key = lambda (addr, name): addr)
-
- def lookup_addr(self, addr):
- try:
- return self.symbol_cache[addr]
- except KeyError:
- pass
-
- tolerance = 4196
- s, e = 0, len(self.symbols)
- while s != e:
- i = (s + e)//2
- start_addr, name = self.symbols[i]
- try:
- end_addr, next_name = self.symbols[i + 1]
- except IndexError:
- end_addr = start_addr + tolerance
- if addr < start_addr:
- e = i
- continue
- if addr == end_addr:
- return next_name
- if addr > end_addr:
- s = i
- continue
- return name
- return "0x%08x" % addr
-
- def lookup_symbol(self, name):
- for symbol_addr, symbol_name in self.symbols:
- if name == symbol_name:
- return symbol_addr
- return 0
-
- def read_data(self, data):
- # TODO: compute these automatically
- caller_overhead = 672 - 2*144 # __debug_profile_reference2 - 2*__debug_profile_reference1
- callee_overhead = 144 # __debug_profile_reference1
- callee_overhead -= 48 # tolerance
- caller_overhead = callee_overhead
-
- fp = file(data, "rb")
- entry_format = "II"
- entry_size = struct.calcsize(entry_format)
- stack = []
- last_stamp = 0
- delta = 0
- while True:
- entry = fp.read(entry_size)
- if len(entry) < entry_size:
- break
- addr_exit, stamp = struct.unpack(entry_format, entry)
- if addr_exit == 0 and stamp == 0:
- break
- addr = addr_exit & 0xfffffffe
- exit = addr_exit & 0x00000001
-
- if self.base_addr is None:
- ref_addr = self.lookup_symbol('__debug_profile_reference2')
- if ref_addr:
- self.base_addr = addr - ref_addr
- else:
- self.base_addr = 0
- #print hex(self.base_addr)
- rel_addr = addr - self.base_addr
- #print hex(addr - self.base_addr)
-
- name = self.lookup_addr(rel_addr)
- stamp = self.unwrap_stamp(stamp)
-
- delta += stamp - last_stamp
-
- if not exit:
- if verbose >= 2:
- print "%10u >> 0x%08x" % (stamp, addr)
- if verbose:
- print "%10u >> %s" % (stamp, name)
- delta -= caller_overhead
- stack.append((name, stamp, delta))
- delta = 0
- else:
- if verbose >= 2:
- print "%10u << 0x%08x" % (stamp, addr)
- if len(stack):
- self_time = delta - callee_overhead
- entry_name, entry_stamp, delta = stack.pop()
- if entry_name != name:
- if verbose:
- print "%10u << %s" % (stamp, name)
- #assert entry_name == name
- break
- total_time = stamp - entry_stamp
- self.functions[entry_name] = self.functions.get(entry_name, 0) + self_time
- if verbose:
- print "%10u << %s %+u" % (stamp, name, self_time)
- else:
- delta = 0
-
- last_stamp = stamp
-
- def write_report(self):
- total = sum(self.functions.values())
- results = self.functions.items()
- results.sort(key = lambda (name, time): -time)
- for name, time in results:
- perc = float(time)/float(total)*100.0
- print "%6.03f %s" % (perc, name)
-
-
- def main():
- parser = optparse.OptionParser(
- usage="\n\t%prog [options] [file] ...",
- version="%%prog %s" % __version__)
- parser.add_option(
- '-m', '--map', metavar='FILE',
- type="string", dest="map",
- help="map file")
- parser.add_option(
- '-b', '--base', metavar='FILE',
- type="string", dest="base",
- help="base addr")
- parser.add_option(
- '-v', '--verbose',
- action="count",
- dest="verbose", default=0,
- help="verbose output")
- (options, args) = parser.parse_args(sys.argv[1:])
-
- global verbose
- verbose = options.verbose
-
- profile = Profile()
- if options.base is not None:
- profile.base_addr = int(options.base, 16)
- if options.map is not None:
- profile.read_map(options.map)
- for arg in args:
- profile.read_data(arg)
- profile.write_report()
-
-
- if __name__ == '__main__':
- main()
|