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 ##########################################################################
39 class ParseError(Exception):
44 # http://www.kegel.com/mangle.html
46 def __init__(self
, symbol
):
51 return self
._symbol
[self
._pos
]
54 ret
= self
.lookahead()
59 if self
.lookahead() != c
:
65 name
= self
.parse_name()
66 qualifications
= self
.parse_qualifications()
67 return '::'.join(qualifications
+ [name
])
70 if self
.lookahead() == '?':
71 return self
.consume() + self
.consume()
73 name
= self
.parse_id()
77 def parse_qualifications(self
):
79 while self
.lookahead() != '@':
80 name
= self
.parse_id()
81 qualifications
.append(name
)
89 if c
.isalnum() or c
in '_':
98 if name
.startswith('_'):
100 idx
= name
.rfind('@')
101 if idx
!= -1 and name
[idx
+1:].isdigit():
104 if name
.startswith('?'):
105 demangler
= MsvcDemangler(name
)
106 return demangler
.parse()
116 self
.symbol_cache
= {}
117 self
.base_addr
= None
122 def unwrap_stamp(self
, stamp
):
123 if stamp
< self
.last_stamp
:
124 self
.stamp_base
+= 1 << 32
125 self
.last_stamp
= stamp
126 return self
.stamp_base
+ stamp
128 def read_map(self
, mapfile
):
129 # See http://msdn.microsoft.com/en-us/library/k7xkk3e2.aspx
132 for line
in file(mapfile
, "rt"):
133 fields
= line
.split()
135 section_offset
, name
, addr
, type, lib_object
= fields
140 section
, offset
= section_offset
.split(':')
141 addr
= int(offset
, 16)
142 name
= demangle(name
)
143 if last_addr
== addr
:
144 # TODO: handle collapsed functions
145 #assert last_name == name
147 self
.symbols
.append((addr
, name
))
152 self
.symbols
.sort(key
= lambda (addr
, name
): addr
)
154 def lookup_addr(self
, addr
):
156 return self
.symbol_cache
[addr
]
161 s
, e
= 0, len(self
.symbols
)
164 start_addr
, name
= self
.symbols
[i
]
166 end_addr
, next_name
= self
.symbols
[i
+ 1]
168 end_addr
= start_addr
+ tolerance
169 if addr
< start_addr
:
178 return "0x%08x" % addr
180 def lookup_symbol(self
, name
):
181 for symbol_addr
, symbol_name
in self
.symbols
:
182 if name
== symbol_name
:
186 def read_data(self
, data
):
187 # TODO: compute these automatically
188 caller_overhead
= 672 - 2*144 # __debug_profile_reference2 - 2*__debug_profile_reference1
189 callee_overhead
= 144 # __debug_profile_reference1
190 callee_overhead
-= 48 # tolerance
191 caller_overhead
= callee_overhead
193 fp
= file(data
, "rb")
195 entry_size
= struct
.calcsize(entry_format
)
200 entry
= fp
.read(entry_size
)
201 if len(entry
) < entry_size
:
203 addr_exit
, stamp
= struct
.unpack(entry_format
, entry
)
204 if addr_exit
== 0 and stamp
== 0:
206 addr
= addr_exit
& 0xfffffffe
207 exit
= addr_exit
& 0x00000001
209 if self
.base_addr
is None:
210 ref_addr
= self
.lookup_symbol('__debug_profile_reference2')
212 self
.base_addr
= (addr
- ref_addr
) & ~
(options
.align
- 1)
215 #print hex(self.base_addr)
216 rel_addr
= addr
- self
.base_addr
217 #print hex(addr - self.base_addr)
219 name
= self
.lookup_addr(rel_addr
)
220 stamp
= self
.unwrap_stamp(stamp
)
222 delta
+= stamp
- last_stamp
225 if options
.verbose
>= 2:
226 print "%10u >> 0x%08x" % (stamp
, addr
)
228 print "%10u >> %s" % (stamp
, name
)
229 delta
-= caller_overhead
230 stack
.append((name
, stamp
, delta
))
233 if options
.verbose
>= 2:
234 print "%10u << 0x%08x" % (stamp
, addr
)
236 self_time
= delta
- callee_overhead
237 entry_name
, entry_stamp
, delta
= stack
.pop()
238 if entry_name
!= name
:
240 print "%10u << %s" % (stamp
, name
)
241 #assert entry_name == name
243 total_time
= stamp
- entry_stamp
244 self
.functions
[entry_name
] = self
.functions
.get(entry_name
, 0) + self_time
246 print "%10u << %s %+u" % (stamp
, name
, self_time
)
252 def write_report(self
):
253 total
= sum(self
.functions
.values())
254 results
= self
.functions
.items()
255 results
.sort(key
= lambda (name
, time
): -time
)
256 for name
, time
in results
:
257 perc
= float(time
)/float(total
)*100.0
258 print "%6.03f %s" % (perc
, name
)
262 parser
= optparse
.OptionParser(
263 usage
="\n\t%prog [options] [file] ...",
264 version
="%%prog %s" % __version__
)
266 '-a', '--align', metavar
='NUMBER',
267 type="int", dest
="align", default
=16,
268 help="section alignment")
270 '-m', '--map', metavar
='FILE',
271 type="string", dest
="map",
274 '-b', '--base', metavar
='FILE',
275 type="string", dest
="base",
280 dest
="verbose", default
=0,
281 help="verbose output")
284 (options
, args
) = parser
.parse_args(sys
.argv
[1:])
287 if options
.base
is not None:
288 profile
.base_addr
= int(options
.base
, 16)
289 if options
.map is not None:
290 profile
.read_map(options
.map)
292 profile
.read_data(arg
)
293 profile
.write_report()
296 if __name__
== '__main__':