Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #! /usr/bin/python2 | 1 #! /usr/bin/python2 |
| 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 collections | 9 import collections |
| 10 import re | 10 import re |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 45 # See the hottest bytecodes on Octane benchmark, by number of samples. | 45 # See the hottest bytecodes on Octane benchmark, by number of samples. |
| 46 # | 46 # |
| 47 $ tools/run-perf.sh out/x64.release/d8 \\ | 47 $ tools/run-perf.sh out/x64.release/d8 \\ |
| 48 --ignition --noturbo --nocrankshaft octane/run.js | 48 --ignition --noturbo --nocrankshaft octane/run.js |
| 49 $ tools/ignition/linux_perf_report.py | 49 $ tools/ignition/linux_perf_report.py |
| 50 """ | 50 """ |
| 51 | 51 |
| 52 | 52 |
| 53 COMPILER_SYMBOLS_RE = re.compile( | 53 COMPILER_SYMBOLS_RE = re.compile( |
| 54 r"v8::internal::(?:\(anonymous namespace\)::)?Compile|v8::internal::Parser") | 54 r"v8::internal::(?:\(anonymous namespace\)::)?Compile|v8::internal::Parser") |
| 55 JIT_CODE_SYMBOLS_RE = re.compile( | |
| 56 r"(LazyCompile|Compile|Eval|Script):(\*|~)") | |
| 55 | 57 |
| 56 | 58 |
| 57 def strip_function_parameters(symbol): | 59 def strip_function_parameters(symbol): |
| 58 if symbol[-1] != ')': return symbol | 60 if symbol[-1] != ')': return symbol |
| 59 pos = 1 | 61 pos = 1 |
| 60 parenthesis_count = 0 | 62 parenthesis_count = 0 |
| 61 for c in reversed(symbol): | 63 for c in reversed(symbol): |
| 62 if c == ')': | 64 if c == ')': |
| 63 parenthesis_count += 1 | 65 parenthesis_count += 1 |
| 64 elif c == '(': | 66 elif c == '(': |
| 65 parenthesis_count -= 1 | 67 parenthesis_count -= 1 |
| 66 if parenthesis_count == 0: | 68 if parenthesis_count == 0: |
| 67 break | 69 break |
| 68 else: | 70 else: |
| 69 pos += 1 | 71 pos += 1 |
| 70 return symbol[:-pos] | 72 return symbol[:-pos] |
| 71 | 73 |
| 72 | 74 |
| 73 def collapsed_callchains_generator(perf_stream, show_all=False, | 75 def collapsed_callchains_generator(perf_stream, hide_other=False, |
| 76 hide_compiler=False, hide_jit=False, | |
| 74 show_full_signatures=False): | 77 show_full_signatures=False): |
| 75 current_chain = [] | 78 current_chain = [] |
| 76 skip_until_end_of_chain = False | 79 skip_until_end_of_chain = False |
| 77 compiler_symbol_in_chain = False | 80 compiler_symbol_in_chain = False |
| 78 | 81 |
| 79 for line in perf_stream: | 82 for line in perf_stream: |
| 80 # Lines starting with a "#" are comments, skip them. | 83 # Lines starting with a "#" are comments, skip them. |
| 81 if line[0] == "#": | 84 if line[0] == "#": |
| 82 continue | 85 continue |
| 83 | 86 |
| 84 line = line.strip() | 87 line = line.strip() |
| 85 | 88 |
| 86 # Empty line signals the end of the callchain. | 89 # Empty line signals the end of the callchain. |
| 87 if not line: | 90 if not line: |
| 88 if not skip_until_end_of_chain and current_chain and show_all: | 91 if (not skip_until_end_of_chain and current_chain |
| 92 and not hide_other): | |
| 89 current_chain.append("[other]") | 93 current_chain.append("[other]") |
| 90 yield current_chain | 94 yield current_chain |
| 91 # Reset parser status. | 95 # Reset parser status. |
| 92 current_chain = [] | 96 current_chain = [] |
| 93 skip_until_end_of_chain = False | 97 skip_until_end_of_chain = False |
| 94 compiler_symbol_in_chain = False | 98 compiler_symbol_in_chain = False |
| 95 continue | 99 continue |
| 96 | 100 |
| 97 if skip_until_end_of_chain: | 101 if skip_until_end_of_chain: |
| 98 continue | 102 continue |
| 99 | 103 |
| 100 # Trim the leading address and the trailing +offset, if present. | 104 # Trim the leading address and the trailing +offset, if present. |
| 101 symbol = line.split(" ", 1)[1].split("+", 1)[0] | 105 symbol = line.split(" ", 1)[1].split("+", 1)[0] |
| 102 if not show_full_signatures: | 106 if not show_full_signatures: |
| 103 symbol = strip_function_parameters(symbol) | 107 symbol = strip_function_parameters(symbol) |
| 108 | |
| 109 # Avoid chains of [unknown] | |
| 110 if symbol == "[unknown]" and current_chain[-1] == "[unknown]": | |
|
mythria
2016/11/08 09:47:07
It is possible that symbol is unknown and the curr
| |
| 111 continue | |
| 112 | |
| 104 current_chain.append(symbol) | 113 current_chain.append(symbol) |
| 105 | 114 |
| 106 if symbol.startswith("BytecodeHandler:"): | 115 if symbol.startswith("BytecodeHandler:"): |
| 116 current_chain.append("[interpreter]") | |
| 107 yield current_chain | 117 yield current_chain |
| 108 skip_until_end_of_chain = True | 118 skip_until_end_of_chain = True |
| 119 elif JIT_CODE_SYMBOLS_RE.match(symbol): | |
| 120 if not hide_jit: | |
| 121 current_chain.append("[jit]") | |
| 122 yield current_chain | |
| 123 skip_until_end_of_chain = True | |
| 109 elif symbol == "Stub:CEntryStub" and compiler_symbol_in_chain: | 124 elif symbol == "Stub:CEntryStub" and compiler_symbol_in_chain: |
| 110 if show_all: | 125 if not hide_compiler: |
| 111 current_chain[-1] = "[compiler]" | 126 current_chain.append("[compiler]") |
| 112 yield current_chain | 127 yield current_chain |
| 113 skip_until_end_of_chain = True | 128 skip_until_end_of_chain = True |
| 114 elif COMPILER_SYMBOLS_RE.match(symbol): | 129 elif COMPILER_SYMBOLS_RE.match(symbol): |
| 115 compiler_symbol_in_chain = True | 130 compiler_symbol_in_chain = True |
| 116 elif symbol == "Builtin:InterpreterEntryTrampoline": | 131 elif symbol == "Builtin:InterpreterEntryTrampoline": |
| 117 if len(current_chain) == 1: | 132 if len(current_chain) == 1: |
| 118 yield ["[entry trampoline]"] | 133 yield ["[entry trampoline]"] |
| 119 else: | 134 else: |
| 120 # If we see an InterpreterEntryTrampoline which is not at the top of the | 135 # If we see an InterpreterEntryTrampoline which is not at the top of the |
| 121 # chain and doesn't have a BytecodeHandler above it, then we have | 136 # chain and doesn't have a BytecodeHandler above it, then we have |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 174 default="perf.data", | 189 default="perf.data", |
| 175 metavar="<perf filename>" | 190 metavar="<perf filename>" |
| 176 ) | 191 ) |
| 177 command_line_parser.add_argument( | 192 command_line_parser.add_argument( |
| 178 "--flamegraph", "-f", | 193 "--flamegraph", "-f", |
| 179 help="output an input file for flamegraph.pl, not a report", | 194 help="output an input file for flamegraph.pl, not a report", |
| 180 action="store_true", | 195 action="store_true", |
| 181 dest="output_flamegraph" | 196 dest="output_flamegraph" |
| 182 ) | 197 ) |
| 183 command_line_parser.add_argument( | 198 command_line_parser.add_argument( |
| 184 "--show-all", "-a", | 199 "--hide-other", |
| 185 help="show samples outside Ignition bytecode handlers", | 200 help="Hide other samples", |
| 186 action="store_true" | 201 action="store_true" |
| 187 ) | 202 ) |
| 188 command_line_parser.add_argument( | 203 command_line_parser.add_argument( |
| 204 "--hide-compiler", | |
| 205 help="Hide samples during compilation", | |
| 206 action="store_true" | |
| 207 ) | |
| 208 command_line_parser.add_argument( | |
| 209 "--hide-jit", | |
| 210 help="Hide samples from JIT code execution", | |
| 211 action="store_true" | |
| 212 ) | |
| 213 command_line_parser.add_argument( | |
| 189 "--show-full-signatures", "-s", | 214 "--show-full-signatures", "-s", |
| 190 help="show full signatures instead of function names", | 215 help="show full signatures instead of function names", |
| 191 action="store_true" | 216 action="store_true" |
| 192 ) | 217 ) |
| 193 command_line_parser.add_argument( | 218 command_line_parser.add_argument( |
| 194 "--output", "-o", | 219 "--output", "-o", |
| 195 help="output file name (stdout if omitted)", | 220 help="output file name (stdout if omitted)", |
| 196 type=argparse.FileType('wt'), | 221 type=argparse.FileType('wt'), |
| 197 default=sys.stdout, | 222 default=sys.stdout, |
| 198 metavar="<output filename>", | 223 metavar="<output filename>", |
| 199 dest="output_stream" | 224 dest="output_stream" |
| 200 ) | 225 ) |
| 201 | 226 |
| 202 return command_line_parser.parse_args() | 227 return command_line_parser.parse_args() |
| 203 | 228 |
| 204 | 229 |
| 205 def main(): | 230 def main(): |
| 206 program_options = parse_command_line() | 231 program_options = parse_command_line() |
| 207 | 232 |
| 208 perf = subprocess.Popen(["perf", "script", "--fields", "ip,sym", | 233 perf = subprocess.Popen(["perf", "script", "--fields", "ip,sym", |
| 209 "-i", program_options.perf_filename], | 234 "-i", program_options.perf_filename], |
| 210 stdout=subprocess.PIPE) | 235 stdout=subprocess.PIPE) |
| 211 | 236 |
| 212 callchains = collapsed_callchains_generator( | 237 callchains = collapsed_callchains_generator( |
| 213 perf.stdout, program_options.show_all, | 238 perf.stdout, program_options.hide_other, program_options.hide_compiler, |
| 214 program_options.show_full_signatures) | 239 program_options.hide_jit, program_options.show_full_signatures) |
| 215 | 240 |
| 216 if program_options.output_flamegraph: | 241 if program_options.output_flamegraph: |
| 217 write_flamegraph_input_file(program_options.output_stream, callchains) | 242 write_flamegraph_input_file(program_options.output_stream, callchains) |
| 218 else: | 243 else: |
| 219 write_handlers_report(program_options.output_stream, callchains) | 244 write_handlers_report(program_options.output_stream, callchains) |
| 220 | 245 |
| 221 | 246 |
| 222 if __name__ == "__main__": | 247 if __name__ == "__main__": |
| 223 main() | 248 main() |
| OLD | NEW |