| Index: tools/ignition/linux_perf_bytecode_annotate.py
|
| diff --git a/tools/ignition/linux_perf_bytecode_annotate.py b/tools/ignition/linux_perf_bytecode_annotate.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..6681190d9909a461b14de2bd2a6ee73c58caa49a
|
| --- /dev/null
|
| +++ b/tools/ignition/linux_perf_bytecode_annotate.py
|
| @@ -0,0 +1,174 @@
|
| +#! /usr/bin/python2
|
| +#
|
| +# 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 collections
|
| +import os
|
| +import subprocess
|
| +import sys
|
| +
|
| +
|
| +__DESCRIPTION = """
|
| +Processes a perf.data sample file and annotates the hottest instructions in a
|
| +given bytecode handler.
|
| +"""
|
| +
|
| +
|
| +__HELP_EPILOGUE = """
|
| +Note:
|
| + This tool uses the disassembly of interpreter's bytecode handler codegen
|
| + from out/<arch>.debug/d8. you should ensure that this binary is in-sync with
|
| + the version used to generate the perf profile.
|
| +
|
| + Also, the tool depends on the symbol offsets from perf samples being accurate.
|
| + As such, you should use the ":pp" suffix for events.
|
| +
|
| +Examples:
|
| + EVENT_TYPE=cycles:pp tools/run-perf.sh out/x64.release/d8
|
| + tools/ignition/linux_perf_bytecode_annotate.py Add
|
| +"""
|
| +
|
| +
|
| +def bytecode_offset_generator(perf_stream, bytecode_name):
|
| + skip_until_end_of_chain = False
|
| + bytecode_symbol = "BytecodeHandler:" + bytecode_name;
|
| +
|
| + for line in perf_stream:
|
| + # Lines starting with a "#" are comments, skip them.
|
| + if line[0] == "#":
|
| + continue
|
| + line = line.strip()
|
| +
|
| + # Empty line signals the end of the callchain.
|
| + if not line:
|
| + skip_until_end_of_chain = False
|
| + continue
|
| +
|
| + if skip_until_end_of_chain:
|
| + continue
|
| +
|
| + symbol_and_offset = line.split(" ", 1)[1]
|
| +
|
| + if symbol_and_offset.startswith("BytecodeHandler:"):
|
| + skip_until_end_of_chain = True
|
| +
|
| + if symbol_and_offset.startswith(bytecode_symbol):
|
| + yield int(symbol_and_offset.split("+", 1)[1], 16)
|
| +
|
| +
|
| +def bytecode_offset_counts(bytecode_offsets):
|
| + offset_counts = collections.defaultdict(int)
|
| + for offset in bytecode_offsets:
|
| + offset_counts[offset] += 1
|
| + return offset_counts
|
| +
|
| +
|
| +def bytecode_disassembly_generator(ignition_codegen, bytecode_name):
|
| + name_string = "name = " + bytecode_name
|
| + for line in ignition_codegen:
|
| + if line.startswith(name_string):
|
| + break
|
| +
|
| + # Found the bytecode disassembly.
|
| + for line in ignition_codegen:
|
| + line = line.strip()
|
| + # Blank line marks the end of the bytecode's disassembly.
|
| + if not line:
|
| + return
|
| +
|
| + # Only yield disassembly output.
|
| + if not line.startswith("0x"):
|
| + continue
|
| +
|
| + yield line
|
| +
|
| +
|
| +def print_disassembly_annotation(offset_counts, bytecode_disassembly):
|
| + total = sum(offset_counts.values())
|
| + offsets = sorted(offset_counts, reverse=True)
|
| + def next_offset():
|
| + return offsets.pop() if offsets else -1
|
| +
|
| + current_offset = next_offset()
|
| + print current_offset;
|
| +
|
| + for line in bytecode_disassembly:
|
| + disassembly_offset = int(line.split()[1])
|
| + if disassembly_offset == current_offset:
|
| + count = offset_counts[current_offset]
|
| + percentage = 100.0 * count / total
|
| + print "{:>8d} ({:>5.1f}%) ".format(count, percentage),
|
| + current_offset = next_offset()
|
| + else:
|
| + print " ",
|
| + print line
|
| +
|
| + if offsets:
|
| + print ("WARNING: Offsets not empty. Output is most likely invalid due to "
|
| + "a mismatch between perf output and debug d8 binary.")
|
| +
|
| +
|
| +def parse_command_line():
|
| + command_line_parser = argparse.ArgumentParser(
|
| + formatter_class=argparse.RawDescriptionHelpFormatter,
|
| + description=__DESCRIPTION,
|
| + epilog=__HELP_EPILOGUE)
|
| +
|
| + command_line_parser.add_argument(
|
| + "--arch", "-a",
|
| + help="The architecture (default: x64)",
|
| + default="x64",
|
| + )
|
| + command_line_parser.add_argument(
|
| + "--input", "-i",
|
| + help="perf sample file to process (default: perf.data)",
|
| + default="perf.data",
|
| + metavar="<perf filename>",
|
| + dest="perf_filename"
|
| + )
|
| + command_line_parser.add_argument(
|
| + "--output", "-o",
|
| + help="output file name (stdout if omitted)",
|
| + type=argparse.FileType("wt"),
|
| + default=sys.stdout,
|
| + metavar="<output filename>",
|
| + dest="output_stream"
|
| + )
|
| + command_line_parser.add_argument(
|
| + "bytecode_name",
|
| + metavar="<bytecode name>",
|
| + nargs="?",
|
| + help="The bytecode handler to annotate"
|
| + )
|
| +
|
| + return command_line_parser.parse_args()
|
| +
|
| +
|
| +def main():
|
| + program_options = parse_command_line()
|
| + perf = subprocess.Popen(["perf", "script", "-f", "ip,sym,symoff",
|
| + "-i", program_options.perf_filename],
|
| + stdout=subprocess.PIPE)
|
| +
|
| + v8_root_path = os.path.dirname(__file__) + "/../../"
|
| + d8_path = "{}/out/{}.debug/d8".format(v8_root_path, program_options.arch)
|
| + d8_codegen = subprocess.Popen([d8_path, "--ignition",
|
| + "--trace-ignition-codegen", "-e", "1"],
|
| + stdout=subprocess.PIPE)
|
| +
|
| + bytecode_offsets = bytecode_offset_generator(
|
| + perf.stdout, program_options.bytecode_name)
|
| + offset_counts = bytecode_offset_counts(bytecode_offsets)
|
| +
|
| + bytecode_disassembly = bytecode_disassembly_generator(
|
| + d8_codegen.stdout, program_options.bytecode_name)
|
| +
|
| + print_disassembly_annotation(offset_counts, bytecode_disassembly)
|
| +
|
| +
|
| +if __name__ == "__main__":
|
| + main()
|
|
|