Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #! /usr/bin/python | 1 #! /usr/bin/python |
| 2 # | 2 # |
| 3 # Copyright 2016 the V8 project authors. All rights reserved. | 3 # Copyright 2016 the V8 project authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 # | 6 # |
| 7 | 7 |
| 8 import argparse | 8 import argparse |
| 9 import heapq | 9 import heapq |
| 10 import json | 10 import json |
| 11 from matplotlib import colors | 11 from matplotlib import colors |
| 12 from matplotlib import pyplot | 12 from matplotlib import pyplot |
| 13 import numpy | 13 import numpy |
| 14 import struct | 14 import struct |
| 15 import sys | |
| 15 | 16 |
| 16 | 17 |
| 17 __DESCRIPTION = """ | 18 __DESCRIPTION = """ |
| 18 Process v8.ignition_dispatches_counters.json and list top counters, | 19 Process v8.ignition_dispatches_counters.json and list top counters, |
| 19 or plot a dispatch heatmap. | 20 or plot a dispatch heatmap. |
| 20 | 21 |
| 21 Please note that those handlers that may not or will never dispatch | 22 Please note that those handlers that may not or will never dispatch |
| 22 (e.g. Return or Throw) do not show up in the results. | 23 (e.g. Return or Throw) do not show up in the results. |
| 23 """ | 24 """ |
| 24 | 25 |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 44 # Display the top 5 sources and destinations of dispatches to/from LdaZero | 45 # Display the top 5 sources and destinations of dispatches to/from LdaZero |
| 45 $ tools/ignition/bytecode_dispatches_report.py -f LdaZero -n 5 | 46 $ tools/ignition/bytecode_dispatches_report.py -f LdaZero -n 5 |
| 46 """ | 47 """ |
| 47 | 48 |
| 48 __COUNTER_BITS = struct.calcsize("P") * 8 # Size in bits of a pointer | 49 __COUNTER_BITS = struct.calcsize("P") * 8 # Size in bits of a pointer |
| 49 __COUNTER_MAX = 2**__COUNTER_BITS - 1 | 50 __COUNTER_MAX = 2**__COUNTER_BITS - 1 |
| 50 | 51 |
| 51 | 52 |
| 52 def warn_if_counter_may_have_saturated(dispatches_table): | 53 def warn_if_counter_may_have_saturated(dispatches_table): |
| 53 for source, counters_from_source in dispatches_table.items(): | 54 for source, counters_from_source in dispatches_table.items(): |
| 54 for destination, counter in counters_from_source.items(): | 55 for destination, (counter, ratio) in counters_from_source.items(): |
| 55 if counter == __COUNTER_MAX: | 56 if counter == __COUNTER_MAX: |
| 56 print "WARNING: {} -> {} may have saturated.".format(source, | 57 print "WARNING: {} -> {} may have saturated.".format(source, |
| 57 destination) | 58 destination) |
| 58 | 59 |
| 59 | 60 |
| 60 def find_top_bytecode_dispatch_pairs(dispatches_table, top_count): | 61 def find_top_bytecode_dispatch_pairs(dispatches_table, top_count): |
| 61 def flattened_counters_generator(): | 62 def flattened_counters_generator(): |
| 62 for source, counters_from_source in dispatches_table.items(): | 63 for source, counters_from_source in dispatches_table.items(): |
| 63 for destination, counter in counters_from_source.items(): | 64 for destination, (counter, ratio) in counters_from_source.items(): |
| 64 yield source, destination, counter | 65 yield source, destination, counter |
| 65 | 66 |
| 66 return heapq.nlargest(top_count, flattened_counters_generator(), | 67 return heapq.nlargest(top_count, flattened_counters_generator(), |
| 67 key=lambda x: x[2]) | 68 key=lambda x: x[2]) |
| 68 | 69 |
| 69 | 70 |
| 70 def print_top_bytecode_dispatch_pairs(dispatches_table, top_count): | 71 def print_top_bytecode_dispatch_pairs(dispatches_table, top_count): |
| 71 top_bytecode_dispatch_pairs = ( | 72 top_bytecode_dispatch_pairs = ( |
| 72 find_top_bytecode_dispatch_pairs(dispatches_table, top_count)) | 73 find_top_bytecode_dispatch_pairs(dispatches_table, top_count)) |
| 73 print "Top {} bytecode dispatch pairs:".format(top_count) | 74 print "Top {} bytecode dispatch pairs:".format(top_count) |
| 74 for source, destination, counter in top_bytecode_dispatch_pairs: | 75 for source, destination, counter in top_bytecode_dispatch_pairs: |
| 75 print "{:>12d}\t{} -> {}".format(counter, source, destination) | 76 print "{:>12d}\t{} -> {}".format(counter, source, destination) |
| 76 | 77 |
| 77 | 78 |
| 78 def find_top_bytecodes(dispatches_table): | 79 def find_top_bytecodes(dispatch_totals): |
| 79 top_bytecodes = [] | 80 top_bytecodes = list(iteritems(dispatch_totals)) |
| 80 for bytecode, counters_from_bytecode in dispatches_table.items(): | |
| 81 top_bytecodes.append((bytecode, sum(counters_from_bytecode.values()))) | |
| 82 top_bytecodes.sort(key=lambda x: x[1], reverse=True) | 81 top_bytecodes.sort(key=lambda x: x[1], reverse=True) |
| 83 return top_bytecodes | 82 return top_bytecodes |
| 84 | 83 |
| 85 | 84 |
| 86 def print_top_bytecodes(dispatches_table): | 85 def print_top_bytecodes(dispatch_totals): |
| 87 top_bytecodes = find_top_bytecodes(dispatches_table) | 86 top_bytecodes = find_top_bytecodes(dispatch_totals) |
| 88 print "Top bytecodes:" | 87 print "Top bytecodes:" |
| 89 for bytecode, counter in top_bytecodes: | 88 for bytecode, counter in top_bytecodes: |
| 90 print "{:>12d}\t{}".format(counter, bytecode) | 89 print "{:>12d}\t{}".format(counter, bytecode) |
| 91 | 90 |
| 92 | 91 |
| 93 def find_top_dispatch_sources(dispatches_table, destination, top_count): | 92 def find_top_dispatch_sources(dispatches_table, destination, top_count, |
| 93 sort_source_relative): | |
| 94 def source_counters_generator(): | 94 def source_counters_generator(): |
| 95 for source, table_row in dispatches_table.items(): | 95 for source, table_row in dispatches_table.items(): |
| 96 if destination in table_row: | 96 if destination in table_row: |
| 97 yield source, table_row[destination] | 97 yield source, table_row[destination] |
| 98 | 98 |
| 99 return heapq.nlargest(top_count, source_counters_generator(), | 99 return heapq.nlargest(top_count, source_counters_generator(), |
| 100 key=lambda x: x[1]) | 100 key=lambda x: x[1][1 if sort_source_relative else 0]) |
| 101 | 101 |
| 102 | 102 |
| 103 def print_top_dispatch_sources_and_destinations(dispatches_table, bytecode, | 103 def print_top_dispatch_sources_and_destinations(dispatches_table, bytecode, |
| 104 top_count): | 104 top_count, sort_relative): |
| 105 top_sources = find_top_dispatch_sources(dispatches_table, bytecode, top_count) | 105 top_sources = find_top_dispatch_sources(dispatches_table, bytecode, |
| 106 top_count, sort_relative) | |
| 106 top_destinations = heapq.nlargest(top_count, | 107 top_destinations = heapq.nlargest(top_count, |
| 107 dispatches_table[bytecode].items(), | 108 dispatches_table[bytecode].items(), |
| 108 key=lambda x: x[1]) | 109 key=lambda x: x[1][0]) |
| 109 | 110 |
| 110 print "Top sources of dispatches to {}:".format(bytecode) | 111 print "Top sources of dispatches to {}:".format(bytecode) |
| 111 for source_name, counter in top_sources: | 112 for source_name, (counter, ratio) in top_sources: |
| 112 print "{:>12d}\t{}".format(counter, source_name) | 113 print "{:>12d}\t{:>5.1f}%\t{}".format(counter, ratio * 100, source_name) |
| 113 | 114 |
| 114 print "\nTop destinations of dispatches from {}:".format(bytecode) | 115 print "\nTop destinations of dispatches from {}:".format(bytecode) |
| 115 for destination_name, counter in top_destinations: | 116 for destination_name, (counter, ratio) in top_destinations: |
| 116 print "{:>12d}\t{}".format(counter, destination_name) | 117 print "{:>12d}\t{:>5.1f}%\t{}".format(counter, ratio * 100, destination_name ) |
| 117 | 118 |
| 118 | 119 |
| 119 def build_counters_matrix(dispatches_table): | 120 def build_counters_matrix(dispatches_table): |
| 120 labels = sorted(dispatches_table.keys()) | 121 labels = sorted(dispatches_table.keys()) |
| 121 | 122 |
| 122 counters_matrix = numpy.empty([len(labels), len(labels)], dtype=int) | 123 counters_matrix = numpy.empty([len(labels), len(labels)], dtype=int) |
| 123 for from_index, from_name in enumerate(labels): | 124 for from_index, from_name in enumerate(labels): |
| 124 current_row = dispatches_table[from_name]; | 125 current_row = dispatches_table[from_name]; |
| 125 for to_index, to_name in enumerate(labels): | 126 for to_index, to_name in enumerate(labels): |
| 126 counters_matrix[from_index, to_index] = current_row.get(to_name, 0) | 127 counters_matrix[from_index, to_index] = current_row.get(to_name, (0, 0))[0 ] |
| 127 | 128 |
| 128 # Reverse y axis for a nicer appearance | 129 # Reverse y axis for a nicer appearance |
| 129 xlabels = labels | 130 xlabels = labels |
| 130 ylabels = list(reversed(xlabels)) | 131 ylabels = list(reversed(xlabels)) |
| 131 counters_matrix = numpy.flipud(counters_matrix) | 132 counters_matrix = numpy.flipud(counters_matrix) |
| 132 | 133 |
| 133 return counters_matrix, xlabels, ylabels | 134 return counters_matrix, xlabels, ylabels |
| 134 | 135 |
| 135 | 136 |
| 136 def plot_dispatches_table(dispatches_table, figure, axis): | 137 def plot_dispatches_table(dispatches_table, figure, axis): |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 208 help="print top dispatch sources and destinations to the specified bytecode" | 209 help="print top dispatch sources and destinations to the specified bytecode" |
| 209 ) | 210 ) |
| 210 command_line_parser.add_argument( | 211 command_line_parser.add_argument( |
| 211 "--output-filename", "-o", | 212 "--output-filename", "-o", |
| 212 metavar="<output filename>", | 213 metavar="<output filename>", |
| 213 default="v8.ignition_dispatches_table.svg", | 214 default="v8.ignition_dispatches_table.svg", |
| 214 help=("file to save the plot file to. File type is deduced from the " | 215 help=("file to save the plot file to. File type is deduced from the " |
| 215 "extension. PDF, SVG, PNG supported") | 216 "extension. PDF, SVG, PNG supported") |
| 216 ) | 217 ) |
| 217 command_line_parser.add_argument( | 218 command_line_parser.add_argument( |
| 219 "--sort-sources-relative", "-r", | |
| 220 action="store_true", | |
| 221 help=("print top sources in order to how often they dispatch to the " | |
| 222 "speficied bytecode") | |
|
rmcilroy
2016/07/19 09:23:08
fix typo
klaasb
2016/07/19 10:24:27
Done.
| |
| 223 ) | |
| 224 command_line_parser.add_argument( | |
| 218 "input_filename", | 225 "input_filename", |
| 219 metavar="<input filename>", | 226 metavar="<input filename>", |
| 220 default="v8.ignition_dispatches_table.json", | 227 default="v8.ignition_dispatches_table.json", |
| 221 nargs='?', | 228 nargs='?', |
| 222 help="Ignition counters JSON file" | 229 help="Ignition counters JSON file" |
| 223 ) | 230 ) |
| 224 | 231 |
| 225 return command_line_parser.parse_args() | 232 return command_line_parser.parse_args() |
| 226 | 233 |
| 227 | 234 |
| 235 def itervalues(d): | |
| 236 return d.values() if sys.version_info[0] > 2 else d.itervalues() | |
| 237 | |
| 238 | |
| 239 def iteritems(d): | |
| 240 return d.items() if sys.version_info[0] > 2 else d.iteritems() | |
| 241 | |
| 242 | |
| 228 def main(): | 243 def main(): |
| 229 program_options = parse_command_line() | 244 program_options = parse_command_line() |
| 230 | 245 |
| 231 with open(program_options.input_filename) as stream: | 246 with open(program_options.input_filename) as stream: |
| 232 dispatches_table = json.load(stream) | 247 dispatches_table = json.load(stream) |
| 233 | 248 |
| 249 dispatch_totals = {} | |
| 250 for source, destinations in dispatches_table.items(): | |
| 251 total = sum(itervalues(destinations)) | |
| 252 dispatch_totals[source] = total | |
| 253 total = float(total) | |
| 254 for destination, count in destinations.iteritems(): | |
| 255 destinations[destination] = (count, count / total) | |
|
rmcilroy
2016/07/19 09:23:08
I think it would be better to do this as a process
klaasb
2016/07/19 10:24:27
Done.
| |
| 256 | |
| 234 warn_if_counter_may_have_saturated(dispatches_table) | 257 warn_if_counter_may_have_saturated(dispatches_table) |
| 235 | 258 |
| 236 if program_options.plot: | 259 if program_options.plot: |
| 237 figure, axis = pyplot.subplots() | 260 figure, axis = pyplot.subplots() |
| 238 plot_dispatches_table(dispatches_table, figure, axis) | 261 plot_dispatches_table(dispatches_table, figure, axis) |
| 239 | 262 |
| 240 if program_options.interactive: | 263 if program_options.interactive: |
| 241 pyplot.show() | 264 pyplot.show() |
| 242 else: | 265 else: |
| 243 figure.set_size_inches(program_options.plot_size, | 266 figure.set_size_inches(program_options.plot_size, |
| 244 program_options.plot_size) | 267 program_options.plot_size) |
| 245 pyplot.savefig(program_options.output_filename) | 268 pyplot.savefig(program_options.output_filename) |
| 246 elif program_options.top_bytecode_dispatch_pairs: | 269 elif program_options.top_bytecode_dispatch_pairs: |
| 247 print_top_bytecode_dispatch_pairs( | 270 print_top_bytecode_dispatch_pairs( |
| 248 dispatches_table, program_options.top_entries_count) | 271 dispatches_table, program_options.top_entries_count) |
| 249 elif program_options.top_dispatches_for_bytecode: | 272 elif program_options.top_dispatches_for_bytecode: |
| 250 print_top_dispatch_sources_and_destinations( | 273 print_top_dispatch_sources_and_destinations( |
| 251 dispatches_table, program_options.top_dispatches_for_bytecode, | 274 dispatches_table, program_options.top_dispatches_for_bytecode, |
| 252 program_options.top_entries_count) | 275 program_options.top_entries_count, program_options.sort_sources_relative) |
| 253 else: | 276 else: |
| 254 print_top_bytecodes(dispatches_table) | 277 print_top_bytecodes(dispatch_totals) |
| 255 | 278 |
| 256 | 279 |
| 257 if __name__ == "__main__": | 280 if __name__ == "__main__": |
| 258 main() | 281 main() |
| OLD | NEW |