Merge branch 'mesa_7_5_branch'
[mesa.git] / bin / win32kprof.py
index 1876fbc0670c474a18b9804c559cbe061431dc3d..c36317d23aed05ddaf51b79309ec4a52a8bd0dd4 100755 (executable)
@@ -32,267 +32,278 @@ import optparse
 import re
 import struct
 
+from gprof2dot import Call, Function, Profile
+from gprof2dot import CALLS, SAMPLES, TIME, TIME_RATIO, TOTAL_TIME, TOTAL_TIME_RATIO
+from gprof2dot import DotWriter, TEMPERATURE_COLORMAP
+
 
 __version__ = '0.1'
 
 
 class ParseError(Exception):
-       pass
+    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
+    # 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
-                       section, offset = section_offset.split(':')
-                       addr = int(offset, 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) & ~(options.align - 1)
-                               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 options.verbose >= 2:
-                                       print "%10u >> 0x%08x" % (stamp, addr)
-                               if options.verbose:
-                                       print "%10u >> %s" % (stamp, name)
-                               delta -= caller_overhead
-                               stack.append((name, stamp, delta))
-                               delta = 0
-                       else:
-                               if options.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 options.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 options.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)
+    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
+
+
+class Reader:
+
+    def __init__(self):
+        self.symbols = []
+        self.symbol_cache = {}
+        self.base_addr = None
+    
+    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
+            section, offset = section_offset.split(':')
+            addr = int(offset, 16)
+            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, addr - start_addr
+            if addr > end_addr:
+                s = i
+                continue
+            return name, addr - start_addr
+        raise ValueError
+
+    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):
+        profile = Profile()
+
+        fp = file(data, "rb")
+        entry_format = "IIII"
+        entry_size = struct.calcsize(entry_format)
+        caller = None
+        caller_stack = []
+        while True:
+            entry = fp.read(entry_size)
+            if len(entry) < entry_size:
+                break
+            caller_addr, callee_addr, samples_lo, samples_hi = struct.unpack(entry_format, entry)
+            if caller_addr == 0 and callee_addr == 0:
+                continue
+
+            if self.base_addr is None:
+                ref_addr = self.lookup_symbol('___debug_profile_reference@0')
+                if ref_addr:
+                    self.base_addr = (caller_addr - ref_addr) & ~(options.align - 1)
+                else:
+                    self.base_addr = 0
+                sys.stderr.write('Base addr: %08x\n' % self.base_addr)
+
+            samples = (samples_hi << 32) | samples_lo
+            
+            try:
+                caller_raddr = caller_addr - self.base_addr
+                caller_sym, caller_ofs = self.lookup_addr(caller_raddr)
+
+                try:
+                    caller = profile.functions[caller_sym]
+                except KeyError:
+                    caller_name = demangle(caller_sym)
+                    caller = Function(caller_sym, caller_name)
+                    profile.add_function(caller)
+                    caller[CALLS] = 0
+                    caller[SAMPLES] = 0
+            except ValueError:
+                caller = None
+
+            if not callee_addr:
+                if caller:
+                    caller[SAMPLES] += samples
+            else:
+                callee_raddr = callee_addr - self.base_addr
+                callee_sym, callee_ofs = self.lookup_addr(callee_raddr)
+
+                try:
+                    callee = profile.functions[callee_sym]
+                except KeyError:
+                    callee_name = demangle(callee_sym)
+                    callee = Function(callee_sym, callee_name)
+                    profile.add_function(callee)
+                    callee[CALLS] = samples
+                    callee[SAMPLES] = 0
+                else:
+                    callee[CALLS] += samples
+
+                if caller is not None:
+                    try:
+                        call = caller.calls[callee.id]
+                    except KeyError:
+                        call = Call(callee.id)
+                        call[CALLS] = samples
+                        caller.add_call(call)
+                    else:
+                        call[CALLS] += samples
+            
+            if options.verbose:
+                if not callee_addr:
+                    sys.stderr.write('%s+%u: %u\n' % (caller_sym, caller_ofs, samples))
+                else:
+                    sys.stderr.write('%s+%u -> %s+%u: %u\n' % (caller_sym, caller_ofs, callee_sym, callee_ofs, samples))
+
+        # compute derived data
+        profile.validate()
+        profile.find_cycles()
+        profile.aggregate(SAMPLES)
+        profile.ratio(TIME_RATIO, SAMPLES)
+        profile.call_ratios(CALLS)
+        profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO)
+
+        return profile
 
 
 def main():
-               parser = optparse.OptionParser(
-                       usage="\n\t%prog [options] [file] ...",
-                       version="%%prog %s" % __version__)
-               parser.add_option(
-                       '-a', '--align', metavar='NUMBER',
-                       type="int", dest="align", default=16,
-                       help="section alignment")
-               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")
-
-               global options
-               (options, args) = parser.parse_args(sys.argv[1:])
-
-               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()
+    parser = optparse.OptionParser(
+        usage="\n\t%prog [options] [file] ...",
+        version="%%prog %s" % __version__)
+    parser.add_option(
+        '-a', '--align', metavar='NUMBER',
+        type="int", dest="align", default=16,
+        help="section alignment")
+    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(
+        '-n', '--node-thres', metavar='PERCENTAGE',
+        type="float", dest="node_thres", default=0.5,
+        help="eliminate nodes below this threshold [default: %default]")
+    parser.add_option(
+        '-e', '--edge-thres', metavar='PERCENTAGE',
+        type="float", dest="edge_thres", default=0.1,
+        help="eliminate edges below this threshold [default: %default]")
+    parser.add_option(
+        '-v', '--verbose',
+        action="count",
+        dest="verbose", default=0,
+        help="verbose output")
+
+    global options
+    (options, args) = parser.parse_args(sys.argv[1:])
+
+    reader = Reader()
+    if options.base is not None:
+        reader.base_addr = int(options.base, 16)
+    if options.map is not None:
+        reader.read_map(options.map)
+    for arg in args:
+        profile = reader.read_data(arg)
+        profile.prune(options.node_thres/100.0, options.edge_thres/100.0)
+        output = sys.stdout
+        dot = DotWriter(output)
+        colormap = TEMPERATURE_COLORMAP
+        dot.graph(profile, colormap)
 
 
 if __name__ == '__main__':
-       main()
+    main()