| 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 17 matching lines...) Expand all Loading... |
| 28 import csv, splaytree, sys | 28 import csv, splaytree, sys |
| 29 | 29 |
| 30 | 30 |
| 31 class CodeEntry(object): | 31 class CodeEntry(object): |
| 32 | 32 |
| 33 def __init__(self, start_addr, name): | 33 def __init__(self, start_addr, name): |
| 34 self.start_addr = start_addr | 34 self.start_addr = start_addr |
| 35 self.tick_count = 0 | 35 self.tick_count = 0 |
| 36 self.name = name | 36 self.name = name |
| 37 | 37 |
| 38 def IncrementTickCount(self): | 38 def Tick(self, pc): |
| 39 self.tick_count += 1 | 39 self.tick_count += 1 |
| 40 | 40 |
| 41 def RegionTicks(self): |
| 42 return None |
| 43 |
| 41 def SetStartAddress(self, start_addr): | 44 def SetStartAddress(self, start_addr): |
| 42 self.start_addr = start_addr | 45 self.start_addr = start_addr |
| 43 | 46 |
| 44 def ToString(self): | 47 def ToString(self): |
| 45 return self.name | 48 return self.name |
| 46 | 49 |
| 47 def IsSharedLibraryEntry(self): | 50 def IsSharedLibraryEntry(self): |
| 48 return False | 51 return False |
| 49 | 52 |
| 50 | 53 |
| 51 class SharedLibraryEntry(CodeEntry): | 54 class SharedLibraryEntry(CodeEntry): |
| 52 | 55 |
| 53 def __init__(self, start_addr, name): | 56 def __init__(self, start_addr, name): |
| 54 CodeEntry.__init__(self, start_addr, name) | 57 CodeEntry.__init__(self, start_addr, name) |
| 55 | 58 |
| 56 def IsSharedLibraryEntry(self): | 59 def IsSharedLibraryEntry(self): |
| 57 return True | 60 return True |
| 58 | 61 |
| 59 | 62 |
| 60 class JSCodeEntry(CodeEntry): | 63 class JSCodeEntry(CodeEntry): |
| 61 | 64 |
| 62 def __init__(self, start_addr, name, type, size): | 65 def __init__(self, start_addr, name, type, size, assembler): |
| 63 CodeEntry.__init__(self, start_addr, name) | 66 CodeEntry.__init__(self, start_addr, name) |
| 64 self.type = type | 67 self.type = type |
| 65 self.size = size | 68 self.size = size |
| 69 self.assembler = assembler |
| 70 self.region_ticks = None |
| 71 |
| 72 def Tick(self, pc): |
| 73 super(JSCodeEntry, self).Tick(pc) |
| 74 if not pc is None: |
| 75 offset = pc - self.start_addr |
| 76 seen = [] |
| 77 narrowest = None |
| 78 narrowest_width = None |
| 79 for region in self.Regions(): |
| 80 if region.Contains(offset): |
| 81 if (not region.name in seen): |
| 82 seen.append(region.name) |
| 83 if narrowest is None or region.Width() < narrowest.Width(): |
| 84 narrowest = region |
| 85 if len(seen) == 0: |
| 86 return |
| 87 if self.region_ticks is None: |
| 88 self.region_ticks = {} |
| 89 for name in seen: |
| 90 if not name in self.region_ticks: |
| 91 self.region_ticks[name] = [0, 0] |
| 92 self.region_ticks[name][0] += 1 |
| 93 if name == narrowest.name: |
| 94 self.region_ticks[name][1] += 1 |
| 95 |
| 96 def RegionTicks(self): |
| 97 return self.region_ticks |
| 98 |
| 99 def Regions(self): |
| 100 if self.assembler: |
| 101 return self.assembler.regions |
| 102 else: |
| 103 return [] |
| 66 | 104 |
| 67 def ToString(self): | 105 def ToString(self): |
| 68 name = self.name | 106 name = self.name |
| 69 if name == '': name = '<anonymous>' | 107 if name == '': name = '<anonymous>' |
| 70 return self.type + ': ' + name | 108 return self.type + ': ' + name |
| 71 | 109 |
| 72 | 110 |
| 111 class CodeRegion(object): |
| 112 |
| 113 def __init__(self, start_offset, name): |
| 114 self.start_offset = start_offset |
| 115 self.name = name |
| 116 self.end_offset = None |
| 117 |
| 118 def Contains(self, pc): |
| 119 return (self.start_offset <= pc) and (pc <= self.end_offset) |
| 120 |
| 121 def Width(self): |
| 122 return self.end_offset - self.start_offset |
| 123 |
| 124 |
| 125 class Assembler(object): |
| 126 |
| 127 def __init__(self): |
| 128 # Mapping from region ids to open regions |
| 129 self.pending_regions = {} |
| 130 self.regions = [] |
| 131 |
| 132 |
| 73 class TickProcessor(object): | 133 class TickProcessor(object): |
| 74 | 134 |
| 75 def __init__(self): | 135 def __init__(self): |
| 76 self.log_file = '' | 136 self.log_file = '' |
| 77 self.deleted_code = [] | 137 self.deleted_code = [] |
| 78 self.vm_extent = {} | 138 self.vm_extent = {} |
| 139 # Map from assembler ids to the pending assembler objects |
| 140 self.pending_assemblers = {} |
| 141 # Map from code addresses the have been allocated but not yet officially |
| 142 # created to their assemblers. |
| 143 self.assemblers = {} |
| 79 self.js_entries = splaytree.SplayTree() | 144 self.js_entries = splaytree.SplayTree() |
| 80 self.cpp_entries = splaytree.SplayTree() | 145 self.cpp_entries = splaytree.SplayTree() |
| 81 self.total_number_of_ticks = 0 | 146 self.total_number_of_ticks = 0 |
| 82 self.number_of_library_ticks = 0 | 147 self.number_of_library_ticks = 0 |
| 83 self.unaccounted_number_of_ticks = 0 | 148 self.unaccounted_number_of_ticks = 0 |
| 84 self.excluded_number_of_ticks = 0 | 149 self.excluded_number_of_ticks = 0 |
| 85 | 150 |
| 86 def ProcessLogfile(self, filename, included_state = None): | 151 def ProcessLogfile(self, filename, included_state = None): |
| 87 self.log_file = filename | 152 self.log_file = filename |
| 88 self.included_state = included_state | 153 self.included_state = included_state |
| 89 try: | 154 try: |
| 90 logfile = open(filename, 'rb') | 155 logfile = open(filename, 'rb') |
| 91 except IOError: | 156 except IOError: |
| 92 sys.exit("Could not open logfile: " + filename) | 157 sys.exit("Could not open logfile: " + filename) |
| 93 try: | 158 try: |
| 94 logreader = csv.reader(logfile) | 159 logreader = csv.reader(logfile) |
| 95 for row in logreader: | 160 for row in logreader: |
| 96 if row[0] == 'tick': | 161 if row[0] == 'tick': |
| 97 self.ProcessTick(int(row[1], 16), int(row[2], 16), int(row[3])) | 162 self.ProcessTick(int(row[1], 16), int(row[2], 16), int(row[3])) |
| 98 elif row[0] == 'code-creation': | 163 elif row[0] == 'code-creation': |
| 99 self.ProcessCodeCreation(row[1], int(row[2], 16), int(row[3]), row[4]) | 164 self.ProcessCodeCreation(row[1], int(row[2], 16), int(row[3]), row[4]) |
| 100 elif row[0] == 'code-move': | 165 elif row[0] == 'code-move': |
| 101 self.ProcessCodeMove(int(row[1], 16), int(row[2], 16)) | 166 self.ProcessCodeMove(int(row[1], 16), int(row[2], 16)) |
| 102 elif row[0] == 'code-delete': | 167 elif row[0] == 'code-delete': |
| 103 self.ProcessCodeDelete(int(row[1], 16)) | 168 self.ProcessCodeDelete(int(row[1], 16)) |
| 104 elif row[0] == 'shared-library': | 169 elif row[0] == 'shared-library': |
| 105 self.AddSharedLibraryEntry(row[1], int(row[2], 16), int(row[3], 16)) | 170 self.AddSharedLibraryEntry(row[1], int(row[2], 16), int(row[3], 16)) |
| 106 self.ParseVMSymbols(row[1], int(row[2], 16), int(row[3], 16)) | 171 self.ParseVMSymbols(row[1], int(row[2], 16), int(row[3], 16)) |
| 172 elif row[0] == 'begin-code-region': |
| 173 self.ProcessBeginCodeRegion(int(row[1], 16), int(row[2], 16), int(row[
3], 16), row[4]) |
| 174 elif row[0] == 'end-code-region': |
| 175 self.ProcessEndCodeRegion(int(row[1], 16), int(row[2], 16), int(row[3]
, 16)) |
| 176 elif row[0] == 'code-allocate': |
| 177 self.ProcessCodeAllocate(int(row[1], 16), int(row[2], 16)) |
| 107 finally: | 178 finally: |
| 108 logfile.close() | 179 logfile.close() |
| 109 | 180 |
| 110 def AddSharedLibraryEntry(self, filename, start, end): | 181 def AddSharedLibraryEntry(self, filename, start, end): |
| 111 # Mark the pages used by this library. | 182 # Mark the pages used by this library. |
| 112 i = start | 183 i = start |
| 113 while i < end: | 184 while i < end: |
| 114 page = i >> 12 | 185 page = i >> 12 |
| 115 self.vm_extent[page] = 1 | 186 self.vm_extent[page] = 1 |
| 116 i += 4096 | 187 i += 4096 |
| 117 # Add the library to the entries so that ticks for which we do not | 188 # Add the library to the entries so that ticks for which we do not |
| 118 # have symbol information is reported as belonging to the library. | 189 # have symbol information is reported as belonging to the library. |
| 119 self.cpp_entries.Insert(start, SharedLibraryEntry(start, filename)) | 190 self.cpp_entries.Insert(start, SharedLibraryEntry(start, filename)) |
| 120 | 191 |
| 121 def ParseVMSymbols(self, filename, start, end): | 192 def ParseVMSymbols(self, filename, start, end): |
| 122 return | 193 return |
| 123 | 194 |
| 195 def ProcessCodeAllocate(self, addr, assem): |
| 196 if assem in self.pending_assemblers: |
| 197 assembler = self.pending_assemblers.pop(assem) |
| 198 self.assemblers[addr] = assembler |
| 199 |
| 124 def ProcessCodeCreation(self, type, addr, size, name): | 200 def ProcessCodeCreation(self, type, addr, size, name): |
| 125 self.js_entries.Insert(addr, JSCodeEntry(addr, name, type, size)) | 201 if addr in self.assemblers: |
| 202 assembler = self.assemblers.pop(addr) |
| 203 else: |
| 204 assembler = None |
| 205 self.js_entries.Insert(addr, JSCodeEntry(addr, name, type, size, assembler)) |
| 126 | 206 |
| 127 def ProcessCodeMove(self, from_addr, to_addr): | 207 def ProcessCodeMove(self, from_addr, to_addr): |
| 128 try: | 208 try: |
| 129 removed_node = self.js_entries.Remove(from_addr) | 209 removed_node = self.js_entries.Remove(from_addr) |
| 130 removed_node.value.SetStartAddress(to_addr); | 210 removed_node.value.SetStartAddress(to_addr); |
| 131 self.js_entries.Insert(to_addr, removed_node.value) | 211 self.js_entries.Insert(to_addr, removed_node.value) |
| 132 except 'KeyNotFound': | 212 except 'KeyNotFound': |
| 133 print('Code move event for unknown code: 0x%x' % from_addr) | 213 print('Code move event for unknown code: 0x%x' % from_addr) |
| 134 | 214 |
| 135 def ProcessCodeDelete(self, from_addr): | 215 def ProcessCodeDelete(self, from_addr): |
| 136 try: | 216 try: |
| 137 removed_node = self.js_entries.Remove(from_addr) | 217 removed_node = self.js_entries.Remove(from_addr) |
| 138 self.deleted_code.append(removed_node.value) | 218 self.deleted_code.append(removed_node.value) |
| 139 except 'KeyNotFound': | 219 except 'KeyNotFound': |
| 140 print('Code delete event for unknown code: 0x%x' % from_addr) | 220 print('Code delete event for unknown code: 0x%x' % from_addr) |
| 141 | 221 |
| 222 def ProcessBeginCodeRegion(self, id, assm, start, name): |
| 223 if not assm in self.pending_assemblers: |
| 224 self.pending_assemblers[assm] = Assembler() |
| 225 assembler = self.pending_assemblers[assm] |
| 226 assembler.pending_regions[id] = CodeRegion(start, name) |
| 227 |
| 228 def ProcessEndCodeRegion(self, id, assm, end): |
| 229 assm = self.pending_assemblers[assm] |
| 230 region = assm.pending_regions.pop(id) |
| 231 region.end_offset = end |
| 232 assm.regions.append(region) |
| 233 |
| 142 def IncludeTick(self, pc, sp, state): | 234 def IncludeTick(self, pc, sp, state): |
| 143 return (self.included_state is None) or (self.included_state == state) | 235 return (self.included_state is None) or (self.included_state == state) |
| 144 | 236 |
| 145 def ProcessTick(self, pc, sp, state): | 237 def ProcessTick(self, pc, sp, state): |
| 146 if not self.IncludeTick(pc, sp, state): | 238 if not self.IncludeTick(pc, sp, state): |
| 147 self.excluded_number_of_ticks += 1; | 239 self.excluded_number_of_ticks += 1; |
| 148 return | 240 return |
| 149 self.total_number_of_ticks += 1 | 241 self.total_number_of_ticks += 1 |
| 150 page = pc >> 12 | 242 page = pc >> 12 |
| 151 if page in self.vm_extent: | 243 if page in self.vm_extent: |
| 152 entry = self.cpp_entries.FindGreatestsLessThan(pc).value | 244 entry = self.cpp_entries.FindGreatestsLessThan(pc).value |
| 153 if entry.IsSharedLibraryEntry(): | 245 if entry.IsSharedLibraryEntry(): |
| 154 self.number_of_library_ticks += 1 | 246 self.number_of_library_ticks += 1 |
| 155 entry.IncrementTickCount() | 247 entry.Tick(None) |
| 156 return | 248 return |
| 157 max = self.js_entries.FindMax() | 249 max = self.js_entries.FindMax() |
| 158 min = self.js_entries.FindMin() | 250 min = self.js_entries.FindMin() |
| 159 if max != None and pc < max.key and pc > min.key: | 251 if max != None and pc < max.key and pc > min.key: |
| 160 self.js_entries.FindGreatestsLessThan(pc).value.IncrementTickCount() | 252 code_obj = self.js_entries.FindGreatestsLessThan(pc).value |
| 253 code_obj.Tick(pc) |
| 161 return | 254 return |
| 162 self.unaccounted_number_of_ticks += 1 | 255 self.unaccounted_number_of_ticks += 1 |
| 163 | 256 |
| 164 def PrintResults(self): | 257 def PrintResults(self): |
| 165 print('Statistical profiling result from %s, (%d ticks, %d unaccounted, %d e
xcluded).' % | 258 print('Statistical profiling result from %s, (%d ticks, %d unaccounted, %d e
xcluded).' % |
| 166 (self.log_file, | 259 (self.log_file, |
| 167 self.total_number_of_ticks, | 260 self.total_number_of_ticks, |
| 168 self.unaccounted_number_of_ticks, | 261 self.unaccounted_number_of_ticks, |
| 169 self.excluded_number_of_ticks)) | 262 self.excluded_number_of_ticks)) |
| 170 if self.total_number_of_ticks > 0: | 263 if self.total_number_of_ticks > 0: |
| (...skipping 23 matching lines...) Expand all Loading... |
| 194 total_percentage = entry.tick_count * 100.0 / number_of_accounted_ticks | 287 total_percentage = entry.tick_count * 100.0 / number_of_accounted_ticks |
| 195 if entry.IsSharedLibraryEntry(): | 288 if entry.IsSharedLibraryEntry(): |
| 196 non_library_percentage = 0 | 289 non_library_percentage = 0 |
| 197 else: | 290 else: |
| 198 non_library_percentage = entry.tick_count * 100.0 / number_of_non_libr
ary_ticks | 291 non_library_percentage = entry.tick_count * 100.0 / number_of_non_libr
ary_ticks |
| 199 print(' %(total)5.1f%% %(nonlib)6.1f%% %(name)s' % { | 292 print(' %(total)5.1f%% %(nonlib)6.1f%% %(name)s' % { |
| 200 'total' : total_percentage, | 293 'total' : total_percentage, |
| 201 'nonlib' : non_library_percentage, | 294 'nonlib' : non_library_percentage, |
| 202 'name' : entry.ToString() | 295 'name' : entry.ToString() |
| 203 }) | 296 }) |
| 297 region_ticks = entry.RegionTicks() |
| 298 if not region_ticks is None: |
| 299 items = region_ticks.items() |
| 300 items.sort(key=lambda e: e[1][1], reverse=True) |
| 301 for (name, ticks) in items: |
| 302 print(' - %(cumulated)5.1f%% %(total)5.1f%% %(name
)s' % { |
| 303 'cumulated' : ticks[1] * 100.0 / entry.tick_count, |
| 304 'total' : ticks[0] * 100.0 / entry.tick_count, |
| 305 'name': name |
| 306 }) |
| 204 | 307 |
| 205 if __name__ == '__main__': | 308 if __name__ == '__main__': |
| 206 sys.exit('You probably want to run windows-tick-processor.py or linux-tick-pro
cessor.py.') | 309 sys.exit('You probably want to run windows-tick-processor.py or linux-tick-pro
cessor.py.') |
| OLD | NEW |