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 |