Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #! /usr/bin/python2 | |
| 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 | |
| 8 import argparse | |
| 9 import collections | |
| 10 import re | |
| 11 import subprocess | |
| 12 import sys | |
| 13 | |
| 14 | |
| 15 DESCRIPTION = """ | |
| 16 Processes a perf.data sample file and reports the hottest Ignition bytecodes, | |
| 17 or write an input file for flamegraph.pl. | |
| 18 """ | |
| 19 | |
| 20 | |
| 21 EPILOGUE = """ | |
|
rmcilroy
2016/03/11 11:26:49
HELP_EPILOGUE. Also make these all private by addi
Stefano Sanfilippo
2016/03/11 16:33:51
Done.
rmcilroy
2016/03/14 12:26:52
You never made the fields private.
Stefano Sanfilippo
2016/03/14 13:14:20
Wooops, sorry about that. Done for the help string
rmcilroy
2016/03/14 14:48:00
This is fine, thanks.
| |
| 22 examples: | |
| 23 # Get a flamegraph for Ignition bytecode handlers on Octane benchmark, | |
| 24 # without considering time spent compiling JS code. | |
| 25 # | |
| 26 $ tools/run-perf.sh out/x64.release/d8 --ignition octane/run.js | |
| 27 $ tools/ignition_perf_report.py --flamegraph --hide-compile -o out.collapsed | |
| 28 $ flamegraph.pl --colors js out.collapsed > out.svg | |
| 29 | |
| 30 # See the hottest bytecodes on Octane benchmark, by number of samples. | |
| 31 # | |
| 32 $ tools/run-perf.sh out/x64.release/d8 --ignition octane/run.js | |
| 33 $ tools/ignition_perf_report.py | |
| 34 """ | |
| 35 | |
| 36 | |
| 37 COMPILER_SYMBOLS_RE = re.compile(r"Builtin:Compile(?:Lazy|OptimizedConcurrent)" | |
| 38 "$|LazyCompile:|v8::internal::Compile") | |
| 39 | |
| 40 | |
| 41 # Function name, strip parameters | |
| 42 SYMBOL_NAME_RE = re.compile(r"((?:\(anonymous namespace\)|[^(])+)") | |
| 43 | |
| 44 | |
| 45 def yield_collapsed_callchains(perf_stream, hide_compile_time=False): | |
|
rmcilroy
2016/03/11 11:26:48
Probably better named collapsed_callchains_generat
Stefano Sanfilippo
2016/03/11 16:33:51
Done.
| |
| 46 current_chain = [] | |
| 47 keep_parsing_chain = True | |
| 48 for line in perf_stream: | |
| 49 if line[0] == "#": | |
|
rmcilroy
2016/03/11 11:26:49
Add some comments (e.g., # Skip comments, # Empty
Stefano Sanfilippo
2016/03/11 16:33:51
Done.
| |
| 50 continue | |
| 51 line = line.strip() | |
| 52 if not line: | |
| 53 keep_parsing_chain = True | |
| 54 current_chain = [] | |
| 55 continue | |
| 56 if not keep_parsing_chain: | |
| 57 continue | |
| 58 symbol = SYMBOL_NAME_RE.match(line.split(" ", 1)[1]).group(1) | |
|
rmcilroy
2016/03/11 11:26:49
nit newline above
Stefano Sanfilippo
2016/03/11 16:33:51
Done.
| |
| 59 current_chain.append(symbol) | |
| 60 if hide_compile_time and COMPILER_SYMBOLS_RE.match(symbol): | |
| 61 keep_parsing_chain = False | |
| 62 elif symbol.startswith("BytecodeHandler:"): | |
| 63 keep_parsing_chain = False | |
| 64 yield current_chain | |
| 65 | |
| 66 | |
| 67 def count_callchains(callchains): | |
|
rmcilroy
2016/03/11 11:26:49
get_callchain_sample_counts ?
Stefano Sanfilippo
2016/03/11 16:33:51
I think calculate_* better conveys the idea that t
| |
| 68 chain_counters = collections.defaultdict(int) | |
| 69 for callchain in callchains: | |
| 70 key = ";".join(reversed(callchain)) | |
| 71 chain_counters[key] += 1 | |
| 72 return chain_counters.items() | |
| 73 | |
| 74 | |
| 75 def count_handler_samples(callchains): | |
|
rmcilroy
2016/03/11 11:26:49
get_bytecode_handler_sample_counts ?
Stefano Sanfilippo
2016/03/11 16:33:51
Same as above.
| |
| 76 handler_counters = collections.defaultdict(int) | |
| 77 for callchain in callchains: | |
| 78 # Strip the "BytecodeHandler:" prefix | |
| 79 handler = callchain[-1].split(":", 1)[1] | |
| 80 handler_counters[handler] += 1 | |
| 81 # Sort by decreasing number of samples | |
| 82 return sorted(handler_counters.items(), | |
|
rmcilroy
2016/03/11 11:26:49
Maybe just do the sort in main below, to keep thes
Stefano Sanfilippo
2016/03/11 16:33:51
Done.
| |
| 83 key=lambda entry: entry[1], reverse=True) | |
| 84 | |
| 85 | |
| 86 def parse_command_line(): | |
| 87 command_line_parser = argparse.ArgumentParser( | |
| 88 formatter_class=argparse.RawDescriptionHelpFormatter, | |
| 89 description=DESCRIPTION, | |
| 90 epilog=EPILOGUE) | |
| 91 | |
| 92 command_line_parser.add_argument( | |
| 93 "perf_filename", | |
| 94 help="perf sample file to process (default: perf.data)", | |
| 95 nargs="?", | |
| 96 default="perf.data", | |
| 97 metavar="<perf filename>") | |
| 98 command_line_parser.add_argument( | |
| 99 "--flamegraph", "-f", | |
| 100 help="output an input file for flamegraph.pl, not a report", | |
| 101 action="store_true", | |
| 102 dest="output_flamegraph") | |
| 103 command_line_parser.add_argument( | |
| 104 "--hide-compile", "-c", | |
| 105 help="do not count samples inside compiler routines", | |
| 106 action="store_true", | |
| 107 dest="hide_compile_time") | |
| 108 command_line_parser.add_argument( | |
| 109 "--output", "-o", | |
| 110 help="output file name (stdout if omitted)", | |
| 111 type=argparse.FileType('wt'), | |
| 112 default=sys.stdout, | |
| 113 metavar="<output filename>", | |
| 114 dest="output_stream") | |
| 115 | |
| 116 return command_line_parser.parse_args() | |
| 117 | |
| 118 | |
| 119 def main(): | |
| 120 program_options = parse_command_line() | |
| 121 output_stream = program_options.output_stream | |
| 122 perf = subprocess.Popen(["perf", "script", "-f", "ip,sym", | |
| 123 "-i", program_options.perf_filename], | |
| 124 stdout=subprocess.PIPE) | |
| 125 callchains = yield_collapsed_callchains(perf.stdout, | |
| 126 program_options.hide_compile_time) | |
| 127 if program_options.output_flamegraph: | |
| 128 for callchain, count in count_callchains(callchains): | |
| 129 output_stream.write("{} {}\n".format(callchain, count)) | |
|
rmcilroy
2016/03/11 11:26:49
nit - move to a seperate function (for clarity) an
Stefano Sanfilippo
2016/03/11 16:33:51
Done.
| |
| 130 else: | |
| 131 handler_counters = count_handler_samples(callchains) | |
| 132 samples_num = sum(counter for _, counter in handler_counters) | |
| 133 for bytecode_name, count in handler_counters: | |
| 134 output_stream.write( | |
| 135 "{}\t{}\t{:.3f}%\n".format(bytecode_name, count, | |
| 136 100. * count / samples_num)) | |
| 137 | |
| 138 | |
| 139 if __name__ == "__main__": | |
| 140 main() | |
| OLD | NEW |