| 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: callstats.py [-h] <command> ... | 6 Usage: callstats.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 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 116 }, timeout); | 116 }, timeout); |
| 117 return; | 117 return; |
| 118 } | 118 } |
| 119 console.log("Ignoring: " + url); | 119 console.log("Ignoring: " + url); |
| 120 }; | 120 }; |
| 121 var sites = | 121 var sites = |
| 122 """, json.dumps(sites), """; | 122 """, json.dumps(sites), """; |
| 123 onLoad(window.location.href); | 123 onLoad(window.location.href); |
| 124 })();""" | 124 })();""" |
| 125 | 125 |
| 126 def get_chrome_flags(js_flags, user_data_dir): |
| 127 return [ |
| 128 "--no-default-browser-check", |
| 129 "--no-sandbox", |
| 130 "--disable-translate", |
| 131 "--enable-benchmarking", |
| 132 "--js-flags={}".format(js_flags), |
| 133 "--no-first-run", |
| 134 "--user-data-dir={}".format(user_data_dir), |
| 135 ] |
| 136 |
| 137 def get_chrome_replay_flags(args): |
| 138 http_port = 4080 + args.port_offset |
| 139 https_port = 4443 + args.port_offset |
| 140 return [ |
| 141 "--host-resolver-rules=MAP *:80 localhost:%s, " \ |
| 142 "MAP *:443 localhost:%s, " \ |
| 143 "EXCLUDE localhost" % ( |
| 144 http_port, https_port), |
| 145 "--ignore-certificate-errors", |
| 146 "--disable-seccomp-sandbox", |
| 147 "--disable-web-security", |
| 148 "--reduce-security-for-testing", |
| 149 "--allow-insecure-localhost", |
| 150 ] |
| 126 | 151 |
| 127 def run_site(site, domain, args, timeout=None): | 152 def run_site(site, domain, args, timeout=None): |
| 128 print "="*80 | 153 print "="*80 |
| 129 print "RUNNING DOMAIN %s" % domain | 154 print "RUNNING DOMAIN %s" % domain |
| 130 print "="*80 | 155 print "="*80 |
| 131 result_template = "{domain}#{count}.txt" if args.repeat else "{domain}.txt" | 156 result_template = "{domain}#{count}.txt" if args.repeat else "{domain}.txt" |
| 132 count = 0 | 157 count = 0 |
| 133 if timeout is None: timeout = args.timeout | 158 if timeout is None: timeout = args.timeout |
| 134 if args.replay_wpr: | 159 if args.replay_wpr: |
| 135 timeout *= 1 + args.refresh | 160 timeout *= 1 + args.refresh |
| 136 timeout += 1 | 161 timeout += 1 |
| 137 retries_since_good_run = 0 | 162 retries_since_good_run = 0 |
| 138 while count == 0 or args.repeat is not None and count < args.repeat: | 163 while count == 0 or args.repeat is not None and count < args.repeat: |
| 139 count += 1 | 164 count += 1 |
| 140 result = result_template.format(domain=domain, count=count) | 165 result = result_template.format(domain=domain, count=count) |
| 141 retries = 0 | 166 retries = 0 |
| 142 while args.retries is None or retries < args.retries: | 167 while args.retries is None or retries < args.retries: |
| 143 retries += 1 | 168 retries += 1 |
| 144 try: | 169 try: |
| 145 if args.user_data_dir: | 170 if args.user_data_dir: |
| 146 user_data_dir = args.user_data_dir | 171 user_data_dir = args.user_data_dir |
| 147 else: | 172 else: |
| 148 user_data_dir = tempfile.mkdtemp(prefix="chr_") | 173 user_data_dir = tempfile.mkdtemp(prefix="chr_") |
| 149 js_flags = "--runtime-call-stats" | 174 js_flags = "--runtime-call-stats" |
| 150 if args.replay_wpr: js_flags += " --allow-natives-syntax" | 175 if args.replay_wpr: js_flags += " --allow-natives-syntax" |
| 151 if args.js_flags: js_flags += " " + args.js_flags | 176 if args.js_flags: js_flags += " " + args.js_flags |
| 152 chrome_flags = [ | 177 chrome_flags = get_chrome_flags(js_flags, user_data_dir) |
| 153 "--no-default-browser-check", | |
| 154 "--no-sandbox", | |
| 155 "--disable-translate", | |
| 156 "--enable-benchmarking", | |
| 157 "--js-flags={}".format(js_flags), | |
| 158 "--no-first-run", | |
| 159 "--user-data-dir={}".format(user_data_dir), | |
| 160 ] | |
| 161 if args.replay_wpr: | 178 if args.replay_wpr: |
| 162 http_port = 4080 + args.port_offset | 179 chrome_flags += get_chrome_replay_flags(args) |
| 163 https_port = 4443 + args.port_offset | |
| 164 chrome_flags += [ | |
| 165 "--host-resolver-rules=MAP *:80 localhost:%s, " \ | |
| 166 "MAP *:443 localhost:%s, " \ | |
| 167 "EXCLUDE localhost" % ( | |
| 168 http_port, https_port), | |
| 169 "--ignore-certificate-errors", | |
| 170 "--disable-seccomp-sandbox", | |
| 171 "--disable-web-security", | |
| 172 "--reduce-security-for-testing", | |
| 173 "--allow-insecure-localhost", | |
| 174 ] | |
| 175 else: | 180 else: |
| 176 chrome_flags += [ | 181 chrome_flags += [ "--single-process", ] |
| 177 "--single-process", | |
| 178 ] | |
| 179 if args.chrome_flags: | 182 if args.chrome_flags: |
| 180 chrome_flags += args.chrome_flags.split() | 183 chrome_flags += args.chrome_flags.split() |
| 181 cmd_args = [ | 184 cmd_args = [ |
| 182 "timeout", str(timeout), | 185 "timeout", str(timeout), |
| 183 args.with_chrome | 186 args.with_chrome |
| 184 ] + chrome_flags + [ site ] | 187 ] + chrome_flags + [ site ] |
| 185 print "- " * 40 | 188 print "- " * 40 |
| 186 print_command(cmd_args) | 189 print_command(cmd_args) |
| 187 print "- " * 40 | 190 print "- " * 40 |
| 188 with open(result, "wt") as f: | 191 with open(result, "wt") as f: |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 228 for line in f: | 231 for line in f: |
| 229 line = line.strip() | 232 line = line.strip() |
| 230 if not line or line.startswith('#'): continue | 233 if not line or line.startswith('#'): continue |
| 231 sites.append({'url': line, 'timeout': args.timeout}) | 234 sites.append({'url': line, 'timeout': args.timeout}) |
| 232 return sites | 235 return sites |
| 233 except IOError as e: | 236 except IOError as e: |
| 234 args.error("Cannot read from {}. {}.".format(args.sites_file, e.strerror)) | 237 args.error("Cannot read from {}. {}.".format(args.sites_file, e.strerror)) |
| 235 sys.exit(1) | 238 sys.exit(1) |
| 236 | 239 |
| 237 | 240 |
| 238 def do_run(args): | 241 def read_sites(args): |
| 239 # Determine the websites to benchmark. | 242 # Determine the websites to benchmark. |
| 240 if args.sites_file: | 243 if args.sites_file: |
| 241 sites = read_sites_file(args) | 244 return read_sites_file(args) |
| 242 else: | 245 return [{'url': site, 'timeout': args.timeout} for site in args.sites] |
| 243 sites = [{'url': site, 'timeout': args.timeout} for site in args.sites] | 246 |
| 247 def do_run(args): |
| 248 sites = read_sites(args) |
| 249 replay_server = start_replay_server(args, sites) if args.replay_wpr else None |
| 244 # Disambiguate domains, if needed. | 250 # Disambiguate domains, if needed. |
| 245 L = [] | 251 L = [] |
| 246 domains = {} | 252 domains = {} |
| 247 for item in sites: | 253 for item in sites: |
| 248 site = item['url'] | 254 site = item['url'] |
| 249 domain = None | 255 domain = None |
| 250 if args.domain: | 256 if args.domain: |
| 251 domain = args.domain | 257 domain = args.domain |
| 252 elif 'domain' in item: | 258 elif 'domain' in item: |
| 253 domain = item['domain'] | 259 domain = item['domain'] |
| 254 else: | 260 else: |
| 255 m = re.match(r'^(https?://)?([^/]+)(/.*)?$', site) | 261 m = re.match(r'^(https?://)?([^/]+)(/.*)?$', site) |
| 256 if not m: | 262 if not m: |
| 257 args.error("Invalid URL {}.".format(site)) | 263 args.error("Invalid URL {}.".format(site)) |
| 258 continue | 264 continue |
| 259 domain = m.group(2) | 265 domain = m.group(2) |
| 260 entry = [site, domain, None, item['timeout']] | 266 entry = [site, domain, None, item['timeout']] |
| 261 if domain not in domains: | 267 if domain not in domains: |
| 262 domains[domain] = entry | 268 domains[domain] = entry |
| 263 else: | 269 else: |
| 264 if not isinstance(domains[domain], int): | 270 if not isinstance(domains[domain], int): |
| 265 domains[domain][2] = 1 | 271 domains[domain][2] = 1 |
| 266 domains[domain] = 1 | 272 domains[domain] = 1 |
| 267 domains[domain] += 1 | 273 domains[domain] += 1 |
| 268 entry[2] = domains[domain] | 274 entry[2] = domains[domain] |
| 269 L.append(entry) | 275 L.append(entry) |
| 270 replay_server = start_replay_server(args, sites) if args.replay_wpr else None | |
| 271 try: | 276 try: |
| 272 # Run them. | 277 # Run them. |
| 273 for site, domain, count, timeout in L: | 278 for site, domain, count, timeout in L: |
| 274 if count is not None: domain = "{}%{}".format(domain, count) | 279 if count is not None: domain = "{}%{}".format(domain, count) |
| 275 print site, domain, timeout | 280 print(site, domain, timeout) |
| 276 run_site(site, domain, args, timeout) | 281 run_site(site, domain, args, timeout) |
| 277 finally: | 282 finally: |
| 278 if replay_server: | 283 if replay_server: |
| 279 stop_replay_server(replay_server) | 284 stop_replay_server(replay_server) |
| 280 | 285 |
| 281 | 286 |
| 287 def do_run_replay_server(args): |
| 288 sites = read_sites(args) |
| 289 print("- " * 40) |
| 290 print("Available URLs:") |
| 291 for site in sites: |
| 292 print(" "+site['url']) |
| 293 print("- " * 40) |
| 294 print("Launch chromium with the following commands for debugging:") |
| 295 flags = get_chrome_flags("'--runtime-calls-stats --allow-natives-syntax'", |
| 296 "/var/tmp/`date +%s`") |
| 297 flags += get_chrome_replay_flags(args) |
| 298 print(" $CHROMIUM_DIR/out/Release/chomium " + (" ".join(flags)) + " <URL>") |
| 299 print("- " * 40) |
| 300 replay_server = start_replay_server(args, sites) |
| 301 try: |
| 302 replay_server['process'].wait() |
| 303 finally: |
| 304 stop_replay_server(replay_server) |
| 305 |
| 306 |
| 282 # Calculate statistics. | 307 # Calculate statistics. |
| 283 | 308 |
| 284 def statistics(data): | 309 def statistics(data): |
| 285 N = len(data) | 310 N = len(data) |
| 286 average = numpy.average(data) | 311 average = numpy.average(data) |
| 287 median = numpy.median(data) | 312 median = numpy.median(data) |
| 288 low = numpy.min(data) | 313 low = numpy.min(data) |
| 289 high= numpy.max(data) | 314 high= numpy.max(data) |
| 290 if N > 1: | 315 if N > 1: |
| 291 # evaluate sample variance by setting delta degrees of freedom (ddof) to | 316 # evaluate sample variance by setting delta degrees of freedom (ddof) to |
| (...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 528 given = sum(1 for x in l if x) | 553 given = sum(1 for x in l if x) |
| 529 return given == 0 or given == len(l) | 554 return given == 0 or given == len(l) |
| 530 | 555 |
| 531 def main(): | 556 def main(): |
| 532 parser = argparse.ArgumentParser() | 557 parser = argparse.ArgumentParser() |
| 533 subparser_adder = parser.add_subparsers(title="commands", dest="command", | 558 subparser_adder = parser.add_subparsers(title="commands", dest="command", |
| 534 metavar="<command>") | 559 metavar="<command>") |
| 535 subparsers = {} | 560 subparsers = {} |
| 536 # Command: run. | 561 # Command: run. |
| 537 subparsers["run"] = subparser_adder.add_parser( | 562 subparsers["run"] = subparser_adder.add_parser( |
| 538 "run", help="run --help") | 563 "run", help="Replay websites and collect runtime stats data.") |
| 539 subparsers["run"].set_defaults( | 564 subparsers["run"].set_defaults( |
| 540 func=do_run, error=subparsers["run"].error) | 565 func=do_run, error=subparsers["run"].error) |
| 541 subparsers["run"].add_argument( | 566 subparsers["run"].add_argument( |
| 542 "--chrome-flags", type=str, default="", | 567 "--chrome-flags", type=str, default="", |
| 543 help="specify additional chrome flags") | 568 help="specify additional chrome flags") |
| 544 subparsers["run"].add_argument( | 569 subparsers["run"].add_argument( |
| 545 "--js-flags", type=str, default="", | 570 "--js-flags", type=str, default="", |
| 546 help="specify additional V8 flags") | 571 help="specify additional V8 flags") |
| 547 subparsers["run"].add_argument( | 572 subparsers["run"].add_argument( |
| 548 "--domain", type=str, default="", | |
| 549 help="specify the output file domain name") | |
| 550 subparsers["run"].add_argument( | |
| 551 "--no-url", dest="print_url", action="store_false", default=True, | |
| 552 help="do not include url in statistics file") | |
| 553 subparsers["run"].add_argument( | |
| 554 "-n", "--repeat", type=int, metavar="<num>", | |
| 555 help="specify iterations for each website (default: once)") | |
| 556 subparsers["run"].add_argument( | |
| 557 "-k", "--refresh", type=int, metavar="<num>", default=0, | |
| 558 help="specify refreshes for each iteration (default: 0)") | |
| 559 subparsers["run"].add_argument( | |
| 560 "--replay-wpr", type=str, metavar="<path>", | |
| 561 help="use the specified web page replay (.wpr) archive") | |
| 562 subparsers["run"].add_argument( | |
| 563 "--replay-bin", type=str, metavar="<path>", | |
| 564 help="specify the replay.py script typically located in " \ | |
| 565 "$CHROMIUM/src/third_party/webpagereplay/replay.py") | |
| 566 subparsers["run"].add_argument( | |
| 567 "-r", "--retries", type=int, metavar="<num>", | |
| 568 help="specify retries if website is down (default: forever)") | |
| 569 subparsers["run"].add_argument( | |
| 570 "-f", "--sites-file", type=str, metavar="<path>", | |
| 571 help="specify file containing benchmark websites") | |
| 572 subparsers["run"].add_argument( | |
| 573 "-t", "--timeout", type=int, metavar="<seconds>", default=60, | |
| 574 help="specify seconds before chrome is killed") | |
| 575 subparsers["run"].add_argument( | |
| 576 "-p", "--port-offset", type=int, metavar="<offset>", default=0, | |
| 577 help="specify the offset for the replay server's default ports") | |
| 578 subparsers["run"].add_argument( | |
| 579 "-u", "--user-data-dir", type=str, metavar="<path>", | 573 "-u", "--user-data-dir", type=str, metavar="<path>", |
| 580 help="specify user data dir (default is temporary)") | 574 help="specify user data dir (default is temporary)") |
| 581 subparsers["run"].add_argument( | 575 subparsers["run"].add_argument( |
| 582 "-c", "--with-chrome", type=str, metavar="<path>", | 576 "-c", "--with-chrome", type=str, metavar="<path>", |
| 583 default="/usr/bin/google-chrome", | 577 default="/usr/bin/google-chrome", |
| 584 help="specify chrome executable to use") | 578 help="specify chrome executable to use") |
| 585 subparsers["run"].add_argument( | 579 subparsers["run"].add_argument( |
| 586 "-l", "--log-stderr", type=str, metavar="<path>", | 580 "-r", "--retries", type=int, metavar="<num>", |
| 587 help="specify where chrome's stderr should go (default: /dev/null)") | 581 help="specify retries if website is down (default: forever)") |
| 588 subparsers["run"].add_argument( | 582 subparsers["run"].add_argument( |
| 589 "sites", type=str, metavar="<URL>", nargs="*", | 583 "--no-url", dest="print_url", action="store_false", default=True, |
| 590 help="specify benchmark website") | 584 help="do not include url in statistics file") |
| 585 subparsers["run"].add_argument( |
| 586 "--domain", type=str, default="", |
| 587 help="specify the output file domain name") |
| 588 subparsers["run"].add_argument( |
| 589 "-n", "--repeat", type=int, metavar="<num>", |
| 590 help="specify iterations for each website (default: once)") |
| 591 |
| 592 def add_replay_args(subparser): |
| 593 subparser.add_argument( |
| 594 "-k", "--refresh", type=int, metavar="<num>", default=0, |
| 595 help="specify refreshes for each iteration (default: 0)") |
| 596 subparser.add_argument( |
| 597 "--replay-wpr", type=str, metavar="<path>", |
| 598 help="use the specified web page replay (.wpr) archive") |
| 599 subparser.add_argument( |
| 600 "--replay-bin", type=str, metavar="<path>", |
| 601 help="specify the replay.py script typically located in " \ |
| 602 "$CHROMIUM/src/third_party/webpagereplay/replay.py") |
| 603 subparser.add_argument( |
| 604 "-f", "--sites-file", type=str, metavar="<path>", |
| 605 help="specify file containing benchmark websites") |
| 606 subparser.add_argument( |
| 607 "-t", "--timeout", type=int, metavar="<seconds>", default=60, |
| 608 help="specify seconds before chrome is killed") |
| 609 subparser.add_argument( |
| 610 "-p", "--port-offset", type=int, metavar="<offset>", default=0, |
| 611 help="specify the offset for the replay server's default ports") |
| 612 subparser.add_argument( |
| 613 "-l", "--log-stderr", type=str, metavar="<path>", |
| 614 help="specify where chrome's stderr should go (default: /dev/null)") |
| 615 subparser.add_argument( |
| 616 "sites", type=str, metavar="<URL>", nargs="*", |
| 617 help="specify benchmark website") |
| 618 add_replay_args(subparsers["run"]) |
| 619 |
| 620 # Command: replay-server |
| 621 subparsers["replay"] = subparser_adder.add_parser( |
| 622 "replay", help="Run the replay server for debugging purposes") |
| 623 subparsers["replay"].set_defaults( |
| 624 func=do_run_replay_server, error=subparsers["replay"].error) |
| 625 add_replay_args(subparsers["replay"]) |
| 626 |
| 591 # Command: stats. | 627 # Command: stats. |
| 592 subparsers["stats"] = subparser_adder.add_parser( | 628 subparsers["stats"] = subparser_adder.add_parser( |
| 593 "stats", help="stats --help") | 629 "stats", help="Analize the results file create by the 'run' command.") |
| 594 subparsers["stats"].set_defaults( | 630 subparsers["stats"].set_defaults( |
| 595 func=do_stats, error=subparsers["stats"].error) | 631 func=do_stats, error=subparsers["stats"].error) |
| 596 subparsers["stats"].add_argument( | 632 subparsers["stats"].add_argument( |
| 597 "-l", "--limit", type=int, metavar="<num>", default=0, | 633 "-l", "--limit", type=int, metavar="<num>", default=0, |
| 598 help="limit how many items to print (default: none)") | 634 help="limit how many items to print (default: none)") |
| 599 subparsers["stats"].add_argument( | 635 subparsers["stats"].add_argument( |
| 600 "-s", "--sort", choices=["asc", "desc"], default="asc", | 636 "-s", "--sort", choices=["asc", "desc"], default="asc", |
| 601 help="specify sorting order (default: ascending)") | 637 help="specify sorting order (default: ascending)") |
| 602 subparsers["stats"].add_argument( | 638 subparsers["stats"].add_argument( |
| 603 "-n", "--no-total", dest="totals", action="store_false", default=True, | 639 "-n", "--no-total", dest="totals", action="store_false", default=True, |
| 604 help="do not print totals") | 640 help="do not print totals") |
| 605 subparsers["stats"].add_argument( | 641 subparsers["stats"].add_argument( |
| 606 "logfiles", type=str, metavar="<logfile>", nargs="*", | 642 "logfiles", type=str, metavar="<logfile>", nargs="*", |
| 607 help="specify log files to parse") | 643 help="specify log files to parse") |
| 608 subparsers["stats"].add_argument( | 644 subparsers["stats"].add_argument( |
| 609 "--aggregate", dest="aggregate", action="store_true", default=False, | 645 "--aggregate", dest="aggregate", action="store_true", default=False, |
| 610 help="Create aggregated entries. Adds Group-* entries at the toplevel. " + | 646 help="Create aggregated entries. Adds Group-* entries at the toplevel. " \ |
| 611 "Additionally creates a Total page with all entries.") | 647 "Additionally creates a Total page with all entries.") |
| 648 |
| 612 # Command: json. | 649 # Command: json. |
| 613 subparsers["json"] = subparser_adder.add_parser( | 650 subparsers["json"] = subparser_adder.add_parser( |
| 614 "json", help="json --help") | 651 "json", help="Collect results file created by the 'run' command into" \ |
| 652 "a single json file.") |
| 615 subparsers["json"].set_defaults( | 653 subparsers["json"].set_defaults( |
| 616 func=do_json, error=subparsers["json"].error) | 654 func=do_json, error=subparsers["json"].error) |
| 617 subparsers["json"].add_argument( | 655 subparsers["json"].add_argument( |
| 618 "logdirs", type=str, metavar="<logdir>", nargs="*", | 656 "logdirs", type=str, metavar="<logdir>", nargs="*", |
| 619 help="specify directories with log files to parse") | 657 help="specify directories with log files to parse") |
| 620 subparsers["json"].add_argument( | 658 subparsers["json"].add_argument( |
| 621 "--aggregate", dest="aggregate", action="store_true", default=False, | 659 "--aggregate", dest="aggregate", action="store_true", default=False, |
| 622 help="Create aggregated entries. Adds Group-* entries at the toplevel. " + | 660 help="Create aggregated entries. Adds Group-* entries at the toplevel. " \ |
| 623 "Additionally creates a Total page with all entries.") | 661 "Additionally creates a Total page with all entries.") |
| 662 |
| 624 # Command: help. | 663 # Command: help. |
| 625 subparsers["help"] = subparser_adder.add_parser( | 664 subparsers["help"] = subparser_adder.add_parser( |
| 626 "help", help="help information") | 665 "help", help="help information") |
| 627 subparsers["help"].set_defaults( | 666 subparsers["help"].set_defaults( |
| 628 func=lambda args: do_help(parser, subparsers, args), | 667 func=lambda args: do_help(parser, subparsers, args), |
| 629 error=subparsers["help"].error) | 668 error=subparsers["help"].error) |
| 630 subparsers["help"].add_argument( | 669 subparsers["help"].add_argument( |
| 631 "help_cmd", type=str, metavar="<command>", nargs="?", | 670 "help_cmd", type=str, metavar="<command>", nargs="?", |
| 632 help="command for which to display help") | 671 help="command for which to display help") |
| 672 |
| 633 # Execute the command. | 673 # Execute the command. |
| 634 args = parser.parse_args() | 674 args = parser.parse_args() |
| 635 setattr(args, 'script_path', os.path.dirname(sys.argv[0])) | 675 setattr(args, 'script_path', os.path.dirname(sys.argv[0])) |
| 636 if args.command == "run" and coexist(args.sites_file, args.sites): | 676 if args.command == "run" and coexist(args.sites_file, args.sites): |
| 637 args.error("use either option --sites-file or site URLs") | 677 args.error("use either option --sites-file or site URLs") |
| 638 sys.exit(1) | 678 sys.exit(1) |
| 639 elif args.command == "run" and not coexist(args.replay_wpr, args.replay_bin): | 679 elif args.command == "run" and not coexist(args.replay_wpr, args.replay_bin): |
| 640 args.error("options --replay-wpr and --replay-bin must be used together") | 680 args.error("options --replay-wpr and --replay-bin must be used together") |
| 641 sys.exit(1) | 681 sys.exit(1) |
| 642 else: | 682 else: |
| 643 args.func(args) | 683 args.func(args) |
| 644 | 684 |
| 645 if __name__ == "__main__": | 685 if __name__ == "__main__": |
| 646 sys.exit(main()) | 686 sys.exit(main()) |
| OLD | NEW |