Index: tools/ignition/bytecode_dispatches_report.py |
diff --git a/tools/ignition/bytecode_dispatches_report.py b/tools/ignition/bytecode_dispatches_report.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..2c04bef600e0336e9cb84f21d928b20eb3eb61eb |
--- /dev/null |
+++ b/tools/ignition/bytecode_dispatches_report.py |
@@ -0,0 +1,193 @@ |
+#! /usr/bin/python |
+# |
+# Copyright 2016 the V8 project authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+# |
+ |
+import argparse |
+import heapq |
+import json |
+from matplotlib import colors |
+from matplotlib import pyplot |
+import numpy |
+import struct |
+ |
+ |
+__DESCRIPTION = """ |
+Process v8.ignition_dispatches_counters.json and list top counters, |
+or plot a dispatch heatmap. |
+""" |
+ |
+ |
+__HELP_EPILOGUE = """ |
+examples: |
+ # Print the top 10 counters, reading from default filename |
+ # v8.ignition_dispatches_counters.json (default mode) |
+ $ tools/ignition/bytecode_dispatches_report.py |
+ |
+ # Print the top 15 counters reading from data.json |
+ $ tools/ignition/bytecode_dispatches_report.py -t 15 data.json |
+ |
+ # Save heatmap to default filename v8.ignition_dispatches_counters.svg |
+ $ tools/ignition/bytecode_dispatches_report.py -p |
+ |
+ # Save heatmap to filename data.svg |
+ $ tools/ignition/bytecode_dispatches_report.py -p -o data.svg |
+ |
+ # Open the heatmap in an interactive viewer |
+ $ tools/ignition/bytecode_dispatches_report.py -p -i |
+""" |
+ |
+__COUNTER_BITS = struct.calcsize("P") * 8 # Size in bits of a pointer |
+__COUNTER_MAX = 2**__COUNTER_BITS - 1 |
+ |
+ |
+def warn_if_counter_may_have_saturated(dispatches_table): |
+ for source, counters_from_source in dispatches_table.items(): |
+ for destination, counter in counters_from_source.items(): |
+ if counter == __COUNTER_MAX: |
+ print "WARNING: {} -> {} may have saturated.".format(source, |
+ destination) |
+ |
+ |
+def find_top_counters(dispatches_table, top_count): |
+ def flattened_counters_generator(): |
+ for source, counters_from_source in dispatches_table.items(): |
+ for destination, counter in counters_from_source.items(): |
+ yield source, destination, counter |
+ |
+ return heapq.nlargest(top_count, flattened_counters_generator(), |
+ key=lambda x: x[2]) |
+ |
+ |
+def print_top_counters(dispatches_table, top_count): |
+ top_counters = find_top_counters(dispatches_table, top_count) |
+ print "Top {} dispatch counters:".format(top_count) |
+ for source, destination, counter in top_counters: |
+ print "{:>12d}\t{} -> {}".format(counter, source, destination) |
+ |
+ |
+def build_counters_matrix(dispatches_table): |
+ labels = sorted(dispatches_table.keys()) |
+ |
+ counters_matrix = numpy.empty([len(labels), len(labels)], dtype=int) |
+ for from_index, from_name in enumerate(labels): |
+ current_row = dispatches_table[from_name]; |
+ for to_index, to_name in enumerate(labels): |
+ counters_matrix[from_index, to_index] = current_row.get(to_name, 0) |
+ |
+ # Reverse y axis for a nicer appearance |
+ xlabels = labels |
+ ylabels = list(reversed(xlabels)) |
+ counters_matrix = numpy.flipud(counters_matrix) |
+ |
+ return counters_matrix, xlabels, ylabels |
+ |
+ |
+def plot_dispatches_table(dispatches_table, figure, axis): |
+ counters_matrix, xlabels, ylabels = build_counters_matrix(dispatches_table) |
+ |
+ image = axis.pcolor( |
+ counters_matrix, |
+ cmap='jet', |
+ norm=colors.LogNorm(), |
+ edgecolor='grey', |
+ linestyle='dotted', |
+ linewidth=0.5 |
+ ) |
+ |
+ axis.xaxis.set( |
+ ticks=numpy.arange(0.5, len(xlabels)), |
+ label="From bytecode handler" |
+ ) |
+ axis.xaxis.tick_top() |
+ axis.set_xlim(0, len(xlabels)) |
+ axis.set_xticklabels(xlabels, rotation='vertical') |
+ |
+ axis.yaxis.set( |
+ ticks=numpy.arange(0.5, len(ylabels)), |
+ label="To bytecode handler", |
+ ticklabels=ylabels |
+ ) |
+ axis.set_ylim(0, len(ylabels)) |
+ |
+ figure.colorbar( |
+ image, |
+ ax=axis, |
+ fraction=0.01, |
+ pad=0.01 |
+ ) |
+ |
+ |
+def parse_command_line(): |
+ command_line_parser = argparse.ArgumentParser( |
+ formatter_class=argparse.RawDescriptionHelpFormatter, |
+ description=__DESCRIPTION, |
+ epilog=__HELP_EPILOGUE |
+ ) |
+ command_line_parser.add_argument( |
+ "--plot_size", "-s", |
+ metavar="N", |
+ default=30, |
+ help="shorter side, in inches, of the output plot (default 30)" |
+ ) |
+ command_line_parser.add_argument( |
+ "--plot", "-p", |
+ action="store_true", |
+ help="plot dispatches table heatmap" |
+ ) |
+ command_line_parser.add_argument( |
+ "--interactive", "-i", |
+ action="store_true", |
+ help="open an interactive viewer, rather than writing to file" |
+ ) |
+ command_line_parser.add_argument( |
+ "--top_count", "-t", |
+ metavar="N", |
+ type=int, |
+ default=10, |
+ help="print the top N counters (default 10)" |
+ ) |
+ command_line_parser.add_argument( |
+ "--output_filename", "-o", |
+ metavar="<output filename>", |
+ default="v8.ignition_dispatches_table.svg", |
+ help=("file to save the plot file to. File type is deduced from the " |
+ "extension. PDF, SVG, PNG supported") |
+ ) |
+ command_line_parser.add_argument( |
+ "input_filename", |
+ metavar="<input filename>", |
+ default="v8.ignition_dispatches_table.json", |
+ nargs='?', |
+ help="Ignition counters JSON file" |
+ ) |
+ |
+ return command_line_parser.parse_args() |
+ |
+ |
+def main(): |
+ program_options = parse_command_line() |
+ |
+ with open(program_options.input_filename) as stream: |
+ dispatches_table = json.load(stream) |
+ |
+ warn_if_counter_may_have_saturated(dispatches_table) |
+ |
+ if program_options.plot: |
+ figure, axis = pyplot.subplots() |
+ plot_dispatches_table(dispatches_table, figure, axis) |
+ |
+ if program_options.interactive: |
+ pyplot.show() |
+ else: |
+ figure.set_size_inches(program_options.plot_size, |
+ program_options.plot_size) |
+ pyplot.savefig(program_options.output_filename) |
+ else: |
+ print_top_counters(dispatches_table, program_options.top_count) |
+ |
+ |
+if __name__ == "__main__": |
+ main() |