| OLD | NEW |
| (Empty) |
| 1 # Copyright 2008 the V8 project authors. All rights reserved. | |
| 2 # Redistribution and use in source and binary forms, with or without | |
| 3 # modification, are permitted provided that the following conditions are | |
| 4 # met: | |
| 5 # | |
| 6 # * Redistributions of source code must retain the above copyright | |
| 7 # notice, this list of conditions and the following disclaimer. | |
| 8 # * Redistributions in binary form must reproduce the above | |
| 9 # copyright notice, this list of conditions and the following | |
| 10 # disclaimer in the documentation and/or other materials provided | |
| 11 # with the distribution. | |
| 12 # * Neither the name of Google Inc. nor the names of its | |
| 13 # contributors may be used to endorse or promote products derived | |
| 14 # from this software without specific prior written permission. | |
| 15 # | |
| 16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 20 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 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. | |
| 27 | |
| 28 import csv, splaytree, sys, re | |
| 29 from operator import itemgetter | |
| 30 import getopt, os, string | |
| 31 | |
| 32 class CodeEntry(object): | |
| 33 | |
| 34 def __init__(self, start_addr, name): | |
| 35 self.start_addr = start_addr | |
| 36 self.tick_count = 0 | |
| 37 self.name = name | |
| 38 self.stacks = {} | |
| 39 | |
| 40 def Tick(self, pc, stack): | |
| 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 | |
| 46 | |
| 47 def RegionTicks(self): | |
| 48 return None | |
| 49 | |
| 50 def SetStartAddress(self, start_addr): | |
| 51 self.start_addr = start_addr | |
| 52 | |
| 53 def ToString(self): | |
| 54 return self.name | |
| 55 | |
| 56 def IsSharedLibraryEntry(self): | |
| 57 return False | |
| 58 | |
| 59 def IsICEntry(self): | |
| 60 return False | |
| 61 | |
| 62 def IsJSFunction(self): | |
| 63 return False | |
| 64 | |
| 65 class SharedLibraryEntry(CodeEntry): | |
| 66 | |
| 67 def __init__(self, start_addr, name): | |
| 68 CodeEntry.__init__(self, start_addr, name) | |
| 69 | |
| 70 def IsSharedLibraryEntry(self): | |
| 71 return True | |
| 72 | |
| 73 | |
| 74 class JSCodeEntry(CodeEntry): | |
| 75 | |
| 76 def __init__(self, start_addr, name, type, size, assembler): | |
| 77 CodeEntry.__init__(self, start_addr, name) | |
| 78 self.type = type | |
| 79 self.size = size | |
| 80 self.assembler = assembler | |
| 81 self.region_ticks = None | |
| 82 self.builtin_ic_re = re.compile('^(Keyed)?(Call|Load|Store)IC_') | |
| 83 | |
| 84 def Tick(self, pc, stack): | |
| 85 super(JSCodeEntry, self).Tick(pc, stack) | |
| 86 if not pc is None: | |
| 87 offset = pc - self.start_addr | |
| 88 seen = [] | |
| 89 narrowest = None | |
| 90 narrowest_width = None | |
| 91 for region in self.Regions(): | |
| 92 if region.Contains(offset): | |
| 93 if (not region.name in seen): | |
| 94 seen.append(region.name) | |
| 95 if narrowest is None or region.Width() < narrowest.Width(): | |
| 96 narrowest = region | |
| 97 if len(seen) == 0: | |
| 98 return | |
| 99 if self.region_ticks is None: | |
| 100 self.region_ticks = {} | |
| 101 for name in seen: | |
| 102 if not name in self.region_ticks: | |
| 103 self.region_ticks[name] = [0, 0] | |
| 104 self.region_ticks[name][0] += 1 | |
| 105 if name == narrowest.name: | |
| 106 self.region_ticks[name][1] += 1 | |
| 107 | |
| 108 def RegionTicks(self): | |
| 109 return self.region_ticks | |
| 110 | |
| 111 def Regions(self): | |
| 112 if self.assembler: | |
| 113 return self.assembler.regions | |
| 114 else: | |
| 115 return [] | |
| 116 | |
| 117 def ToString(self): | |
| 118 name = self.name | |
| 119 if name == '': | |
| 120 name = '<anonymous>' | |
| 121 elif name.startswith(' '): | |
| 122 name = '<anonymous>' + name | |
| 123 return self.type + ': ' + name | |
| 124 | |
| 125 def IsICEntry(self): | |
| 126 return self.type in ('CallIC', 'LoadIC', 'StoreIC') or \ | |
| 127 (self.type == 'Builtin' and self.builtin_ic_re.match(self.name)) | |
| 128 | |
| 129 def IsJSFunction(self): | |
| 130 return self.type in ('Function', 'LazyCompile', 'Script') | |
| 131 | |
| 132 class CodeRegion(object): | |
| 133 | |
| 134 def __init__(self, start_offset, name): | |
| 135 self.start_offset = start_offset | |
| 136 self.name = name | |
| 137 self.end_offset = None | |
| 138 | |
| 139 def Contains(self, pc): | |
| 140 return (self.start_offset <= pc) and (pc <= self.end_offset) | |
| 141 | |
| 142 def Width(self): | |
| 143 return self.end_offset - self.start_offset | |
| 144 | |
| 145 | |
| 146 class Assembler(object): | |
| 147 | |
| 148 def __init__(self): | |
| 149 # Mapping from region ids to open regions | |
| 150 self.pending_regions = {} | |
| 151 self.regions = [] | |
| 152 | |
| 153 | |
| 154 class FunctionEnumerator(object): | |
| 155 | |
| 156 def __init__(self): | |
| 157 self.known_funcs = {} | |
| 158 self.next_func_id = 0 | |
| 159 | |
| 160 def GetFunctionId(self, name): | |
| 161 if not self.known_funcs.has_key(name): | |
| 162 self.known_funcs[name] = self.next_func_id | |
| 163 self.next_func_id += 1 | |
| 164 return self.known_funcs[name] | |
| 165 | |
| 166 def GetKnownFunctions(self): | |
| 167 known_funcs_items = self.known_funcs.items(); | |
| 168 known_funcs_items.sort(key = itemgetter(1)) | |
| 169 result = [] | |
| 170 for func, id_not_used in known_funcs_items: | |
| 171 result.append(func) | |
| 172 return result | |
| 173 | |
| 174 | |
| 175 VMStates = { 'JS': 0, 'GC': 1, 'COMPILER': 2, 'OTHER': 3, 'EXTERNAL' : 4 } | |
| 176 | |
| 177 | |
| 178 class TickProcessor(object): | |
| 179 | |
| 180 def __init__(self): | |
| 181 self.log_file = '' | |
| 182 self.deleted_code = [] | |
| 183 self.vm_extent = {} | |
| 184 # Map from assembler ids to the pending assembler objects | |
| 185 self.pending_assemblers = {} | |
| 186 # Map from code addresses the have been allocated but not yet officially | |
| 187 # created to their assemblers. | |
| 188 self.assemblers = {} | |
| 189 self.js_entries = splaytree.SplayTree() | |
| 190 self.cpp_entries = splaytree.SplayTree() | |
| 191 self.total_number_of_ticks = 0 | |
| 192 self.number_of_library_ticks = 0 | |
| 193 self.unaccounted_number_of_ticks = 0 | |
| 194 self.excluded_number_of_ticks = 0 | |
| 195 self.number_of_gc_ticks = 0 | |
| 196 # Flag indicating whether to ignore unaccounted ticks in the report | |
| 197 self.ignore_unknown = False | |
| 198 self.func_enum = FunctionEnumerator() | |
| 199 self.packed_stacks = [] | |
| 200 | |
| 201 def ProcessLogfile(self, filename, included_state = None, ignore_unknown = Fal
se, separate_ic = False, call_graph_json = False): | |
| 202 self.log_file = filename | |
| 203 self.included_state = included_state | |
| 204 self.ignore_unknown = ignore_unknown | |
| 205 self.separate_ic = separate_ic | |
| 206 self.call_graph_json = call_graph_json | |
| 207 | |
| 208 try: | |
| 209 logfile = open(filename, 'rb') | |
| 210 except IOError: | |
| 211 sys.exit("Could not open logfile: " + filename) | |
| 212 try: | |
| 213 try: | |
| 214 logreader = csv.reader(logfile) | |
| 215 row_num = 1 | |
| 216 for row in logreader: | |
| 217 row_num += 1 | |
| 218 if row[0] == 'tick': | |
| 219 self.ProcessTick(int(row[1], 16), int(row[2], 16), int(row[3], 16),
int(row[4]), self.PreprocessStack(row[5:])) | |
| 220 elif row[0] == 'code-creation': | |
| 221 self.ProcessCodeCreation(row[1], int(row[2], 16), int(row[3]), row[4
]) | |
| 222 elif row[0] == 'code-move': | |
| 223 self.ProcessCodeMove(int(row[1], 16), int(row[2], 16)) | |
| 224 elif row[0] == 'code-delete': | |
| 225 self.ProcessCodeDelete(int(row[1], 16)) | |
| 226 elif row[0] == 'function-creation': | |
| 227 self.ProcessFunctionCreation(int(row[1], 16), int(row[2], 16)) | |
| 228 elif row[0] == 'function-move': | |
| 229 self.ProcessFunctionMove(int(row[1], 16), int(row[2], 16)) | |
| 230 elif row[0] == 'function-delete': | |
| 231 self.ProcessFunctionDelete(int(row[1], 16)) | |
| 232 elif row[0] == 'shared-library': | |
| 233 self.AddSharedLibraryEntry(row[1], int(row[2], 16), int(row[3], 16)) | |
| 234 self.ParseVMSymbols(row[1], int(row[2], 16), int(row[3], 16)) | |
| 235 elif row[0] == 'begin-code-region': | |
| 236 self.ProcessBeginCodeRegion(int(row[1], 16), int(row[2], 16), int(ro
w[3], 16), row[4]) | |
| 237 elif row[0] == 'end-code-region': | |
| 238 self.ProcessEndCodeRegion(int(row[1], 16), int(row[2], 16), int(row[
3], 16)) | |
| 239 elif row[0] == 'code-allocate': | |
| 240 self.ProcessCodeAllocate(int(row[1], 16), int(row[2], 16)) | |
| 241 except csv.Error: | |
| 242 print("parse error in line " + str(row_num)) | |
| 243 raise | |
| 244 finally: | |
| 245 logfile.close() | |
| 246 | |
| 247 def AddSharedLibraryEntry(self, filename, start, end): | |
| 248 # Mark the pages used by this library. | |
| 249 i = start | |
| 250 while i < end: | |
| 251 page = i >> 12 | |
| 252 self.vm_extent[page] = 1 | |
| 253 i += 4096 | |
| 254 # Add the library to the entries so that ticks for which we do not | |
| 255 # have symbol information is reported as belonging to the library. | |
| 256 self.cpp_entries.Insert(start, SharedLibraryEntry(start, filename)) | |
| 257 | |
| 258 def ParseVMSymbols(self, filename, start, end): | |
| 259 return | |
| 260 | |
| 261 def ProcessCodeAllocate(self, addr, assem): | |
| 262 if assem in self.pending_assemblers: | |
| 263 assembler = self.pending_assemblers.pop(assem) | |
| 264 self.assemblers[addr] = assembler | |
| 265 | |
| 266 def ProcessCodeCreation(self, type, addr, size, name): | |
| 267 if addr in self.assemblers: | |
| 268 assembler = self.assemblers.pop(addr) | |
| 269 else: | |
| 270 assembler = None | |
| 271 self.js_entries.Insert(addr, JSCodeEntry(addr, name, type, size, assembler)) | |
| 272 | |
| 273 def ProcessCodeMove(self, from_addr, to_addr): | |
| 274 try: | |
| 275 removed_node = self.js_entries.Remove(from_addr) | |
| 276 removed_node.value.SetStartAddress(to_addr); | |
| 277 self.js_entries.Insert(to_addr, removed_node.value) | |
| 278 except splaytree.KeyNotFoundError: | |
| 279 print('Code move event for unknown code: 0x%x' % from_addr) | |
| 280 | |
| 281 def ProcessCodeDelete(self, from_addr): | |
| 282 try: | |
| 283 removed_node = self.js_entries.Remove(from_addr) | |
| 284 self.deleted_code.append(removed_node.value) | |
| 285 except splaytree.KeyNotFoundError: | |
| 286 print('Code delete event for unknown code: 0x%x' % from_addr) | |
| 287 | |
| 288 def ProcessFunctionCreation(self, func_addr, code_addr): | |
| 289 js_entry_node = self.js_entries.Find(code_addr) | |
| 290 if js_entry_node: | |
| 291 js_entry = js_entry_node.value | |
| 292 self.js_entries.Insert(func_addr, JSCodeEntry(func_addr, js_entry.name, js
_entry.type, 1, None)) | |
| 293 | |
| 294 def ProcessFunctionMove(self, from_addr, to_addr): | |
| 295 try: | |
| 296 removed_node = self.js_entries.Remove(from_addr) | |
| 297 removed_node.value.SetStartAddress(to_addr); | |
| 298 self.js_entries.Insert(to_addr, removed_node.value) | |
| 299 except splaytree.KeyNotFoundError: | |
| 300 return | |
| 301 | |
| 302 def ProcessFunctionDelete(self, from_addr): | |
| 303 try: | |
| 304 removed_node = self.js_entries.Remove(from_addr) | |
| 305 self.deleted_code.append(removed_node.value) | |
| 306 except splaytree.KeyNotFoundError: | |
| 307 return | |
| 308 | |
| 309 def ProcessBeginCodeRegion(self, id, assm, start, name): | |
| 310 if not assm in self.pending_assemblers: | |
| 311 self.pending_assemblers[assm] = Assembler() | |
| 312 assembler = self.pending_assemblers[assm] | |
| 313 assembler.pending_regions[id] = CodeRegion(start, name) | |
| 314 | |
| 315 def ProcessEndCodeRegion(self, id, assm, end): | |
| 316 assm = self.pending_assemblers[assm] | |
| 317 region = assm.pending_regions.pop(id) | |
| 318 region.end_offset = end | |
| 319 assm.regions.append(region) | |
| 320 | |
| 321 def IncludeTick(self, pc, sp, state): | |
| 322 return (self.included_state is None) or (self.included_state == state) | |
| 323 | |
| 324 def FindEntry(self, pc): | |
| 325 page = pc >> 12 | |
| 326 if page in self.vm_extent: | |
| 327 entry = self.cpp_entries.FindGreatestsLessThan(pc) | |
| 328 if entry != None: | |
| 329 return entry.value | |
| 330 else: | |
| 331 return entry | |
| 332 max = self.js_entries.FindMax() | |
| 333 min = self.js_entries.FindMin() | |
| 334 if max != None and pc < (max.key + max.value.size) and pc > min.key: | |
| 335 return self.js_entries.FindGreatestsLessThan(pc).value | |
| 336 return None | |
| 337 | |
| 338 def PreprocessStack(self, stack): | |
| 339 # remove all non-addresses (e.g. 'overflow') and convert to int | |
| 340 result = [] | |
| 341 for frame in stack: | |
| 342 if frame.startswith('0x'): | |
| 343 result.append(int(frame, 16)) | |
| 344 return result | |
| 345 | |
| 346 def ProcessStack(self, stack): | |
| 347 result = [] | |
| 348 for frame in stack: | |
| 349 entry = self.FindEntry(frame) | |
| 350 if entry != None: | |
| 351 result.append(entry.ToString()) | |
| 352 return result | |
| 353 | |
| 354 def ProcessTick(self, pc, sp, func, state, stack): | |
| 355 if state == VMStates['GC']: | |
| 356 self.number_of_gc_ticks += 1 | |
| 357 if not self.IncludeTick(pc, sp, state): | |
| 358 self.excluded_number_of_ticks += 1; | |
| 359 return | |
| 360 self.total_number_of_ticks += 1 | |
| 361 entry = self.FindEntry(pc) | |
| 362 if entry == None: | |
| 363 self.unaccounted_number_of_ticks += 1 | |
| 364 return | |
| 365 if entry.IsSharedLibraryEntry(): | |
| 366 self.number_of_library_ticks += 1 | |
| 367 if entry.IsICEntry() and not self.separate_ic: | |
| 368 if len(stack) > 0: | |
| 369 caller_pc = stack.pop(0) | |
| 370 self.total_number_of_ticks -= 1 | |
| 371 self.ProcessTick(caller_pc, sp, func, state, stack) | |
| 372 else: | |
| 373 self.unaccounted_number_of_ticks += 1 | |
| 374 else: | |
| 375 processed_stack = self.ProcessStack(stack) | |
| 376 if not entry.IsSharedLibraryEntry() and not entry.IsJSFunction(): | |
| 377 func_entry_node = self.js_entries.Find(func) | |
| 378 if func_entry_node and func_entry_node.value.IsJSFunction(): | |
| 379 processed_stack.insert(0, func_entry_node.value.ToString()) | |
| 380 entry.Tick(pc, processed_stack) | |
| 381 if self.call_graph_json: | |
| 382 self.AddToPackedStacks(pc, stack) | |
| 383 | |
| 384 def AddToPackedStacks(self, pc, stack): | |
| 385 full_stack = stack | |
| 386 full_stack.insert(0, pc) | |
| 387 func_names = self.ProcessStack(full_stack) | |
| 388 func_ids = [] | |
| 389 for func in func_names: | |
| 390 func_ids.append(self.func_enum.GetFunctionId(func)) | |
| 391 self.packed_stacks.append(func_ids) | |
| 392 | |
| 393 def PrintResults(self): | |
| 394 if not self.call_graph_json: | |
| 395 self.PrintStatistics() | |
| 396 else: | |
| 397 self.PrintCallGraphJSON() | |
| 398 | |
| 399 def PrintStatistics(self): | |
| 400 print('Statistical profiling result from %s, (%d ticks, %d unaccounted, %d e
xcluded).' % | |
| 401 (self.log_file, | |
| 402 self.total_number_of_ticks, | |
| 403 self.unaccounted_number_of_ticks, | |
| 404 self.excluded_number_of_ticks)) | |
| 405 if self.total_number_of_ticks > 0: | |
| 406 js_entries = self.js_entries.ExportValueList() | |
| 407 js_entries.extend(self.deleted_code) | |
| 408 cpp_entries = self.cpp_entries.ExportValueList() | |
| 409 # Print the unknown ticks percentage if they are not ignored. | |
| 410 if not self.ignore_unknown and self.unaccounted_number_of_ticks > 0: | |
| 411 self.PrintHeader('Unknown') | |
| 412 self.PrintCounter(self.unaccounted_number_of_ticks) | |
| 413 # Print the library ticks. | |
| 414 self.PrintHeader('Shared libraries') | |
| 415 self.PrintEntries(cpp_entries, lambda e:e.IsSharedLibraryEntry()) | |
| 416 # Print the JavaScript ticks. | |
| 417 self.PrintHeader('JavaScript') | |
| 418 self.PrintEntries(js_entries, lambda e:not e.IsSharedLibraryEntry()) | |
| 419 # Print the C++ ticks. | |
| 420 self.PrintHeader('C++') | |
| 421 self.PrintEntries(cpp_entries, lambda e:not e.IsSharedLibraryEntry()) | |
| 422 # Print the GC ticks. | |
| 423 self.PrintHeader('GC') | |
| 424 self.PrintCounter(self.number_of_gc_ticks) | |
| 425 # Print call profile. | |
| 426 print('\n [Call profile]:') | |
| 427 print(' total call path') | |
| 428 js_entries.extend(cpp_entries) | |
| 429 self.PrintCallProfile(js_entries) | |
| 430 | |
| 431 def PrintHeader(self, header_title): | |
| 432 print('\n [%s]:' % header_title) | |
| 433 print(' ticks total nonlib name') | |
| 434 | |
| 435 def PrintCounter(self, ticks_count): | |
| 436 percentage = ticks_count * 100.0 / self.total_number_of_ticks | |
| 437 print(' %(ticks)5d %(total)5.1f%%' % { | |
| 438 'ticks' : ticks_count, | |
| 439 'total' : percentage, | |
| 440 }) | |
| 441 | |
| 442 def PrintEntries(self, entries, condition): | |
| 443 # If ignoring unaccounted ticks don't include these in percentage | |
| 444 # calculations | |
| 445 number_of_accounted_ticks = self.total_number_of_ticks | |
| 446 if self.ignore_unknown: | |
| 447 number_of_accounted_ticks -= self.unaccounted_number_of_ticks | |
| 448 | |
| 449 number_of_non_library_ticks = number_of_accounted_ticks - self.number_of_lib
rary_ticks | |
| 450 entries.sort(key=lambda e: (e.tick_count, e.ToString()), reverse=True) | |
| 451 for entry in entries: | |
| 452 if entry.tick_count > 0 and condition(entry): | |
| 453 total_percentage = entry.tick_count * 100.0 / number_of_accounted_ticks | |
| 454 if entry.IsSharedLibraryEntry(): | |
| 455 non_library_percentage = 0 | |
| 456 else: | |
| 457 non_library_percentage = entry.tick_count * 100.0 / number_of_non_libr
ary_ticks | |
| 458 print(' %(ticks)5d %(total)5.1f%% %(nonlib)6.1f%% %(name)s' % { | |
| 459 'ticks' : entry.tick_count, | |
| 460 'total' : total_percentage, | |
| 461 'nonlib' : non_library_percentage, | |
| 462 'name' : entry.ToString() | |
| 463 }) | |
| 464 region_ticks = entry.RegionTicks() | |
| 465 if not region_ticks is None: | |
| 466 items = region_ticks.items() | |
| 467 items.sort(key=lambda e: e[1][1], reverse=True) | |
| 468 for (name, ticks) in items: | |
| 469 print(' flat cum') | |
| 470 print(' %(flat)5.1f%% %(accum)5.1f%% %(name)s' %
{ | |
| 471 'flat' : ticks[1] * 100.0 / entry.tick_count, | |
| 472 'accum' : ticks[0] * 100.0 / entry.tick_count, | |
| 473 'name': name | |
| 474 }) | |
| 475 | |
| 476 def PrintCallProfile(self, entries): | |
| 477 all_stacks = {} | |
| 478 total_stacks = 0 | |
| 479 for entry in entries: | |
| 480 all_stacks.update(entry.stacks) | |
| 481 for count in entry.stacks.itervalues(): | |
| 482 total_stacks += count | |
| 483 all_stacks_items = all_stacks.items(); | |
| 484 all_stacks_items.sort(key = itemgetter(1), reverse=True) | |
| 485 missing_percentage = (self.total_number_of_ticks - total_stacks) * 100.0 / s
elf.total_number_of_ticks | |
| 486 print(' %(ticks)5d %(total)5.1f%% <no call path information>' % { | |
| 487 'ticks' : self.total_number_of_ticks - total_stacks, | |
| 488 'total' : missing_percentage | |
| 489 }) | |
| 490 for stack, count in all_stacks_items: | |
| 491 total_percentage = count * 100.0 / self.total_number_of_ticks | |
| 492 print(' %(ticks)5d %(total)5.1f%% %(call_path)s' % { | |
| 493 'ticks' : count, | |
| 494 'total' : total_percentage, | |
| 495 'call_path' : stack[0] + ' <- ' + stack[1] | |
| 496 }) | |
| 497 | |
| 498 def PrintCallGraphJSON(self): | |
| 499 print('\nvar __profile_funcs = ["' + | |
| 500 '",\n"'.join(self.func_enum.GetKnownFunctions()) + | |
| 501 '"];') | |
| 502 print('var __profile_ticks = [') | |
| 503 str_packed_stacks = [] | |
| 504 for stack in self.packed_stacks: | |
| 505 str_packed_stacks.append('[' + ','.join(map(str, stack)) + ']') | |
| 506 print(',\n'.join(str_packed_stacks)) | |
| 507 print('];') | |
| 508 | |
| 509 class CmdLineProcessor(object): | |
| 510 | |
| 511 def __init__(self): | |
| 512 self.options = ["js", | |
| 513 "gc", | |
| 514 "compiler", | |
| 515 "other", | |
| 516 "external", | |
| 517 "ignore-unknown", | |
| 518 "separate-ic", | |
| 519 "call-graph-json"] | |
| 520 # default values | |
| 521 self.state = None | |
| 522 self.ignore_unknown = False | |
| 523 self.log_file = None | |
| 524 self.separate_ic = False | |
| 525 self.call_graph_json = False | |
| 526 | |
| 527 def ProcessArguments(self): | |
| 528 try: | |
| 529 opts, args = getopt.getopt(sys.argv[1:], "jgcoe", self.options) | |
| 530 except getopt.GetoptError: | |
| 531 self.PrintUsageAndExit() | |
| 532 for key, value in opts: | |
| 533 if key in ("-j", "--js"): | |
| 534 self.state = VMStates['JS'] | |
| 535 if key in ("-g", "--gc"): | |
| 536 self.state = VMStates['GC'] | |
| 537 if key in ("-c", "--compiler"): | |
| 538 self.state = VMStates['COMPILER'] | |
| 539 if key in ("-o", "--other"): | |
| 540 self.state = VMStates['OTHER'] | |
| 541 if key in ("-e", "--external"): | |
| 542 self.state = VMStates['EXTERNAL'] | |
| 543 if key in ("--ignore-unknown"): | |
| 544 self.ignore_unknown = True | |
| 545 if key in ("--separate-ic"): | |
| 546 self.separate_ic = True | |
| 547 if key in ("--call-graph-json"): | |
| 548 self.call_graph_json = True | |
| 549 self.ProcessRequiredArgs(args) | |
| 550 | |
| 551 def ProcessRequiredArgs(self, args): | |
| 552 return | |
| 553 | |
| 554 def GetRequiredArgsNames(self): | |
| 555 return | |
| 556 | |
| 557 def PrintUsageAndExit(self): | |
| 558 print('Usage: %(script_name)s --{%(opts)s} %(req_opts)s' % { | |
| 559 'script_name': os.path.basename(sys.argv[0]), | |
| 560 'opts': string.join(self.options, ','), | |
| 561 'req_opts': self.GetRequiredArgsNames() | |
| 562 }) | |
| 563 sys.exit(2) | |
| 564 | |
| 565 def RunLogfileProcessing(self, tick_processor): | |
| 566 tick_processor.ProcessLogfile(self.log_file, self.state, self.ignore_unknown
, | |
| 567 self.separate_ic, self.call_graph_json) | |
| 568 | |
| 569 | |
| 570 if __name__ == '__main__': | |
| 571 sys.exit('You probably want to run windows-tick-processor.py or linux-tick-pro
cessor.py.') | |
| OLD | NEW |