OLD | NEW |
1 # Copyright 2008 the V8 project authors. All rights reserved. | 1 # Copyright 2008 the V8 project authors. All rights reserved. |
2 # Redistribution and use in source and binary forms, with or without | 2 # Redistribution and use in source and binary forms, with or without |
3 # modification, are permitted provided that the following conditions are | 3 # modification, are permitted provided that the following conditions are |
4 # met: | 4 # met: |
5 # | 5 # |
6 # * Redistributions of source code must retain the above copyright | 6 # * Redistributions of source code must retain the above copyright |
7 # notice, this list of conditions and the following disclaimer. | 7 # notice, this list of conditions and the following disclaimer. |
8 # * Redistributions in binary form must reproduce the above | 8 # * Redistributions in binary form must reproduce the above |
9 # copyright notice, this list of conditions and the following | 9 # copyright notice, this list of conditions and the following |
10 # disclaimer in the documentation and/or other materials provided | 10 # disclaimer in the documentation and/or other materials provided |
11 # with the distribution. | 11 # with the distribution. |
12 # * Neither the name of Google Inc. nor the names of its | 12 # * Neither the name of Google Inc. nor the names of its |
13 # contributors may be used to endorse or promote products derived | 13 # contributors may be used to endorse or promote products derived |
14 # from this software without specific prior written permission. | 14 # from this software without specific prior written permission. |
15 # | 15 # |
16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
20 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 20 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 27 |
28 import csv, splaytree, sys | 28 import csv, splaytree, sys |
| 29 from operator import itemgetter |
29 | 30 |
30 | 31 |
31 class CodeEntry(object): | 32 class CodeEntry(object): |
32 | 33 |
33 def __init__(self, start_addr, name): | 34 def __init__(self, start_addr, name): |
34 self.start_addr = start_addr | 35 self.start_addr = start_addr |
35 self.tick_count = 0 | 36 self.tick_count = 0 |
36 self.name = name | 37 self.name = name |
| 38 self.stacks = {} |
37 | 39 |
38 def Tick(self, pc): | 40 def Tick(self, pc, stack): |
39 self.tick_count += 1 | 41 self.tick_count += 1 |
| 42 if len(stack) > 0: |
| 43 stack.insert(0, self.ToString()) |
| 44 stack_key = tuple(stack) |
| 45 self.stacks[stack_key] = self.stacks.setdefault(stack_key, 0) + 1 |
40 | 46 |
41 def RegionTicks(self): | 47 def RegionTicks(self): |
42 return None | 48 return None |
43 | 49 |
44 def SetStartAddress(self, start_addr): | 50 def SetStartAddress(self, start_addr): |
45 self.start_addr = start_addr | 51 self.start_addr = start_addr |
46 | 52 |
47 def ToString(self): | 53 def ToString(self): |
48 return self.name | 54 return self.name |
49 | 55 |
(...skipping 12 matching lines...) Expand all Loading... |
62 | 68 |
63 class JSCodeEntry(CodeEntry): | 69 class JSCodeEntry(CodeEntry): |
64 | 70 |
65 def __init__(self, start_addr, name, type, size, assembler): | 71 def __init__(self, start_addr, name, type, size, assembler): |
66 CodeEntry.__init__(self, start_addr, name) | 72 CodeEntry.__init__(self, start_addr, name) |
67 self.type = type | 73 self.type = type |
68 self.size = size | 74 self.size = size |
69 self.assembler = assembler | 75 self.assembler = assembler |
70 self.region_ticks = None | 76 self.region_ticks = None |
71 | 77 |
72 def Tick(self, pc): | 78 def Tick(self, pc, stack): |
73 super(JSCodeEntry, self).Tick(pc) | 79 super(JSCodeEntry, self).Tick(pc, stack) |
74 if not pc is None: | 80 if not pc is None: |
75 offset = pc - self.start_addr | 81 offset = pc - self.start_addr |
76 seen = [] | 82 seen = [] |
77 narrowest = None | 83 narrowest = None |
78 narrowest_width = None | 84 narrowest_width = None |
79 for region in self.Regions(): | 85 for region in self.Regions(): |
80 if region.Contains(offset): | 86 if region.Contains(offset): |
81 if (not region.name in seen): | 87 if (not region.name in seen): |
82 seen.append(region.name) | 88 seen.append(region.name) |
83 if narrowest is None or region.Width() < narrowest.Width(): | 89 if narrowest is None or region.Width() < narrowest.Width(): |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 self.log_file = filename | 161 self.log_file = filename |
156 self.included_state = included_state | 162 self.included_state = included_state |
157 try: | 163 try: |
158 logfile = open(filename, 'rb') | 164 logfile = open(filename, 'rb') |
159 except IOError: | 165 except IOError: |
160 sys.exit("Could not open logfile: " + filename) | 166 sys.exit("Could not open logfile: " + filename) |
161 try: | 167 try: |
162 logreader = csv.reader(logfile) | 168 logreader = csv.reader(logfile) |
163 for row in logreader: | 169 for row in logreader: |
164 if row[0] == 'tick': | 170 if row[0] == 'tick': |
165 self.ProcessTick(int(row[1], 16), int(row[2], 16), int(row[3])) | 171 self.ProcessTick(int(row[1], 16), int(row[2], 16), int(row[3]), row[4:
]) |
166 elif row[0] == 'code-creation': | 172 elif row[0] == 'code-creation': |
167 self.ProcessCodeCreation(row[1], int(row[2], 16), int(row[3]), row[4]) | 173 self.ProcessCodeCreation(row[1], int(row[2], 16), int(row[3]), row[4]) |
168 elif row[0] == 'code-move': | 174 elif row[0] == 'code-move': |
169 self.ProcessCodeMove(int(row[1], 16), int(row[2], 16)) | 175 self.ProcessCodeMove(int(row[1], 16), int(row[2], 16)) |
170 elif row[0] == 'code-delete': | 176 elif row[0] == 'code-delete': |
171 self.ProcessCodeDelete(int(row[1], 16)) | 177 self.ProcessCodeDelete(int(row[1], 16)) |
172 elif row[0] == 'shared-library': | 178 elif row[0] == 'shared-library': |
173 self.AddSharedLibraryEntry(row[1], int(row[2], 16), int(row[3], 16)) | 179 self.AddSharedLibraryEntry(row[1], int(row[2], 16), int(row[3], 16)) |
174 self.ParseVMSymbols(row[1], int(row[2], 16), int(row[3], 16)) | 180 self.ParseVMSymbols(row[1], int(row[2], 16), int(row[3], 16)) |
175 elif row[0] == 'begin-code-region': | 181 elif row[0] == 'begin-code-region': |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
230 | 236 |
231 def ProcessEndCodeRegion(self, id, assm, end): | 237 def ProcessEndCodeRegion(self, id, assm, end): |
232 assm = self.pending_assemblers[assm] | 238 assm = self.pending_assemblers[assm] |
233 region = assm.pending_regions.pop(id) | 239 region = assm.pending_regions.pop(id) |
234 region.end_offset = end | 240 region.end_offset = end |
235 assm.regions.append(region) | 241 assm.regions.append(region) |
236 | 242 |
237 def IncludeTick(self, pc, sp, state): | 243 def IncludeTick(self, pc, sp, state): |
238 return (self.included_state is None) or (self.included_state == state) | 244 return (self.included_state is None) or (self.included_state == state) |
239 | 245 |
240 def ProcessTick(self, pc, sp, state): | 246 def FindEntry(self, pc): |
| 247 page = pc >> 12 |
| 248 if page in self.vm_extent: |
| 249 entry = self.cpp_entries.FindGreatestsLessThan(pc) |
| 250 if entry != None: |
| 251 return entry.value |
| 252 else: |
| 253 return entry |
| 254 max = self.js_entries.FindMax() |
| 255 min = self.js_entries.FindMin() |
| 256 if max != None and pc < max.key and pc > min.key: |
| 257 return self.js_entries.FindGreatestsLessThan(pc).value |
| 258 return None |
| 259 |
| 260 def ProcessStack(self, stack): |
| 261 result = [] |
| 262 for frame in stack: |
| 263 if frame.startswith('0x'): |
| 264 entry = self.FindEntry(int(frame, 16)) |
| 265 if entry != None: |
| 266 result.append(entry.ToString()) |
| 267 return result |
| 268 |
| 269 def ProcessTick(self, pc, sp, state, stack): |
241 if not self.IncludeTick(pc, sp, state): | 270 if not self.IncludeTick(pc, sp, state): |
242 self.excluded_number_of_ticks += 1; | 271 self.excluded_number_of_ticks += 1; |
243 return | 272 return |
244 self.total_number_of_ticks += 1 | 273 self.total_number_of_ticks += 1 |
245 page = pc >> 12 | 274 entry = self.FindEntry(pc) |
246 if page in self.vm_extent: | 275 if entry == None: |
247 entry = self.cpp_entries.FindGreatestsLessThan(pc).value | 276 self.unaccounted_number_of_ticks += 1 |
248 if entry.IsSharedLibraryEntry(): | |
249 self.number_of_library_ticks += 1 | |
250 entry.Tick(None) | |
251 return | 277 return |
252 max = self.js_entries.FindMax() | 278 if entry.IsSharedLibraryEntry(): |
253 min = self.js_entries.FindMin() | 279 self.number_of_library_ticks += 1 |
254 if max != None and pc < max.key and pc > min.key: | 280 entry.Tick(pc, self.ProcessStack(stack)) |
255 code_obj = self.js_entries.FindGreatestsLessThan(pc).value | |
256 code_obj.Tick(pc) | |
257 return | |
258 self.unaccounted_number_of_ticks += 1 | |
259 | 281 |
260 def PrintResults(self): | 282 def PrintResults(self): |
261 print('Statistical profiling result from %s, (%d ticks, %d unaccounted, %d e
xcluded).' % | 283 print('Statistical profiling result from %s, (%d ticks, %d unaccounted, %d e
xcluded).' % |
262 (self.log_file, | 284 (self.log_file, |
263 self.total_number_of_ticks, | 285 self.total_number_of_ticks, |
264 self.unaccounted_number_of_ticks, | 286 self.unaccounted_number_of_ticks, |
265 self.excluded_number_of_ticks)) | 287 self.excluded_number_of_ticks)) |
266 if self.total_number_of_ticks > 0: | 288 if self.total_number_of_ticks > 0: |
267 js_entries = self.js_entries.ExportValueList() | 289 js_entries = self.js_entries.ExportValueList() |
268 js_entries.extend(self.deleted_code) | 290 js_entries.extend(self.deleted_code) |
269 cpp_entries = self.cpp_entries.ExportValueList() | 291 cpp_entries = self.cpp_entries.ExportValueList() |
270 # Print the library ticks. | 292 # Print the library ticks. |
271 self.PrintHeader('Shared libraries') | 293 self.PrintHeader('Shared libraries') |
272 self.PrintEntries(cpp_entries, lambda e:e.IsSharedLibraryEntry()) | 294 self.PrintEntries(cpp_entries, lambda e:e.IsSharedLibraryEntry()) |
273 # Print the JavaScript ticks. | 295 # Print the JavaScript ticks. |
274 self.PrintHeader('JavaScript') | 296 self.PrintHeader('JavaScript') |
275 self.PrintEntries(js_entries, lambda e:not e.IsSharedLibraryEntry()) | 297 self.PrintEntries(js_entries, lambda e:not e.IsSharedLibraryEntry()) |
276 # Print the C++ ticks. | 298 # Print the C++ ticks. |
277 self.PrintHeader('C++') | 299 self.PrintHeader('C++') |
278 self.PrintEntries(cpp_entries, lambda e:not e.IsSharedLibraryEntry()) | 300 self.PrintEntries(cpp_entries, lambda e:not e.IsSharedLibraryEntry()) |
| 301 # Print call profile. |
| 302 print('\n [Call profile]:') |
| 303 print(' total call path') |
| 304 js_entries.extend(cpp_entries) |
| 305 self.PrintCallProfile(js_entries) |
279 | 306 |
280 def PrintHeader(self, header_title): | 307 def PrintHeader(self, header_title): |
281 print('\n [%s]:' % header_title) | 308 print('\n [%s]:' % header_title) |
282 print(' total nonlib name') | 309 print(' total nonlib name') |
283 | 310 |
284 def PrintEntries(self, entries, condition): | 311 def PrintEntries(self, entries, condition): |
285 number_of_accounted_ticks = self.total_number_of_ticks - self.unaccounted_nu
mber_of_ticks | 312 number_of_accounted_ticks = self.total_number_of_ticks - self.unaccounted_nu
mber_of_ticks |
286 number_of_non_library_ticks = number_of_accounted_ticks - self.number_of_lib
rary_ticks | 313 number_of_non_library_ticks = number_of_accounted_ticks - self.number_of_lib
rary_ticks |
287 entries.sort(key=lambda e:e.tick_count, reverse=True) | 314 entries.sort(key=lambda e:e.tick_count, reverse=True) |
288 for entry in entries: | 315 for entry in entries: |
(...skipping 13 matching lines...) Expand all Loading... |
302 items = region_ticks.items() | 329 items = region_ticks.items() |
303 items.sort(key=lambda e: e[1][1], reverse=True) | 330 items.sort(key=lambda e: e[1][1], reverse=True) |
304 for (name, ticks) in items: | 331 for (name, ticks) in items: |
305 print(' flat cum') | 332 print(' flat cum') |
306 print(' %(flat)5.1f%% %(accum)5.1f%% %(name)s' %
{ | 333 print(' %(flat)5.1f%% %(accum)5.1f%% %(name)s' %
{ |
307 'flat' : ticks[1] * 100.0 / entry.tick_count, | 334 'flat' : ticks[1] * 100.0 / entry.tick_count, |
308 'accum' : ticks[0] * 100.0 / entry.tick_count, | 335 'accum' : ticks[0] * 100.0 / entry.tick_count, |
309 'name': name | 336 'name': name |
310 }) | 337 }) |
311 | 338 |
| 339 def PrintCallProfile(self, entries): |
| 340 all_stacks = {} |
| 341 total_stacks = 0 |
| 342 for entry in entries: |
| 343 all_stacks.update(entry.stacks) |
| 344 for count in entry.stacks.itervalues(): |
| 345 total_stacks += count |
| 346 all_stacks_items = all_stacks.items(); |
| 347 all_stacks_items.sort(key = itemgetter(1), reverse=True) |
| 348 for stack, count in all_stacks_items: |
| 349 total_percentage = count * 100.0 / total_stacks |
| 350 print(' %(total)5.1f%% %(call_path)s' % { |
| 351 'total' : total_percentage, |
| 352 'call_path' : stack[0] + ' <- ' + stack[1] |
| 353 }) |
| 354 |
312 if __name__ == '__main__': | 355 if __name__ == '__main__': |
313 sys.exit('You probably want to run windows-tick-processor.py or linux-tick-pro
cessor.py.') | 356 sys.exit('You probably want to run windows-tick-processor.py or linux-tick-pro
cessor.py.') |
OLD | NEW |