OSDN Git Service

egl: Improve logging facility.
[android-x86/external-mesa.git] / bin / win32kprof.py
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
29
30 import sys
31 import optparse
32 import re
33 import struct
34
35 from gprof2dot import Call, Function, Profile
36 from gprof2dot import CALLS, SAMPLES, TIME, TIME_RATIO, TOTAL_TIME, TOTAL_TIME_RATIO
37 from gprof2dot import DotWriter, TEMPERATURE_COLORMAP
38
39
40 __version__ = '0.1'
41
42
43 class ParseError(Exception):
44     pass
45
46
47 class MsvcDemangler:
48     # http://www.kegel.com/mangle.html
49
50     def __init__(self, symbol):
51         self._symbol = symbol
52         self._pos = 0
53
54     def lookahead(self):
55         return self._symbol[self._pos]
56
57     def consume(self):
58         ret = self.lookahead()
59         self._pos += 1
60         return ret
61     
62     def match(self, c):
63         if self.lookahead() != c:
64             raise ParseError
65         self.consume()
66
67     def parse(self):
68         self.match('?')
69         name = self.parse_name()
70         qualifications = self.parse_qualifications()
71         return '::'.join(qualifications + [name])
72
73     def parse_name(self):
74         if self.lookahead() == '?':
75             return self.consume() + self.consume()
76         else:
77             name = self.parse_id()
78             self.match('@')
79             return name
80
81     def parse_qualifications(self):
82         qualifications = []
83         while self.lookahead() != '@':
84             name = self.parse_id()
85             qualifications.append(name)
86             self.match('@')
87         return qualifications
88
89     def parse_id(self):
90         s = ''
91         while True:
92             c = self.lookahead()
93             if c.isalnum() or c in '_':
94                 s += c
95                 self.consume()
96             else:
97                 break
98         return s
99
100
101 def demangle(name):
102     if name.startswith('_'):
103         name = name[1:]
104         idx = name.rfind('@')
105         if idx != -1 and name[idx+1:].isdigit():
106             name = name[:idx]
107         return name
108     if name.startswith('?'):
109         demangler = MsvcDemangler(name)
110         return demangler.parse()
111     return name
112
113
114 class Reader:
115
116     def __init__(self):
117         self.symbols = []
118         self.symbol_cache = {}
119         self.base_addr = None
120     
121     def read_map(self, mapfile):
122         # See http://msdn.microsoft.com/en-us/library/k7xkk3e2.aspx
123         last_addr = 0
124         last_name = 0
125         for line in file(mapfile, "rt"):
126             fields = line.split()
127             try:
128                 section_offset, name, addr, type, lib_object = fields
129             except ValueError:
130                 continue
131             if type != 'f':
132                 continue
133             section, offset = section_offset.split(':')
134             addr = int(offset, 16)
135             self.symbols.append((addr, name))
136             last_addr = addr
137             last_name = name
138
139         # sort symbols
140         self.symbols.sort(key = lambda (addr, name): addr)
141
142     def lookup_addr(self, addr):
143         try:
144             return self.symbol_cache[addr]
145         except KeyError:
146             pass
147
148         tolerance = 4196
149         s, e = 0, len(self.symbols)
150         while s != e:
151             i = (s + e)//2
152             start_addr, name = self.symbols[i]
153             try:
154                 end_addr, next_name = self.symbols[i + 1]
155             except IndexError:
156                 end_addr = start_addr + tolerance
157             if addr < start_addr:
158                 e = i
159                 continue
160             if addr == end_addr:
161                 return next_name, addr - start_addr
162             if addr > end_addr:
163                 s = i
164                 continue
165             return name, addr - start_addr
166         raise ValueError
167
168     def lookup_symbol(self, name):
169         for symbol_addr, symbol_name in self.symbols:
170             if name == symbol_name:
171                 return symbol_addr
172         return 0
173
174     def read_data(self, data):
175         profile = Profile()
176
177         fp = file(data, "rb")
178         entry_format = "IIII"
179         entry_size = struct.calcsize(entry_format)
180         caller = None
181         caller_stack = []
182         while True:
183             entry = fp.read(entry_size)
184             if len(entry) < entry_size:
185                 break
186             caller_addr, callee_addr, samples_lo, samples_hi = struct.unpack(entry_format, entry)
187             if caller_addr == 0 and callee_addr == 0:
188                 continue
189
190             if self.base_addr is None:
191                 ref_addr = self.lookup_symbol('___debug_profile_reference@0')
192                 if ref_addr:
193                     self.base_addr = (caller_addr - ref_addr) & ~(options.align - 1)
194                 else:
195                     self.base_addr = 0
196                 sys.stderr.write('Base addr: %08x\n' % self.base_addr)
197
198             samples = (samples_hi << 32) | samples_lo
199             
200             try:
201                 caller_raddr = caller_addr - self.base_addr
202                 caller_sym, caller_ofs = self.lookup_addr(caller_raddr)
203
204                 try:
205                     caller = profile.functions[caller_sym]
206                 except KeyError:
207                     caller_name = demangle(caller_sym)
208                     caller = Function(caller_sym, caller_name)
209                     profile.add_function(caller)
210                     caller[CALLS] = 0
211                     caller[SAMPLES] = 0
212             except ValueError:
213                 caller = None
214
215             if not callee_addr:
216                 if caller:
217                     caller[SAMPLES] += samples
218             else:
219                 callee_raddr = callee_addr - self.base_addr
220                 callee_sym, callee_ofs = self.lookup_addr(callee_raddr)
221
222                 try:
223                     callee = profile.functions[callee_sym]
224                 except KeyError:
225                     callee_name = demangle(callee_sym)
226                     callee = Function(callee_sym, callee_name)
227                     profile.add_function(callee)
228                     callee[CALLS] = samples
229                     callee[SAMPLES] = 0
230                 else:
231                     callee[CALLS] += samples
232
233                 if caller is not None:
234                     try:
235                         call = caller.calls[callee.id]
236                     except KeyError:
237                         call = Call(callee.id)
238                         call[CALLS] = samples
239                         caller.add_call(call)
240                     else:
241                         call[CALLS] += samples
242             
243             if options.verbose:
244                 if not callee_addr:
245                     sys.stderr.write('%s+%u: %u\n' % (caller_sym, caller_ofs, samples))
246                 else:
247                     sys.stderr.write('%s+%u -> %s+%u: %u\n' % (caller_sym, caller_ofs, callee_sym, callee_ofs, samples))
248
249         # compute derived data
250         profile.validate()
251         profile.find_cycles()
252         profile.aggregate(SAMPLES)
253         profile.ratio(TIME_RATIO, SAMPLES)
254         profile.call_ratios(CALLS)
255         profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO)
256
257         return profile
258
259
260 def main():
261     parser = optparse.OptionParser(
262         usage="\n\t%prog [options] [file] ...",
263         version="%%prog %s" % __version__)
264     parser.add_option(
265         '-a', '--align', metavar='NUMBER',
266         type="int", dest="align", default=16,
267         help="section alignment")
268     parser.add_option(
269         '-m', '--map', metavar='FILE',
270         type="string", dest="map",
271         help="map file")
272     parser.add_option(
273         '-b', '--base', metavar='FILE',
274         type="string", dest="base",
275         help="base addr")
276     parser.add_option(
277         '-n', '--node-thres', metavar='PERCENTAGE',
278         type="float", dest="node_thres", default=0.5,
279         help="eliminate nodes below this threshold [default: %default]")
280     parser.add_option(
281         '-e', '--edge-thres', metavar='PERCENTAGE',
282         type="float", dest="edge_thres", default=0.1,
283         help="eliminate edges below this threshold [default: %default]")
284     parser.add_option(
285         '-v', '--verbose',
286         action="count",
287         dest="verbose", default=0,
288         help="verbose output")
289
290     global options
291     (options, args) = parser.parse_args(sys.argv[1:])
292
293     reader = Reader()
294     if options.base is not None:
295         reader.base_addr = int(options.base, 16)
296     if options.map is not None:
297         reader.read_map(options.map)
298     for arg in args:
299         profile = reader.read_data(arg)
300         profile.prune(options.node_thres/100.0, options.edge_thres/100.0)
301         output = sys.stdout
302         dot = DotWriter(output)
303         colormap = TEMPERATURE_COLORMAP
304         dot.graph(profile, colormap)
305
306
307 if __name__ == '__main__':
308     main()
309