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 |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
140 | 140 |
141 | 141 |
142 class Assembler(object): | 142 class Assembler(object): |
143 | 143 |
144 def __init__(self): | 144 def __init__(self): |
145 # Mapping from region ids to open regions | 145 # Mapping from region ids to open regions |
146 self.pending_regions = {} | 146 self.pending_regions = {} |
147 self.regions = [] | 147 self.regions = [] |
148 | 148 |
149 | 149 |
| 150 class FunctionEnumerator(object): |
| 151 |
| 152 def __init__(self): |
| 153 self.known_funcs = {} |
| 154 self.next_func_id = 0 |
| 155 |
| 156 def GetFunctionId(self, name): |
| 157 if not self.known_funcs.has_key(name): |
| 158 self.known_funcs[name] = self.next_func_id |
| 159 self.next_func_id += 1 |
| 160 return self.known_funcs[name] |
| 161 |
| 162 def GetKnownFunctions(self): |
| 163 known_funcs_items = self.known_funcs.items(); |
| 164 known_funcs_items.sort(key = itemgetter(1)) |
| 165 result = [] |
| 166 for func, id_not_used in known_funcs_items: |
| 167 result.append(func) |
| 168 return result |
| 169 |
| 170 |
150 VMStates = { 'JS': 0, 'GC': 1, 'COMPILER': 2, 'OTHER': 3, 'EXTERNAL' : 4 } | 171 VMStates = { 'JS': 0, 'GC': 1, 'COMPILER': 2, 'OTHER': 3, 'EXTERNAL' : 4 } |
151 | 172 |
152 | 173 |
153 class TickProcessor(object): | 174 class TickProcessor(object): |
154 | 175 |
155 def __init__(self): | 176 def __init__(self): |
156 self.log_file = '' | 177 self.log_file = '' |
157 self.deleted_code = [] | 178 self.deleted_code = [] |
158 self.vm_extent = {} | 179 self.vm_extent = {} |
159 # Map from assembler ids to the pending assembler objects | 180 # Map from assembler ids to the pending assembler objects |
160 self.pending_assemblers = {} | 181 self.pending_assemblers = {} |
161 # Map from code addresses the have been allocated but not yet officially | 182 # Map from code addresses the have been allocated but not yet officially |
162 # created to their assemblers. | 183 # created to their assemblers. |
163 self.assemblers = {} | 184 self.assemblers = {} |
164 self.js_entries = splaytree.SplayTree() | 185 self.js_entries = splaytree.SplayTree() |
165 self.cpp_entries = splaytree.SplayTree() | 186 self.cpp_entries = splaytree.SplayTree() |
166 self.total_number_of_ticks = 0 | 187 self.total_number_of_ticks = 0 |
167 self.number_of_library_ticks = 0 | 188 self.number_of_library_ticks = 0 |
168 self.unaccounted_number_of_ticks = 0 | 189 self.unaccounted_number_of_ticks = 0 |
169 self.excluded_number_of_ticks = 0 | 190 self.excluded_number_of_ticks = 0 |
170 self.number_of_gc_ticks = 0 | 191 self.number_of_gc_ticks = 0 |
171 # Flag indicating whether to ignore unaccounted ticks in the report | 192 # Flag indicating whether to ignore unaccounted ticks in the report |
172 self.ignore_unknown = False | 193 self.ignore_unknown = False |
| 194 self.func_enum = FunctionEnumerator() |
| 195 self.packed_stacks = [] |
173 | 196 |
174 def ProcessLogfile(self, filename, included_state = None, ignore_unknown = Fal
se, separate_ic = False): | 197 def ProcessLogfile(self, filename, included_state = None, ignore_unknown = Fal
se, separate_ic = False, call_graph_json = False): |
175 self.log_file = filename | 198 self.log_file = filename |
176 self.included_state = included_state | 199 self.included_state = included_state |
177 self.ignore_unknown = ignore_unknown | 200 self.ignore_unknown = ignore_unknown |
178 self.separate_ic = separate_ic | 201 self.separate_ic = separate_ic |
| 202 self.call_graph_json = call_graph_json |
179 | 203 |
180 try: | 204 try: |
181 logfile = open(filename, 'rb') | 205 logfile = open(filename, 'rb') |
182 except IOError: | 206 except IOError: |
183 sys.exit("Could not open logfile: " + filename) | 207 sys.exit("Could not open logfile: " + filename) |
184 try: | 208 try: |
185 logreader = csv.reader(logfile) | 209 try: |
186 for row in logreader: | 210 logreader = csv.reader(logfile) |
187 if row[0] == 'tick': | 211 row_num = 1 |
188 self.ProcessTick(int(row[1], 16), int(row[2], 16), int(row[3]), self.P
reprocessStack(row[4:])) | 212 for row in logreader: |
189 elif row[0] == 'code-creation': | 213 row_num += 1 |
190 self.ProcessCodeCreation(row[1], int(row[2], 16), int(row[3]), row[4]) | 214 if row[0] == 'tick': |
191 elif row[0] == 'code-move': | 215 self.ProcessTick(int(row[1], 16), int(row[2], 16), int(row[3]), self
.PreprocessStack(row[4:])) |
192 self.ProcessCodeMove(int(row[1], 16), int(row[2], 16)) | 216 elif row[0] == 'code-creation': |
193 elif row[0] == 'code-delete': | 217 self.ProcessCodeCreation(row[1], int(row[2], 16), int(row[3]), row[4
]) |
194 self.ProcessCodeDelete(int(row[1], 16)) | 218 elif row[0] == 'code-move': |
195 elif row[0] == 'shared-library': | 219 self.ProcessCodeMove(int(row[1], 16), int(row[2], 16)) |
196 self.AddSharedLibraryEntry(row[1], int(row[2], 16), int(row[3], 16)) | 220 elif row[0] == 'code-delete': |
197 self.ParseVMSymbols(row[1], int(row[2], 16), int(row[3], 16)) | 221 self.ProcessCodeDelete(int(row[1], 16)) |
198 elif row[0] == 'begin-code-region': | 222 elif row[0] == 'shared-library': |
199 self.ProcessBeginCodeRegion(int(row[1], 16), int(row[2], 16), int(row[
3], 16), row[4]) | 223 self.AddSharedLibraryEntry(row[1], int(row[2], 16), int(row[3], 16)) |
200 elif row[0] == 'end-code-region': | 224 self.ParseVMSymbols(row[1], int(row[2], 16), int(row[3], 16)) |
201 self.ProcessEndCodeRegion(int(row[1], 16), int(row[2], 16), int(row[3]
, 16)) | 225 elif row[0] == 'begin-code-region': |
202 elif row[0] == 'code-allocate': | 226 self.ProcessBeginCodeRegion(int(row[1], 16), int(row[2], 16), int(ro
w[3], 16), row[4]) |
203 self.ProcessCodeAllocate(int(row[1], 16), int(row[2], 16)) | 227 elif row[0] == 'end-code-region': |
| 228 self.ProcessEndCodeRegion(int(row[1], 16), int(row[2], 16), int(row[
3], 16)) |
| 229 elif row[0] == 'code-allocate': |
| 230 self.ProcessCodeAllocate(int(row[1], 16), int(row[2], 16)) |
| 231 except csv.Error: |
| 232 print("parse error in line " + str(row_num)) |
| 233 raise |
204 finally: | 234 finally: |
205 logfile.close() | 235 logfile.close() |
206 | 236 |
207 def AddSharedLibraryEntry(self, filename, start, end): | 237 def AddSharedLibraryEntry(self, filename, start, end): |
208 # Mark the pages used by this library. | 238 # Mark the pages used by this library. |
209 i = start | 239 i = start |
210 while i < end: | 240 while i < end: |
211 page = i >> 12 | 241 page = i >> 12 |
212 self.vm_extent[page] = 1 | 242 self.vm_extent[page] = 1 |
213 i += 4096 | 243 i += 4096 |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
305 self.number_of_library_ticks += 1 | 335 self.number_of_library_ticks += 1 |
306 if entry.IsICEntry() and not self.separate_ic: | 336 if entry.IsICEntry() and not self.separate_ic: |
307 if len(stack) > 0: | 337 if len(stack) > 0: |
308 caller_pc = stack.pop(0) | 338 caller_pc = stack.pop(0) |
309 self.total_number_of_ticks -= 1 | 339 self.total_number_of_ticks -= 1 |
310 self.ProcessTick(caller_pc, sp, state, stack) | 340 self.ProcessTick(caller_pc, sp, state, stack) |
311 else: | 341 else: |
312 self.unaccounted_number_of_ticks += 1 | 342 self.unaccounted_number_of_ticks += 1 |
313 else: | 343 else: |
314 entry.Tick(pc, self.ProcessStack(stack)) | 344 entry.Tick(pc, self.ProcessStack(stack)) |
| 345 if self.call_graph_json: |
| 346 self.AddToPackedStacks(pc, stack) |
| 347 |
| 348 def AddToPackedStacks(self, pc, stack): |
| 349 full_stack = stack |
| 350 full_stack.insert(0, pc) |
| 351 func_names = self.ProcessStack(full_stack) |
| 352 func_ids = [] |
| 353 for func in func_names: |
| 354 func_ids.append(self.func_enum.GetFunctionId(func)) |
| 355 self.packed_stacks.append(func_ids) |
315 | 356 |
316 def PrintResults(self): | 357 def PrintResults(self): |
| 358 if not self.call_graph_json: |
| 359 self.PrintStatistics() |
| 360 else: |
| 361 self.PrintCallGraphJSON() |
| 362 |
| 363 def PrintStatistics(self): |
317 print('Statistical profiling result from %s, (%d ticks, %d unaccounted, %d e
xcluded).' % | 364 print('Statistical profiling result from %s, (%d ticks, %d unaccounted, %d e
xcluded).' % |
318 (self.log_file, | 365 (self.log_file, |
319 self.total_number_of_ticks, | 366 self.total_number_of_ticks, |
320 self.unaccounted_number_of_ticks, | 367 self.unaccounted_number_of_ticks, |
321 self.excluded_number_of_ticks)) | 368 self.excluded_number_of_ticks)) |
322 if self.total_number_of_ticks > 0: | 369 if self.total_number_of_ticks > 0: |
323 js_entries = self.js_entries.ExportValueList() | 370 js_entries = self.js_entries.ExportValueList() |
324 js_entries.extend(self.deleted_code) | 371 js_entries.extend(self.deleted_code) |
325 cpp_entries = self.cpp_entries.ExportValueList() | 372 cpp_entries = self.cpp_entries.ExportValueList() |
326 # Print the unknown ticks percentage if they are not ignored. | 373 # Print the unknown ticks percentage if they are not ignored. |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
405 'total' : missing_percentage | 452 'total' : missing_percentage |
406 }) | 453 }) |
407 for stack, count in all_stacks_items: | 454 for stack, count in all_stacks_items: |
408 total_percentage = count * 100.0 / self.total_number_of_ticks | 455 total_percentage = count * 100.0 / self.total_number_of_ticks |
409 print(' %(ticks)5d %(total)5.1f%% %(call_path)s' % { | 456 print(' %(ticks)5d %(total)5.1f%% %(call_path)s' % { |
410 'ticks' : count, | 457 'ticks' : count, |
411 'total' : total_percentage, | 458 'total' : total_percentage, |
412 'call_path' : stack[0] + ' <- ' + stack[1] | 459 'call_path' : stack[0] + ' <- ' + stack[1] |
413 }) | 460 }) |
414 | 461 |
| 462 def PrintCallGraphJSON(self): |
| 463 print('\nvar __profile_funcs = ["' + |
| 464 '",\n"'.join(self.func_enum.GetKnownFunctions()) + |
| 465 '"];') |
| 466 print('var __profile_ticks = [') |
| 467 str_packed_stacks = [] |
| 468 for stack in self.packed_stacks: |
| 469 str_packed_stacks.append('[' + ','.join(map(str, stack)) + ']') |
| 470 print(',\n'.join(str_packed_stacks)) |
| 471 print('];') |
415 | 472 |
416 class CmdLineProcessor(object): | 473 class CmdLineProcessor(object): |
417 | 474 |
418 def __init__(self): | 475 def __init__(self): |
419 self.options = ["js", | 476 self.options = ["js", |
420 "gc", | 477 "gc", |
421 "compiler", | 478 "compiler", |
422 "other", | 479 "other", |
423 "external", | 480 "external", |
424 "ignore-unknown", | 481 "ignore-unknown", |
425 "separate-ic"] | 482 "separate-ic", |
| 483 "call-graph-json"] |
426 # default values | 484 # default values |
427 self.state = None | 485 self.state = None |
428 self.ignore_unknown = False | 486 self.ignore_unknown = False |
429 self.log_file = None | 487 self.log_file = None |
430 self.separate_ic = False | 488 self.separate_ic = False |
| 489 self.call_graph_json = False |
431 | 490 |
432 def ProcessArguments(self): | 491 def ProcessArguments(self): |
433 try: | 492 try: |
434 opts, args = getopt.getopt(sys.argv[1:], "jgcoe", self.options) | 493 opts, args = getopt.getopt(sys.argv[1:], "jgcoe", self.options) |
435 except getopt.GetoptError: | 494 except getopt.GetoptError: |
436 self.PrintUsageAndExit() | 495 self.PrintUsageAndExit() |
437 for key, value in opts: | 496 for key, value in opts: |
438 if key in ("-j", "--js"): | 497 if key in ("-j", "--js"): |
439 self.state = VMStates['JS'] | 498 self.state = VMStates['JS'] |
440 if key in ("-g", "--gc"): | 499 if key in ("-g", "--gc"): |
441 self.state = VMStates['GC'] | 500 self.state = VMStates['GC'] |
442 if key in ("-c", "--compiler"): | 501 if key in ("-c", "--compiler"): |
443 self.state = VMStates['COMPILER'] | 502 self.state = VMStates['COMPILER'] |
444 if key in ("-o", "--other"): | 503 if key in ("-o", "--other"): |
445 self.state = VMStates['OTHER'] | 504 self.state = VMStates['OTHER'] |
446 if key in ("-e", "--external"): | 505 if key in ("-e", "--external"): |
447 self.state = VMStates['EXTERNAL'] | 506 self.state = VMStates['EXTERNAL'] |
448 if key in ("--ignore-unknown"): | 507 if key in ("--ignore-unknown"): |
449 self.ignore_unknown = True | 508 self.ignore_unknown = True |
450 if key in ("--separate-ic"): | 509 if key in ("--separate-ic"): |
451 self.separate_ic = True | 510 self.separate_ic = True |
| 511 if key in ("--call-graph-json"): |
| 512 self.call_graph_json = True |
452 self.ProcessRequiredArgs(args) | 513 self.ProcessRequiredArgs(args) |
453 | 514 |
454 def ProcessRequiredArgs(self, args): | 515 def ProcessRequiredArgs(self, args): |
455 return | 516 return |
456 | 517 |
457 def GetRequiredArgsNames(self): | 518 def GetRequiredArgsNames(self): |
458 return | 519 return |
459 | 520 |
460 def PrintUsageAndExit(self): | 521 def PrintUsageAndExit(self): |
461 print('Usage: %(script_name)s --{%(opts)s} %(req_opts)s' % { | 522 print('Usage: %(script_name)s --{%(opts)s} %(req_opts)s' % { |
462 'script_name': os.path.basename(sys.argv[0]), | 523 'script_name': os.path.basename(sys.argv[0]), |
463 'opts': string.join(self.options, ','), | 524 'opts': string.join(self.options, ','), |
464 'req_opts': self.GetRequiredArgsNames() | 525 'req_opts': self.GetRequiredArgsNames() |
465 }) | 526 }) |
466 sys.exit(2) | 527 sys.exit(2) |
467 | 528 |
468 def RunLogfileProcessing(self, tick_processor): | 529 def RunLogfileProcessing(self, tick_processor): |
469 tick_processor.ProcessLogfile(self.log_file, self.state, self.ignore_unknown
, self.separate_ic) | 530 tick_processor.ProcessLogfile(self.log_file, self.state, self.ignore_unknown
, |
| 531 self.separate_ic, self.call_graph_json) |
470 | 532 |
471 | 533 |
472 if __name__ == '__main__': | 534 if __name__ == '__main__': |
473 sys.exit('You probably want to run windows-tick-processor.py or linux-tick-pro
cessor.py.') | 535 sys.exit('You probably want to run windows-tick-processor.py or linux-tick-pro
cessor.py.') |
OLD | NEW |