OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2016 the V8 project authors. All rights reserved. | 2 # Copyright 2016 the V8 project authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 ''' | 5 ''' |
6 Usage: runtime-call-stats.py [-h] <command> ... | 6 Usage: runtime-call-stats.py [-h] <command> ... |
7 | 7 |
8 Optional arguments: | 8 Optional arguments: |
9 -h, --help show this help message and exit | 9 -h, --help show this help message and exit |
10 | 10 |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
42 elif ' ' in arg: | 42 elif ' ' in arg: |
43 arg = "'{}'".format(arg) | 43 arg = "'{}'".format(arg) |
44 return arg | 44 return arg |
45 print " ".join(map(fix_for_printing, cmd_args)) | 45 print " ".join(map(fix_for_printing, cmd_args)) |
46 | 46 |
47 | 47 |
48 def start_replay_server(args, sites): | 48 def start_replay_server(args, sites): |
49 with tempfile.NamedTemporaryFile(prefix='callstats-inject-', suffix='.js', | 49 with tempfile.NamedTemporaryFile(prefix='callstats-inject-', suffix='.js', |
50 mode='wt', delete=False) as f: | 50 mode='wt', delete=False) as f: |
51 injection = f.name | 51 injection = f.name |
52 generate_injection(f, sites) | 52 generate_injection(f, sites, args.refresh) |
53 cmd_args = [ | 53 cmd_args = [ |
54 args.replay_bin, | 54 args.replay_bin, |
55 "--port=4080", | 55 "--port=4080", |
56 "--ssl_port=4443", | 56 "--ssl_port=4443", |
57 "--no-dns_forwarding", | 57 "--no-dns_forwarding", |
58 "--use_closest_match", | 58 "--use_closest_match", |
59 "--no-diff_unknown_requests", | 59 "--no-diff_unknown_requests", |
60 "--inject_scripts=deterministic.js,{}".format(injection), | 60 "--inject_scripts=deterministic.js,{}".format(injection), |
61 args.replay_wpr, | 61 args.replay_wpr, |
62 ] | 62 ] |
63 print "=" * 80 | 63 print "=" * 80 |
64 print_command(cmd_args) | 64 print_command(cmd_args) |
65 with open(os.devnull, 'w') as null: | 65 with open(os.devnull, 'w') as null: |
66 server = subprocess.Popen(cmd_args, stdout=null, stderr=null) | 66 server = subprocess.Popen(cmd_args, stdout=null, stderr=null) |
67 print "RUNNING REPLAY SERVER: %s with PID=%s" % (args.replay_bin, server.pid) | 67 print "RUNNING REPLAY SERVER: %s with PID=%s" % (args.replay_bin, server.pid) |
68 print "=" * 80 | 68 print "=" * 80 |
69 return {'process': server, 'injection': injection} | 69 return {'process': server, 'injection': injection} |
70 | 70 |
71 | 71 |
72 def stop_replay_server(server): | 72 def stop_replay_server(server): |
73 print("SHUTTING DOWN REPLAY SERVER %s" % server['process'].pid) | 73 print("SHUTTING DOWN REPLAY SERVER %s" % server['process'].pid) |
74 server['process'].terminate() | 74 server['process'].terminate() |
75 os.remove(server['injection']) | 75 os.remove(server['injection']) |
76 | 76 |
77 | 77 |
78 def generate_injection(f, sites): | 78 def generate_injection(f, sites, refreshes=0): |
79 print >> f, """\ | 79 print >> f, """\ |
80 (function() { | 80 (function() { |
| 81 let s = window.sessionStorage.getItem("refreshCounter"); |
| 82 let refreshTotal = """, refreshes, """; |
| 83 let refreshCounter = s ? parseInt(s) : refreshTotal; |
| 84 let refreshId = refreshTotal - refreshCounter; |
| 85 if (refreshCounter > 0) { |
| 86 window.sessionStorage.setItem("refreshCounter", refreshCounter-1); |
| 87 } |
| 88 |
81 function match(url, item) { | 89 function match(url, item) { |
82 if ('regexp' in item) return url.match(item.regexp) !== null; | 90 if ('regexp' in item) return url.match(item.regexp) !== null; |
83 let url_wanted = item.url; | 91 let url_wanted = item.url; |
84 // Allow automatic redirections from http to https. | 92 // Allow automatic redirections from http to https. |
85 if (url_wanted.startsWith("http://") && url.startsWith("https://")) { | 93 if (url_wanted.startsWith("http://") && url.startsWith("https://")) { |
86 url_wanted = "https://" + url_wanted.substr(7); | 94 url_wanted = "https://" + url_wanted.substr(7); |
87 } | 95 } |
88 return url.startsWith(url_wanted); | 96 return url.startsWith(url_wanted); |
89 }; | 97 }; |
90 | 98 |
91 function onLoad(e) { | 99 function onLoad(e) { |
92 let url = e.target.URL; | 100 let url = e.target.URL; |
93 for (let item of sites) { | 101 for (let item of sites) { |
94 if (!match(url, item)) continue; | 102 if (!match(url, item)) continue; |
95 let timeout = 'timeline' in item ? 2500 * item.timeline + 3000 | 103 let timeout = 'timeline' in item ? 2500 * item.timeline |
96 : 'timeout' in item ? 1000 * (item.timeout - 3) | 104 : 'timeout' in item ? 1000 * (item.timeout - 3) |
97 : 10000; | 105 : 10000; |
98 console.log("Setting time out of " + timeout + " for: " + url); | 106 console.log("Setting time out of " + timeout + " for: " + url); |
99 window.setTimeout(function () { | 107 window.setTimeout(function() { |
100 console.log("Time is out for: " + url); | 108 console.log("Time is out for: " + url); |
101 %GetAndResetRuntimeCallStats(1); | 109 let msg = "STATS: (" + refreshId + ") " + url; |
| 110 %GetAndResetRuntimeCallStats(1, msg); |
| 111 if (refreshCounter > 0) { |
| 112 console.log("Refresh counter is " + refreshCounter + ", refreshing: "
+ url); |
| 113 window.location.reload(); |
| 114 } |
102 }, timeout); | 115 }, timeout); |
103 return; | 116 return; |
104 } | 117 } |
105 console.log("Ignoring: " + url); | 118 console.log("Ignoring: " + url); |
106 }; | 119 }; |
107 | 120 |
108 let sites = | 121 let sites = |
109 """, json.dumps(sites), """; | 122 """, json.dumps(sites), """; |
110 | 123 |
111 console.log("Event listenner added for: " + window.location.href); | 124 console.log("Event listenner added for: " + window.location.href); |
112 window.addEventListener("load", onLoad); | 125 window.addEventListener("load", onLoad); |
113 })();""" | 126 })();""" |
114 | 127 |
115 | 128 |
116 def run_site(site, domain, args, timeout=None): | 129 def run_site(site, domain, args, timeout=None): |
117 print "="*80 | 130 print "="*80 |
118 print "RUNNING DOMAIN %s" % domain | 131 print "RUNNING DOMAIN %s" % domain |
119 print "="*80 | 132 print "="*80 |
120 result_template = "{domain}#{count}.txt" if args.repeat else "{domain}.txt" | 133 result_template = "{domain}#{count}.txt" if args.repeat else "{domain}.txt" |
121 count = 0 | 134 count = 0 |
122 if timeout is None: timeout = args.timeout | 135 if timeout is None: timeout = args.timeout |
123 if args.replay_wpr: timeout += 1 | 136 if args.replay_wpr: |
| 137 timeout *= 1 + args.refresh |
| 138 timeout += 1 |
124 while count == 0 or args.repeat is not None and count < args.repeat: | 139 while count == 0 or args.repeat is not None and count < args.repeat: |
125 count += 1 | 140 count += 1 |
126 result = result_template.format(domain=domain, count=count) | 141 result = result_template.format(domain=domain, count=count) |
127 retries = 0 | 142 retries = 0 |
128 while args.retries is None or retries < args.retries: | 143 while args.retries is None or retries < args.retries: |
129 retries += 1 | 144 retries += 1 |
130 try: | 145 try: |
131 if args.user_data_dir: | 146 if args.user_data_dir: |
132 user_data_dir = args.user_data_dir | 147 user_data_dir = args.user_data_dir |
133 else: | 148 else: |
134 user_data_dir = tempfile.mkdtemp(prefix="chr_") | 149 user_data_dir = tempfile.mkdtemp(prefix="chr_") |
135 js_flags = "--runtime-call-stats" | 150 js_flags = "--runtime-call-stats" |
136 if args.replay_wpr: js_flags += " --allow-natives-syntax" | 151 if args.replay_wpr: js_flags += " --allow-natives-syntax" |
137 if args.js_flags: js_flags += " " + args.js_flags | 152 if args.js_flags: js_flags += " " + args.js_flags |
138 chrome_flags = [ | 153 chrome_flags = [ |
139 "--no-default-browser-check", | 154 "--no-default-browser-check", |
140 "--disable-translate", | 155 "--disable-translate", |
141 "--disable-seccomp-sandbox", | |
142 "--js-flags={}".format(js_flags), | 156 "--js-flags={}".format(js_flags), |
143 "--no-first-run", | 157 "--no-first-run", |
144 "--user-data-dir={}".format(user_data_dir), | 158 "--user-data-dir={}".format(user_data_dir), |
145 ] | 159 ] |
146 if args.replay_wpr: | 160 if args.replay_wpr: |
147 chrome_flags += [ | 161 chrome_flags += [ |
148 "--host-resolver-rules=MAP *:80 localhost:4080, " \ | 162 "--host-resolver-rules=MAP *:80 localhost:4080, " \ |
149 "MAP *:443 localhost:4443, " \ | 163 "MAP *:443 localhost:4443, " \ |
150 "EXCLUDE localhost", | 164 "EXCLUDE localhost", |
151 "--ignore-certificate-errors", | 165 "--ignore-certificate-errors", |
| 166 "--disable-seccomp-sandbox", |
152 "--disable-web-security", | 167 "--disable-web-security", |
153 "--reduce-security-for-testing", | 168 "--reduce-security-for-testing", |
154 "--allow-insecure-localhost", | 169 "--allow-insecure-localhost", |
155 ] | 170 ] |
156 else: | 171 else: |
157 chrome_flags += [ | 172 chrome_flags += [ |
158 "--single-process", | 173 "--single-process", |
159 ] | 174 ] |
160 if args.chrome_flags: | 175 if args.chrome_flags: |
161 chrome_flags += args.chrome_flags.split() | 176 chrome_flags += args.chrome_flags.split() |
162 cmd_args = [ | 177 cmd_args = [ |
163 "timeout", str(timeout), | 178 "timeout", str(timeout), |
164 args.with_chrome | 179 args.with_chrome |
165 ] + chrome_flags + [ site ] | 180 ] + chrome_flags + [ site ] |
166 print "- " * 40 | 181 print "- " * 40 |
167 print_command(cmd_args) | 182 print_command(cmd_args) |
168 print "- " * 40 | 183 print "- " * 40 |
169 with open(result, "wt") as f: | 184 with open(result, "wt") as f: |
170 status = subprocess.call(cmd_args, stdout=f) | 185 status = subprocess.call(cmd_args, stdout=f) |
171 # 124 means timeout killed chrome, 0 means the user was bored first! | 186 # 124 means timeout killed chrome, 0 means the user was bored first! |
172 # If none of these two happened, then chrome apparently crashed, so | 187 # If none of these two happened, then chrome apparently crashed, so |
173 # it must be called again. | 188 # it must be called again. |
174 if status != 124 and status != 0: | 189 if status != 124 and status != 0: |
175 print("CHROME CRASHED, REPEATING RUN"); | 190 print("CHROME CRASHED, REPEATING RUN"); |
176 continue | 191 continue |
177 # If the stats file is empty, chrome must be called again. | 192 # If the stats file is empty, chrome must be called again. |
178 if os.path.isfile(result) and os.path.getsize(result) > 0: | 193 if os.path.isfile(result) and os.path.getsize(result) > 0: |
179 if args.print_url: | 194 if args.print_url: |
180 with open(result, "at") as f: | 195 with open(result, "at") as f: |
181 print >> f | 196 print >> f |
182 print >> f, "URL: {}".format(site) | 197 print >> f, "URL: {}".format(site) |
183 break | 198 break |
184 if retries <= 5: timeout += 1 | 199 if retries <= 6: timeout += 2 ** (retries-1) |
185 print("EMPTY RESULT, REPEATING RUN"); | 200 print("EMPTY RESULT, REPEATING RUN"); |
186 finally: | 201 finally: |
187 if not args.user_data_dir: | 202 if not args.user_data_dir: |
188 shutil.rmtree(user_data_dir) | 203 shutil.rmtree(user_data_dir) |
189 | 204 |
190 | 205 |
191 def read_sites_file(args): | 206 def read_sites_file(args): |
192 try: | 207 try: |
193 sites = [] | 208 sites = [] |
194 try: | 209 try: |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
286 # Process the whole file and sum repeating entries. | 301 # Process the whole file and sum repeating entries. |
287 D = { 'Sum': {'time': 0, 'count': 0} } | 302 D = { 'Sum': {'time': 0, 'count': 0} } |
288 for line in f: | 303 for line in f: |
289 line = line.strip() | 304 line = line.strip() |
290 # Discard headers and footers. | 305 # Discard headers and footers. |
291 if not line: continue | 306 if not line: continue |
292 if line.startswith("Runtime Function"): continue | 307 if line.startswith("Runtime Function"): continue |
293 if line.startswith("===="): continue | 308 if line.startswith("===="): continue |
294 if line.startswith("----"): continue | 309 if line.startswith("----"): continue |
295 if line.startswith("URL:"): continue | 310 if line.startswith("URL:"): continue |
| 311 if line.startswith("STATS:"): continue |
296 # We have a regular line. | 312 # We have a regular line. |
297 fields = line.split() | 313 fields = line.split() |
298 key = fields[0] | 314 key = fields[0] |
299 time = float(fields[1].replace("ms", "")) | 315 time = float(fields[1].replace("ms", "")) |
300 count = int(fields[3]) | 316 count = int(fields[3]) |
301 if key not in D: D[key] = { 'time': 0, 'count': 0 } | 317 if key not in D: D[key] = { 'time': 0, 'count': 0 } |
302 D[key]['time'] += time | 318 D[key]['time'] += time |
303 D[key]['count'] += count | 319 D[key]['count'] += count |
304 # We calculate the sum, if it's not the "total" line. | 320 # We calculate the sum, if it's not the "total" line. |
305 if key != "Total": | 321 if key != "Total": |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
393 | 409 |
394 def do_json(args): | 410 def do_json(args): |
395 J = {} | 411 J = {} |
396 for path in args.logdirs: | 412 for path in args.logdirs: |
397 if os.path.isdir(path): | 413 if os.path.isdir(path): |
398 for root, dirs, files in os.walk(path): | 414 for root, dirs, files in os.walk(path): |
399 version = os.path.basename(root) | 415 version = os.path.basename(root) |
400 if version not in J: J[version] = {} | 416 if version not in J: J[version] = {} |
401 for filename in files: | 417 for filename in files: |
402 if filename.endswith(".txt"): | 418 if filename.endswith(".txt"): |
403 m = re.match(r'^([^#]+)(#.*)?$', filename) | 419 m = re.match(r'^([^#]+)(#.*)?\.txt$', filename) |
404 domain = m.group(1) | 420 domain = m.group(1) |
405 if domain not in J[version]: J[version][domain] = {} | 421 if domain not in J[version]: J[version][domain] = {} |
406 read_stats(os.path.join(root, filename), J[version][domain]) | 422 read_stats(os.path.join(root, filename), J[version][domain]) |
407 for version, T in J.items(): | 423 for version, T in J.items(): |
408 for domain, S in T.items(): | 424 for domain, S in T.items(): |
409 A = [] | 425 A = [] |
410 for name, value in S.items(): | 426 for name, value in S.items(): |
411 # We don't want the calculated sum in the JSON file. | 427 # We don't want the calculated sum in the JSON file. |
412 if name == "Sum": continue | 428 if name == "Sum": continue |
413 entry = [name] | 429 entry = [name] |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
455 subparsers["run"].add_argument( | 471 subparsers["run"].add_argument( |
456 "--js-flags", type=str, default="", | 472 "--js-flags", type=str, default="", |
457 help="specify additional V8 flags") | 473 help="specify additional V8 flags") |
458 subparsers["run"].add_argument( | 474 subparsers["run"].add_argument( |
459 "--no-url", dest="print_url", action="store_false", default=True, | 475 "--no-url", dest="print_url", action="store_false", default=True, |
460 help="do not include url in statistics file") | 476 help="do not include url in statistics file") |
461 subparsers["run"].add_argument( | 477 subparsers["run"].add_argument( |
462 "-n", "--repeat", type=int, metavar="<num>", | 478 "-n", "--repeat", type=int, metavar="<num>", |
463 help="specify iterations for each website (default: once)") | 479 help="specify iterations for each website (default: once)") |
464 subparsers["run"].add_argument( | 480 subparsers["run"].add_argument( |
| 481 "-k", "--refresh", type=int, metavar="<num>", default=0, |
| 482 help="specify refreshes for each iteration (default: 0)") |
| 483 subparsers["run"].add_argument( |
465 "--replay-wpr", type=str, metavar="<path>", | 484 "--replay-wpr", type=str, metavar="<path>", |
466 help="use the specified web page replay (.wpr) archive") | 485 help="use the specified web page replay (.wpr) archive") |
467 subparsers["run"].add_argument( | 486 subparsers["run"].add_argument( |
468 "--replay-bin", type=str, metavar="<path>", | 487 "--replay-bin", type=str, metavar="<path>", |
469 help="specify the replay.py script typically located in " \ | 488 help="specify the replay.py script typically located in " \ |
470 "$CHROMIUM/src/third_party/webpagereplay/replay.py") | 489 "$CHROMIUM/src/third_party/webpagereplay/replay.py") |
471 subparsers["run"].add_argument( | 490 subparsers["run"].add_argument( |
472 "-r", "--retries", type=int, metavar="<num>", | 491 "-r", "--retries", type=int, metavar="<num>", |
473 help="specify retries if website is down (default: forever)") | 492 help="specify retries if website is down (default: forever)") |
474 subparsers["run"].add_argument( | 493 subparsers["run"].add_argument( |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
528 args.error("use either option --sites-file or site URLs") | 547 args.error("use either option --sites-file or site URLs") |
529 sys.exit(1) | 548 sys.exit(1) |
530 elif args.command == "run" and not coexist(args.replay_wpr, args.replay_bin): | 549 elif args.command == "run" and not coexist(args.replay_wpr, args.replay_bin): |
531 args.error("options --replay-wpr and --replay-bin must be used together") | 550 args.error("options --replay-wpr and --replay-bin must be used together") |
532 sys.exit(1) | 551 sys.exit(1) |
533 else: | 552 else: |
534 args.func(args) | 553 args.func(args) |
535 | 554 |
536 if __name__ == "__main__": | 555 if __name__ == "__main__": |
537 sys.exit(main()) | 556 sys.exit(main()) |
OLD | NEW |