blob: 1c140141f0dd3904a271001627372abaf072c639 [file] [log] [blame]
Ben Murdochc5610432016-08-08 18:44:38 +01001#! /usr/bin/python
2#
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
5# found in the LICENSE file.
6#
7
8import argparse
9import heapq
10import json
11from matplotlib import colors
12from matplotlib import pyplot
13import numpy
14import struct
15
16
17__DESCRIPTION = """
18Process v8.ignition_dispatches_counters.json and list top counters,
19or plot a dispatch heatmap.
20
21Please 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"""
24
25
26__HELP_EPILOGUE = """
27examples:
28 # Print the hottest bytecodes in descending order, reading from
29 # default filename v8.ignition_dispatches_counters.json (default mode)
30 $ tools/ignition/bytecode_dispatches_report.py
31
32 # Print the hottest 15 bytecode dispatch pairs reading from data.json
33 $ tools/ignition/bytecode_dispatches_report.py -t -n 15 data.json
34
35 # Save heatmap to default filename v8.ignition_dispatches_counters.svg
36 $ tools/ignition/bytecode_dispatches_report.py -p
37
38 # Save heatmap to filename data.svg
39 $ tools/ignition/bytecode_dispatches_report.py -p -o data.svg
40
41 # Open the heatmap in an interactive viewer
42 $ tools/ignition/bytecode_dispatches_report.py -p -i
43
44 # Display the top 5 sources and destinations of dispatches to/from LdaZero
45 $ tools/ignition/bytecode_dispatches_report.py -f LdaZero -n 5
46"""
47
48__COUNTER_BITS = struct.calcsize("P") * 8 # Size in bits of a pointer
49__COUNTER_MAX = 2**__COUNTER_BITS - 1
50
51
52def warn_if_counter_may_have_saturated(dispatches_table):
53 for source, counters_from_source in dispatches_table.items():
54 for destination, counter in counters_from_source.items():
55 if counter == __COUNTER_MAX:
56 print "WARNING: {} -> {} may have saturated.".format(source,
57 destination)
58
59
60def find_top_bytecode_dispatch_pairs(dispatches_table, top_count):
61 def flattened_counters_generator():
62 for source, counters_from_source in dispatches_table.items():
63 for destination, counter in counters_from_source.items():
64 yield source, destination, counter
65
66 return heapq.nlargest(top_count, flattened_counters_generator(),
67 key=lambda x: x[2])
68
69
70def print_top_bytecode_dispatch_pairs(dispatches_table, top_count):
71 top_bytecode_dispatch_pairs = (
72 find_top_bytecode_dispatch_pairs(dispatches_table, top_count))
73 print "Top {} bytecode dispatch pairs:".format(top_count)
74 for source, destination, counter in top_bytecode_dispatch_pairs:
75 print "{:>12d}\t{} -> {}".format(counter, source, destination)
76
77
78def find_top_bytecodes(dispatches_table):
79 top_bytecodes = []
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)
83 return top_bytecodes
84
85
86def print_top_bytecodes(dispatches_table):
87 top_bytecodes = find_top_bytecodes(dispatches_table)
88 print "Top bytecodes:"
89 for bytecode, counter in top_bytecodes:
90 print "{:>12d}\t{}".format(counter, bytecode)
91
92
93def find_top_dispatch_sources(dispatches_table, destination, top_count):
94 def source_counters_generator():
95 for source, table_row in dispatches_table.items():
96 if destination in table_row:
97 yield source, table_row[destination]
98
99 return heapq.nlargest(top_count, source_counters_generator(),
100 key=lambda x: x[1])
101
102
103def print_top_dispatch_sources_and_destinations(dispatches_table, bytecode,
104 top_count):
105 top_sources = find_top_dispatch_sources(dispatches_table, bytecode, top_count)
106 top_destinations = heapq.nlargest(top_count,
107 dispatches_table[bytecode].items(),
108 key=lambda x: x[1])
109
110 print "Top sources of dispatches to {}:".format(bytecode)
111 for source_name, counter in top_sources:
112 print "{:>12d}\t{}".format(counter, source_name)
113
114 print "\nTop destinations of dispatches from {}:".format(bytecode)
115 for destination_name, counter in top_destinations:
116 print "{:>12d}\t{}".format(counter, destination_name)
117
118
119def build_counters_matrix(dispatches_table):
120 labels = sorted(dispatches_table.keys())
121
122 counters_matrix = numpy.empty([len(labels), len(labels)], dtype=int)
123 for from_index, from_name in enumerate(labels):
124 current_row = dispatches_table[from_name];
125 for to_index, to_name in enumerate(labels):
126 counters_matrix[from_index, to_index] = current_row.get(to_name, 0)
127
128 # Reverse y axis for a nicer appearance
129 xlabels = labels
130 ylabels = list(reversed(xlabels))
131 counters_matrix = numpy.flipud(counters_matrix)
132
133 return counters_matrix, xlabels, ylabels
134
135
136def plot_dispatches_table(dispatches_table, figure, axis):
137 counters_matrix, xlabels, ylabels = build_counters_matrix(dispatches_table)
138
139 image = axis.pcolor(
140 counters_matrix,
141 cmap="jet",
142 norm=colors.LogNorm(),
143 edgecolor="grey",
144 linestyle="dotted",
145 linewidth=0.5
146 )
147
148 axis.xaxis.set(
149 ticks=numpy.arange(0.5, len(xlabels)),
150 label="From bytecode handler"
151 )
152 axis.xaxis.tick_top()
153 axis.set_xlim(0, len(xlabels))
154 axis.set_xticklabels(xlabels, rotation="vertical")
155
156 axis.yaxis.set(
157 ticks=numpy.arange(0.5, len(ylabels)),
158 label="To bytecode handler",
159 ticklabels=ylabels
160 )
161 axis.set_ylim(0, len(ylabels))
162
163 figure.colorbar(
164 image,
165 ax=axis,
166 fraction=0.01,
167 pad=0.01
168 )
169
170
171def parse_command_line():
172 command_line_parser = argparse.ArgumentParser(
173 formatter_class=argparse.RawDescriptionHelpFormatter,
174 description=__DESCRIPTION,
175 epilog=__HELP_EPILOGUE
176 )
177 command_line_parser.add_argument(
178 "--plot-size", "-s",
179 metavar="N",
180 default=30,
181 help="shorter side in inches of the output plot (default 30)"
182 )
183 command_line_parser.add_argument(
184 "--plot", "-p",
185 action="store_true",
186 help="plot dispatch pairs heatmap"
187 )
188 command_line_parser.add_argument(
189 "--interactive", "-i",
190 action="store_true",
191 help="open the heatmap in an interactive viewer, instead of writing to file"
192 )
193 command_line_parser.add_argument(
194 "--top-bytecode-dispatch-pairs", "-t",
195 action="store_true",
196 help="print the top bytecode dispatch pairs"
197 )
198 command_line_parser.add_argument(
199 "--top-entries-count", "-n",
200 metavar="N",
201 type=int,
202 default=10,
203 help="print N top entries when running with -t or -f (default 10)"
204 )
205 command_line_parser.add_argument(
206 "--top-dispatches-for-bytecode", "-f",
207 metavar="<bytecode name>",
208 help="print top dispatch sources and destinations to the specified bytecode"
209 )
210 command_line_parser.add_argument(
211 "--output-filename", "-o",
212 metavar="<output filename>",
213 default="v8.ignition_dispatches_table.svg",
214 help=("file to save the plot file to. File type is deduced from the "
215 "extension. PDF, SVG, PNG supported")
216 )
217 command_line_parser.add_argument(
218 "input_filename",
219 metavar="<input filename>",
220 default="v8.ignition_dispatches_table.json",
221 nargs='?',
222 help="Ignition counters JSON file"
223 )
224
225 return command_line_parser.parse_args()
226
227
228def main():
229 program_options = parse_command_line()
230
231 with open(program_options.input_filename) as stream:
232 dispatches_table = json.load(stream)
233
234 warn_if_counter_may_have_saturated(dispatches_table)
235
236 if program_options.plot:
237 figure, axis = pyplot.subplots()
238 plot_dispatches_table(dispatches_table, figure, axis)
239
240 if program_options.interactive:
241 pyplot.show()
242 else:
243 figure.set_size_inches(program_options.plot_size,
244 program_options.plot_size)
245 pyplot.savefig(program_options.output_filename)
246 elif program_options.top_bytecode_dispatch_pairs:
247 print_top_bytecode_dispatch_pairs(
248 dispatches_table, program_options.top_entries_count)
249 elif program_options.top_dispatches_for_bytecode:
250 print_top_dispatch_sources_and_destinations(
251 dispatches_table, program_options.top_dispatches_for_bytecode,
252 program_options.top_entries_count)
253 else:
254 print_top_bytecodes(dispatches_table)
255
256
257if __name__ == "__main__":
258 main()