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 and |
| 111 current_chain[-1] == "[unknown]"): |
| 112 continue |
| 113 |
104 current_chain.append(symbol) | 114 current_chain.append(symbol) |
105 | 115 |
106 if symbol.startswith("BytecodeHandler:"): | 116 if symbol.startswith("BytecodeHandler:"): |
| 117 current_chain.append("[interpreter]") |
107 yield current_chain | 118 yield current_chain |
108 skip_until_end_of_chain = True | 119 skip_until_end_of_chain = True |
| 120 elif JIT_CODE_SYMBOLS_RE.match(symbol): |
| 121 if not hide_jit: |
| 122 current_chain.append("[jit]") |
| 123 yield current_chain |
| 124 skip_until_end_of_chain = True |
109 elif symbol == "Stub:CEntryStub" and compiler_symbol_in_chain: | 125 elif symbol == "Stub:CEntryStub" and compiler_symbol_in_chain: |
110 if show_all: | 126 if not hide_compiler: |
111 current_chain[-1] = "[compiler]" | 127 current_chain.append("[compiler]") |
112 yield current_chain | 128 yield current_chain |
113 skip_until_end_of_chain = True | 129 skip_until_end_of_chain = True |
114 elif COMPILER_SYMBOLS_RE.match(symbol): | 130 elif COMPILER_SYMBOLS_RE.match(symbol): |
115 compiler_symbol_in_chain = True | 131 compiler_symbol_in_chain = True |
116 elif symbol == "Builtin:InterpreterEntryTrampoline": | 132 elif symbol == "Builtin:InterpreterEntryTrampoline": |
117 if len(current_chain) == 1: | 133 if len(current_chain) == 1: |
118 yield ["[entry trampoline]"] | 134 yield ["[entry trampoline]"] |
119 else: | 135 else: |
120 # If we see an InterpreterEntryTrampoline which is not at the top of the | 136 # 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 | 137 # 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", | 190 default="perf.data", |
175 metavar="<perf filename>" | 191 metavar="<perf filename>" |
176 ) | 192 ) |
177 command_line_parser.add_argument( | 193 command_line_parser.add_argument( |
178 "--flamegraph", "-f", | 194 "--flamegraph", "-f", |
179 help="output an input file for flamegraph.pl, not a report", | 195 help="output an input file for flamegraph.pl, not a report", |
180 action="store_true", | 196 action="store_true", |
181 dest="output_flamegraph" | 197 dest="output_flamegraph" |
182 ) | 198 ) |
183 command_line_parser.add_argument( | 199 command_line_parser.add_argument( |
184 "--show-all", "-a", | 200 "--hide-other", |
185 help="show samples outside Ignition bytecode handlers", | 201 help="Hide other samples", |
186 action="store_true" | 202 action="store_true" |
187 ) | 203 ) |
188 command_line_parser.add_argument( | 204 command_line_parser.add_argument( |
| 205 "--hide-compiler", |
| 206 help="Hide samples during compilation", |
| 207 action="store_true" |
| 208 ) |
| 209 command_line_parser.add_argument( |
| 210 "--hide-jit", |
| 211 help="Hide samples from JIT code execution", |
| 212 action="store_true" |
| 213 ) |
| 214 command_line_parser.add_argument( |
189 "--show-full-signatures", "-s", | 215 "--show-full-signatures", "-s", |
190 help="show full signatures instead of function names", | 216 help="show full signatures instead of function names", |
191 action="store_true" | 217 action="store_true" |
192 ) | 218 ) |
193 command_line_parser.add_argument( | 219 command_line_parser.add_argument( |
194 "--output", "-o", | 220 "--output", "-o", |
195 help="output file name (stdout if omitted)", | 221 help="output file name (stdout if omitted)", |
196 type=argparse.FileType('wt'), | 222 type=argparse.FileType('wt'), |
197 default=sys.stdout, | 223 default=sys.stdout, |
198 metavar="<output filename>", | 224 metavar="<output filename>", |
199 dest="output_stream" | 225 dest="output_stream" |
200 ) | 226 ) |
201 | 227 |
202 return command_line_parser.parse_args() | 228 return command_line_parser.parse_args() |
203 | 229 |
204 | 230 |
205 def main(): | 231 def main(): |
206 program_options = parse_command_line() | 232 program_options = parse_command_line() |
207 | 233 |
208 perf = subprocess.Popen(["perf", "script", "--fields", "ip,sym", | 234 perf = subprocess.Popen(["perf", "script", "--fields", "ip,sym", |
209 "-i", program_options.perf_filename], | 235 "-i", program_options.perf_filename], |
210 stdout=subprocess.PIPE) | 236 stdout=subprocess.PIPE) |
211 | 237 |
212 callchains = collapsed_callchains_generator( | 238 callchains = collapsed_callchains_generator( |
213 perf.stdout, program_options.show_all, | 239 perf.stdout, program_options.hide_other, program_options.hide_compiler, |
214 program_options.show_full_signatures) | 240 program_options.hide_jit, program_options.show_full_signatures) |
215 | 241 |
216 if program_options.output_flamegraph: | 242 if program_options.output_flamegraph: |
217 write_flamegraph_input_file(program_options.output_stream, callchains) | 243 write_flamegraph_input_file(program_options.output_stream, callchains) |
218 else: | 244 else: |
219 write_handlers_report(program_options.output_stream, callchains) | 245 write_handlers_report(program_options.output_stream, callchains) |
220 | 246 |
221 | 247 |
222 if __name__ == "__main__": | 248 if __name__ == "__main__": |
223 main() | 249 main() |
OLD | NEW |