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__':