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, re |
29 from operator import itemgetter | 29 from operator import itemgetter |
30 import getopt, os | 30 import getopt, os, string |
31 | 31 |
32 class CodeEntry(object): | 32 class CodeEntry(object): |
33 | 33 |
34 def __init__(self, start_addr, name): | 34 def __init__(self, start_addr, name): |
35 self.start_addr = start_addr | 35 self.start_addr = start_addr |
36 self.tick_count = 0 | 36 self.tick_count = 0 |
37 self.name = name | 37 self.name = name |
38 self.stacks = {} | 38 self.stacks = {} |
39 | 39 |
40 def Tick(self, pc, stack): | 40 def Tick(self, pc, stack): |
41 self.tick_count += 1 | 41 self.tick_count += 1 |
42 if len(stack) > 0: | 42 if len(stack) > 0: |
43 stack.insert(0, self.ToString()) | 43 stack.insert(0, self.ToString()) |
44 stack_key = tuple(stack) | 44 stack_key = tuple(stack) |
45 self.stacks[stack_key] = self.stacks.setdefault(stack_key, 0) + 1 | 45 self.stacks[stack_key] = self.stacks.setdefault(stack_key, 0) + 1 |
46 | 46 |
47 def RegionTicks(self): | 47 def RegionTicks(self): |
48 return None | 48 return None |
49 | 49 |
50 def SetStartAddress(self, start_addr): | 50 def SetStartAddress(self, start_addr): |
51 self.start_addr = start_addr | 51 self.start_addr = start_addr |
52 | 52 |
53 def ToString(self): | 53 def ToString(self): |
54 return self.name | 54 return self.name |
55 | 55 |
56 def IsSharedLibraryEntry(self): | 56 def IsSharedLibraryEntry(self): |
57 return False | 57 return False |
58 | 58 |
| 59 def IsICEntry(self): |
| 60 return False |
| 61 |
59 | 62 |
60 class SharedLibraryEntry(CodeEntry): | 63 class SharedLibraryEntry(CodeEntry): |
61 | 64 |
62 def __init__(self, start_addr, name): | 65 def __init__(self, start_addr, name): |
63 CodeEntry.__init__(self, start_addr, name) | 66 CodeEntry.__init__(self, start_addr, name) |
64 | 67 |
65 def IsSharedLibraryEntry(self): | 68 def IsSharedLibraryEntry(self): |
66 return True | 69 return True |
67 | 70 |
68 | 71 |
69 class JSCodeEntry(CodeEntry): | 72 class JSCodeEntry(CodeEntry): |
70 | 73 |
71 def __init__(self, start_addr, name, type, size, assembler): | 74 def __init__(self, start_addr, name, type, size, assembler): |
72 CodeEntry.__init__(self, start_addr, name) | 75 CodeEntry.__init__(self, start_addr, name) |
73 self.type = type | 76 self.type = type |
74 self.size = size | 77 self.size = size |
75 self.assembler = assembler | 78 self.assembler = assembler |
76 self.region_ticks = None | 79 self.region_ticks = None |
| 80 self.builtin_ic_re = re.compile('^(Keyed)?(Call|Load|Store)IC_') |
77 | 81 |
78 def Tick(self, pc, stack): | 82 def Tick(self, pc, stack): |
79 super(JSCodeEntry, self).Tick(pc, stack) | 83 super(JSCodeEntry, self).Tick(pc, stack) |
80 if not pc is None: | 84 if not pc is None: |
81 offset = pc - self.start_addr | 85 offset = pc - self.start_addr |
82 seen = [] | 86 seen = [] |
83 narrowest = None | 87 narrowest = None |
84 narrowest_width = None | 88 narrowest_width = None |
85 for region in self.Regions(): | 89 for region in self.Regions(): |
86 if region.Contains(offset): | 90 if region.Contains(offset): |
(...skipping 22 matching lines...) Expand all Loading... |
109 return [] | 113 return [] |
110 | 114 |
111 def ToString(self): | 115 def ToString(self): |
112 name = self.name | 116 name = self.name |
113 if name == '': | 117 if name == '': |
114 name = '<anonymous>' | 118 name = '<anonymous>' |
115 elif name.startswith(' '): | 119 elif name.startswith(' '): |
116 name = '<anonymous>' + name | 120 name = '<anonymous>' + name |
117 return self.type + ': ' + name | 121 return self.type + ': ' + name |
118 | 122 |
| 123 def IsICEntry(self): |
| 124 return self.type in ('CallIC', 'LoadIC', 'StoreIC') or \ |
| 125 (self.type == 'Builtin' and self.builtin_ic_re.match(self.name)) |
| 126 |
119 | 127 |
120 class CodeRegion(object): | 128 class CodeRegion(object): |
121 | 129 |
122 def __init__(self, start_offset, name): | 130 def __init__(self, start_offset, name): |
123 self.start_offset = start_offset | 131 self.start_offset = start_offset |
124 self.name = name | 132 self.name = name |
125 self.end_offset = None | 133 self.end_offset = None |
126 | 134 |
127 def Contains(self, pc): | 135 def Contains(self, pc): |
128 return (self.start_offset <= pc) and (pc <= self.end_offset) | 136 return (self.start_offset <= pc) and (pc <= self.end_offset) |
(...skipping 23 matching lines...) Expand all Loading... |
152 self.assemblers = {} | 160 self.assemblers = {} |
153 self.js_entries = splaytree.SplayTree() | 161 self.js_entries = splaytree.SplayTree() |
154 self.cpp_entries = splaytree.SplayTree() | 162 self.cpp_entries = splaytree.SplayTree() |
155 self.total_number_of_ticks = 0 | 163 self.total_number_of_ticks = 0 |
156 self.number_of_library_ticks = 0 | 164 self.number_of_library_ticks = 0 |
157 self.unaccounted_number_of_ticks = 0 | 165 self.unaccounted_number_of_ticks = 0 |
158 self.excluded_number_of_ticks = 0 | 166 self.excluded_number_of_ticks = 0 |
159 # Flag indicating whether to ignore unaccounted ticks in the report | 167 # Flag indicating whether to ignore unaccounted ticks in the report |
160 self.ignore_unknown = False | 168 self.ignore_unknown = False |
161 | 169 |
162 def ProcessLogfile(self, filename, included_state = None, ignore_unknown = Fal
se): | 170 def ProcessLogfile(self, filename, included_state = None, ignore_unknown = Fal
se, separate_ic = False): |
163 self.log_file = filename | 171 self.log_file = filename |
164 self.included_state = included_state | 172 self.included_state = included_state |
165 self.ignore_unknown = ignore_unknown | 173 self.ignore_unknown = ignore_unknown |
| 174 self.separate_ic = separate_ic |
166 | 175 |
167 try: | 176 try: |
168 logfile = open(filename, 'rb') | 177 logfile = open(filename, 'rb') |
169 except IOError: | 178 except IOError: |
170 sys.exit("Could not open logfile: " + filename) | 179 sys.exit("Could not open logfile: " + filename) |
171 try: | 180 try: |
172 logreader = csv.reader(logfile) | 181 logreader = csv.reader(logfile) |
173 for row in logreader: | 182 for row in logreader: |
174 if row[0] == 'tick': | 183 if row[0] == 'tick': |
175 self.ProcessTick(int(row[1], 16), int(row[2], 16), int(row[3]), row[4:
]) | 184 self.ProcessTick(int(row[1], 16), int(row[2], 16), int(row[3]), self.P
reprocessStack(row[4:])) |
176 elif row[0] == 'code-creation': | 185 elif row[0] == 'code-creation': |
177 self.ProcessCodeCreation(row[1], int(row[2], 16), int(row[3]), row[4]) | 186 self.ProcessCodeCreation(row[1], int(row[2], 16), int(row[3]), row[4]) |
178 elif row[0] == 'code-move': | 187 elif row[0] == 'code-move': |
179 self.ProcessCodeMove(int(row[1], 16), int(row[2], 16)) | 188 self.ProcessCodeMove(int(row[1], 16), int(row[2], 16)) |
180 elif row[0] == 'code-delete': | 189 elif row[0] == 'code-delete': |
181 self.ProcessCodeDelete(int(row[1], 16)) | 190 self.ProcessCodeDelete(int(row[1], 16)) |
182 elif row[0] == 'shared-library': | 191 elif row[0] == 'shared-library': |
183 self.AddSharedLibraryEntry(row[1], int(row[2], 16), int(row[3], 16)) | 192 self.AddSharedLibraryEntry(row[1], int(row[2], 16), int(row[3], 16)) |
184 self.ParseVMSymbols(row[1], int(row[2], 16), int(row[3], 16)) | 193 self.ParseVMSymbols(row[1], int(row[2], 16), int(row[3], 16)) |
185 elif row[0] == 'begin-code-region': | 194 elif row[0] == 'begin-code-region': |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
254 if entry != None: | 263 if entry != None: |
255 return entry.value | 264 return entry.value |
256 else: | 265 else: |
257 return entry | 266 return entry |
258 max = self.js_entries.FindMax() | 267 max = self.js_entries.FindMax() |
259 min = self.js_entries.FindMin() | 268 min = self.js_entries.FindMin() |
260 if max != None and pc < (max.key + max.value.size) and pc > min.key: | 269 if max != None and pc < (max.key + max.value.size) and pc > min.key: |
261 return self.js_entries.FindGreatestsLessThan(pc).value | 270 return self.js_entries.FindGreatestsLessThan(pc).value |
262 return None | 271 return None |
263 | 272 |
| 273 def PreprocessStack(self, stack): |
| 274 # remove all non-addresses (e.g. 'overflow') and convert to int |
| 275 result = [] |
| 276 for frame in stack: |
| 277 if frame.startswith('0x'): |
| 278 result.append(int(frame, 16)) |
| 279 return result |
| 280 |
264 def ProcessStack(self, stack): | 281 def ProcessStack(self, stack): |
265 result = [] | 282 result = [] |
266 for frame in stack: | 283 for frame in stack: |
267 if frame.startswith('0x'): | 284 entry = self.FindEntry(frame) |
268 entry = self.FindEntry(int(frame, 16)) | 285 if entry != None: |
269 if entry != None: | 286 result.append(entry.ToString()) |
270 result.append(entry.ToString()) | |
271 return result | 287 return result |
272 | 288 |
273 def ProcessTick(self, pc, sp, state, stack): | 289 def ProcessTick(self, pc, sp, state, stack): |
274 if not self.IncludeTick(pc, sp, state): | 290 if not self.IncludeTick(pc, sp, state): |
275 self.excluded_number_of_ticks += 1; | 291 self.excluded_number_of_ticks += 1; |
276 return | 292 return |
277 self.total_number_of_ticks += 1 | 293 self.total_number_of_ticks += 1 |
278 entry = self.FindEntry(pc) | 294 entry = self.FindEntry(pc) |
279 if entry == None: | 295 if entry == None: |
280 self.unaccounted_number_of_ticks += 1 | 296 self.unaccounted_number_of_ticks += 1 |
281 return | 297 return |
282 if entry.IsSharedLibraryEntry(): | 298 if entry.IsSharedLibraryEntry(): |
283 self.number_of_library_ticks += 1 | 299 self.number_of_library_ticks += 1 |
284 entry.Tick(pc, self.ProcessStack(stack)) | 300 if entry.IsICEntry() and not self.separate_ic: |
| 301 if len(stack) > 0: |
| 302 caller_pc = stack.pop(0) |
| 303 self.total_number_of_ticks -= 1 |
| 304 self.ProcessTick(caller_pc, sp, state, stack) |
| 305 else: |
| 306 self.unaccounted_number_of_ticks += 1 |
| 307 else: |
| 308 entry.Tick(pc, self.ProcessStack(stack)) |
285 | 309 |
286 def PrintResults(self): | 310 def PrintResults(self): |
287 print('Statistical profiling result from %s, (%d ticks, %d unaccounted, %d e
xcluded).' % | 311 print('Statistical profiling result from %s, (%d ticks, %d unaccounted, %d e
xcluded).' % |
288 (self.log_file, | 312 (self.log_file, |
289 self.total_number_of_ticks, | 313 self.total_number_of_ticks, |
290 self.unaccounted_number_of_ticks, | 314 self.unaccounted_number_of_ticks, |
291 self.excluded_number_of_ticks)) | 315 self.excluded_number_of_ticks)) |
292 if self.total_number_of_ticks > 0: | 316 if self.total_number_of_ticks > 0: |
293 js_entries = self.js_entries.ExportValueList() | 317 js_entries = self.js_entries.ExportValueList() |
294 js_entries.extend(self.deleted_code) | 318 js_entries.extend(self.deleted_code) |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
365 total_percentage = count * 100.0 / total_stacks | 389 total_percentage = count * 100.0 / total_stacks |
366 print(' %(total)5.1f%% %(call_path)s' % { | 390 print(' %(total)5.1f%% %(call_path)s' % { |
367 'total' : total_percentage, | 391 'total' : total_percentage, |
368 'call_path' : stack[0] + ' <- ' + stack[1] | 392 'call_path' : stack[0] + ' <- ' + stack[1] |
369 }) | 393 }) |
370 | 394 |
371 | 395 |
372 class CmdLineProcessor(object): | 396 class CmdLineProcessor(object): |
373 | 397 |
374 def __init__(self): | 398 def __init__(self): |
| 399 self.options = ["js", "gc", "compiler", "other", "ignore-unknown", "separate
-ic"] |
375 # default values | 400 # default values |
376 self.state = None | 401 self.state = None |
377 self.ignore_unknown = False | 402 self.ignore_unknown = False |
378 self.log_file = None | 403 self.log_file = None |
| 404 self.separate_ic = False |
379 | 405 |
380 def ProcessArguments(self): | 406 def ProcessArguments(self): |
381 try: | 407 try: |
382 opts, args = getopt.getopt(sys.argv[1:], "jgco", ["js", "gc", "compiler",
"other", "ignore-unknown"]) | 408 opts, args = getopt.getopt(sys.argv[1:], "jgco", self.options) |
383 except getopt.GetoptError: | 409 except getopt.GetoptError: |
384 self.PrintUsageAndExit() | 410 self.PrintUsageAndExit() |
385 for key, value in opts: | 411 for key, value in opts: |
386 if key in ("-j", "--js"): | 412 if key in ("-j", "--js"): |
387 self.state = 0 | 413 self.state = 0 |
388 if key in ("-g", "--gc"): | 414 if key in ("-g", "--gc"): |
389 self.state = 1 | 415 self.state = 1 |
390 if key in ("-c", "--compiler"): | 416 if key in ("-c", "--compiler"): |
391 self.state = 2 | 417 self.state = 2 |
392 if key in ("-o", "--other"): | 418 if key in ("-o", "--other"): |
393 self.state = 3 | 419 self.state = 3 |
394 if key in ("--ignore-unknown"): | 420 if key in ("--ignore-unknown"): |
395 self.ignore_unknown = True | 421 self.ignore_unknown = True |
| 422 if key in ("--separate-ic"): |
| 423 self.separate_ic = True |
396 self.ProcessRequiredArgs(args) | 424 self.ProcessRequiredArgs(args) |
397 | 425 |
398 def ProcessRequiredArgs(self, args): | 426 def ProcessRequiredArgs(self, args): |
399 return | 427 return |
400 | 428 |
401 def GetRequiredArgsNames(self): | 429 def GetRequiredArgsNames(self): |
402 return | 430 return |
403 | 431 |
404 def PrintUsageAndExit(self): | 432 def PrintUsageAndExit(self): |
405 print('Usage: %(script_name)s --{js,gc,compiler,other} %(req_opts)s' % { | 433 print('Usage: %(script_name)s --{%(opts)s} %(req_opts)s' % { |
406 'script_name': os.path.basename(sys.argv[0]), | 434 'script_name': os.path.basename(sys.argv[0]), |
| 435 'opts': string.join(self.options, ','), |
407 'req_opts': self.GetRequiredArgsNames() | 436 'req_opts': self.GetRequiredArgsNames() |
408 }) | 437 }) |
409 sys.exit(2) | 438 sys.exit(2) |
410 | 439 |
411 def RunLogfileProcessing(self, tick_processor): | 440 def RunLogfileProcessing(self, tick_processor): |
412 tick_processor.ProcessLogfile(self.log_file, self.state, self.ignore_unknown
) | 441 tick_processor.ProcessLogfile(self.log_file, self.state, self.ignore_unknown
, self.separate_ic) |
413 | 442 |
414 | 443 |
415 if __name__ == '__main__': | 444 if __name__ == '__main__': |
416 sys.exit('You probably want to run windows-tick-processor.py or linux-tick-pro
cessor.py.') | 445 sys.exit('You probably want to run windows-tick-processor.py or linux-tick-pro
cessor.py.') |
OLD | NEW |