2 ##########################################################################
4 # Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
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:
15 # The above copyright notice and this permission notice (including the
16 # next paragraph) shall be included in all copies or substantial portions
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.
27 ##########################################################################
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
43 class ParseError(Exception):
48 # http://www.kegel.com/mangle.html
50 def __init__(self, symbol):
55 return self._symbol[self._pos]
58 ret = self.lookahead()
63 if self.lookahead() != c:
69 name = self.parse_name()
70 qualifications = self.parse_qualifications()
71 return '::'.join(qualifications + [name])
74 if self.lookahead() == '?':
75 return self.consume() + self.consume()
77 name = self.parse_id()
81 def parse_qualifications(self):
83 while self.lookahead() != '@':
84 name = self.parse_id()
85 qualifications.append(name)
93 if c.isalnum() or c in '_':
102 if name.startswith('_'):
104 idx = name.rfind('@')
105 if idx != -1 and name[idx+1:].isdigit():
108 if name.startswith('?'):
109 demangler = MsvcDemangler(name)
110 return demangler.parse()
118 self.symbol_cache = {}
119 self.base_addr = None
121 def read_map(self, mapfile):
122 # See http://msdn.microsoft.com/en-us/library/k7xkk3e2.aspx
125 for line in file(mapfile, "rt"):
126 fields = line.split()
128 section_offset, name, addr, type, lib_object = fields
133 section, offset = section_offset.split(':')
134 addr = int(offset, 16)
135 self.symbols.append((addr, name))
140 self.symbols.sort(key = lambda (addr, name): addr)
142 def lookup_addr(self, addr):
144 return self.symbol_cache[addr]
149 s, e = 0, len(self.symbols)
152 start_addr, name = self.symbols[i]
154 end_addr, next_name = self.symbols[i + 1]
156 end_addr = start_addr + tolerance
157 if addr < start_addr:
161 return next_name, addr - start_addr
165 return name, addr - start_addr
168 def lookup_symbol(self, name):
169 for symbol_addr, symbol_name in self.symbols:
170 if name == symbol_name:
174 def read_data(self, data):
177 fp = file(data, "rb")
178 entry_format = "IIII"
179 entry_size = struct.calcsize(entry_format)
183 entry = fp.read(entry_size)
184 if len(entry) < entry_size:
186 caller_addr, callee_addr, samples_lo, samples_hi = struct.unpack(entry_format, entry)
187 if caller_addr == 0 and callee_addr == 0:
190 if self.base_addr is None:
191 ref_addr = self.lookup_symbol('___debug_profile_reference@0')
193 self.base_addr = (caller_addr - ref_addr) & ~(options.align - 1)
196 sys.stderr.write('Base addr: %08x\n' % self.base_addr)
198 samples = (samples_hi << 32) | samples_lo
201 caller_raddr = caller_addr - self.base_addr
202 caller_sym, caller_ofs = self.lookup_addr(caller_raddr)
205 caller = profile.functions[caller_sym]
207 caller_name = demangle(caller_sym)
208 caller = Function(caller_sym, caller_name)
209 profile.add_function(caller)
217 caller[SAMPLES] += samples
219 callee_raddr = callee_addr - self.base_addr
220 callee_sym, callee_ofs = self.lookup_addr(callee_raddr)
223 callee = profile.functions[callee_sym]
225 callee_name = demangle(callee_sym)
226 callee = Function(callee_sym, callee_name)
227 profile.add_function(callee)
228 callee[CALLS] = samples
231 callee[CALLS] += samples
233 if caller is not None:
235 call = caller.calls[callee.id]
237 call = Call(callee.id)
238 call[CALLS] = samples
239 caller.add_call(call)
241 call[CALLS] += samples
245 sys.stderr.write('%s+%u: %u\n' % (caller_sym, caller_ofs, samples))
247 sys.stderr.write('%s+%u -> %s+%u: %u\n' % (caller_sym, caller_ofs, callee_sym, callee_ofs, samples))
249 # compute derived data
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)
261 parser = optparse.OptionParser(
262 usage="\n\t%prog [options] [file] ...",
263 version="%%prog %s" % __version__)
265 '-a', '--align', metavar='NUMBER',
266 type="int", dest="align", default=16,
267 help="section alignment")
269 '-m', '--map', metavar='FILE',
270 type="string", dest="map",
273 '-b', '--base', metavar='FILE',
274 type="string", dest="base",
277 '-n', '--node-thres', metavar='PERCENTAGE',
278 type="float", dest="node_thres", default=0.5,
279 help="eliminate nodes below this threshold [default: %default]")
281 '-e', '--edge-thres', metavar='PERCENTAGE',
282 type="float", dest="edge_thres", default=0.1,
283 help="eliminate edges below this threshold [default: %default]")
287 dest="verbose", default=0,
288 help="verbose output")
291 (options, args) = parser.parse_args(sys.argv[1:])
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)
299 profile = reader.read_data(arg)
300 profile.prune(options.node_thres/100.0, options.edge_thres/100.0)
302 dot = DotWriter(output)
303 colormap = TEMPERATURE_COLORMAP
304 dot.graph(profile, colormap)
307 if __name__ == '__main__':