code: #!/usr/bin/env python
  # Copyright 2016 the V8 project authors. All rights reserved.
  # Use of this source code is governed by a BSD-style license that can be
  # found in the LICENSE file.
  '''
  Usage: callstats.py [-h] <command> ...
  
  Optional arguments:
    -h, --help  show this help message and exit
  
  Commands:
    run         run chrome with --runtime-call-stats and generate logs
    stats       process logs and print statistics
    json        process logs from several versions and generate JSON
    help        help information
  
  For each command, you can try ./runtime-call-stats.py help command.
  '''
  
  # for py2/py3 compatibility
  from __future__ import print_function
  
  import argparse
  import json
  import os
  import re
  import shutil
  import subprocess
  import sys
  import tempfile
  import operator
  from callstats_groups import RUNTIME_CALL_STATS_GROUPS
  
  import numpy
  from math import sqrt
  
  
  MAX_NOF_RETRIES = 5
  
  
  # Run benchmarks.
  
  def print_command(cmd_args):
    def fix_for_printing(arg):
      m = re.match(r'^--([^=]+)=(.*)$', arg)
      if m and (' ' in m.group(2) or m.group(2).startswith('-')):
        arg = "--{}='{}'".format(m.group(1), m.group(2))
      elif ' ' in arg:
        arg = "'{}'".format(arg)
      return arg
    print(" ".join(map(fix_for_printing, cmd_args)))
  
  
  def start_replay_server(args, sites, discard_output=True):
    with tempfile.NamedTemporaryFile(prefix='callstats-inject-', suffix='.js',
                                     mode='wt', delete=False) as f:
      injection = f.name
      generate_injection(f, sites, args.refresh)
    http_port = 4080 + args.port_offset
    https_port = 4443 + args.port_offset
    cmd_args = [
        args.replay_bin,
        "--port=%s" % http_port,
        "--ssl_port=%s" % https_port,
        "--no-dns_forwarding",
        "--use_closest_match",
        "--no-diff_unknown_requests",
        "--inject_scripts=deterministic.js,{}".format(injection),
        args.replay_wpr,
    ]
    print("=" * 80)
    print_command(cmd_args)
    if discard_output:
      with open(os.devnull, 'w') as null:
        server = subprocess.Popen(cmd_args, stdout=null, stderr=null)
    else:
        server = subprocess.Popen(cmd_args)
    print("RUNNING REPLAY SERVER: %s with PID=%s" % (args.replay_bin, server.pid))
    print("=" * 80)
    return {'process': server, 'injection': injection}
  
  
  def stop_replay_server(server):
    print("SHUTTING DOWN REPLAY SERVER %s" % server['process'].pid)
    server['process'].terminate()
    os.remove(server['injection'])
  
  
  def generate_injection(f, sites, refreshes=0):
    print("""\
  (function() {
    var s = window.sessionStorage.getItem("refreshCounter");
    var refreshTotal = """, refreshes, """;
    var refreshCounter = s ? parseInt(s) : refreshTotal;
    var refreshId = refreshTotal - refreshCounter;
    if (refreshCounter > 0) {
      window.sessionStorage.setItem("refreshCounter", refreshCounter-1);
    }
    function match(url, item) {
      if ('regexp' in item) { return url.match(item.regexp) !== null };
      var url_wanted = item.url;
      /* Allow automatic redirections from http to https. */
      if (url_wanted.startsWith("http://") && url.startsWith("https://")) {
        url_wanted = "https://" + url_wanted.substr(7);
      }
      return url.startsWith(url_wanted);
    };
    function onLoad(url) {
      for (var item of sites) {
        if (!match(url, item)) continue;
        var timeout = 'timeline' in item ? 2000 * item.timeline
                    : 'timeout'  in item ? 1000 * (item.timeout - 3)
                    : 10000;
        console.log("Setting time out of " + timeout + " for: " + url);
        window.setTimeout(function() {
          console.log("Time is out for: " + url);
          var msg = "STATS: (" + refreshId + ") " + url;
          %GetAndResetRuntimeCallStats(1, msg);
          if (refreshCounter > 0) {
            console.log(
                "Refresh counter is " + refreshCounter + ", refreshing: " + url);
            window.location.reload();
          }
        }, timeout);
        return;
      }
      console.log("Ignoring: " + url);
    };
    var sites =
      """, json.dumps(sites), """;
    onLoad(window.location.href);
  })();""", file=f)
  
  def get_chrome_flags(js_flags, user_data_dir, arg_delimiter=""):
    return [
        "--no-default-browser-check",
        "--no-sandbox",
        "--disable-translate",
        "--enable-benchmarking",
        "--enable-stats-table",
        "--js-flags={}{}{}".format(arg_delimiter, js_flags, arg_delimiter),
        "--no-first-run",
        "--user-data-dir={}{}{}".format(arg_delimiter, user_data_dir,
                                        arg_delimiter),
        "--data-path={}{}{}".format(arg_delimiter,
            os.path.join(user_data_dir, 'content-shell-data'), arg_delimiter),
      ]
  
  def get_chrome_replay_flags(args, arg_delimiter=""):
    http_port = 4080 + args.port_offset
    https_port = 4443 + args.port_offset
    return [
        "--host-resolver-rules=%sMAP *:80 localhost:%s, "  \
                                "MAP *:443 localhost:%s, " \
                                "EXCLUDE localhost%s" % (
                                 arg_delimiter, http_port, https_port,
                                 arg_delimiter),
        "--ignore-certificate-errors",
        "--disable-seccomp-sandbox",
        "--disable-web-security",
        "--reduce-security-for-testing",
        "--allow-insecure-localhost",
      ]
  
  def run_site(site, domain, args, timeout=None):
    print("="*80)
    print("RUNNING DOMAIN %s" % domain)
    print("="*80)
    result_template = "{domain}#{count}.txt" if args.repeat else "{domain}.txt"
    count = 0
    if timeout is None: timeout = args.timeout
    if args.replay_wpr:
      timeout *= 1 + args.refresh
      timeout += 1
    retries_since_good_run = 0
    while count == 0 or args.repeat is not None and count < args.repeat:
      count += 1
      result = result_template.format(domain=domain, count=count)
      retries = 0
      while args.retries is None or retries < args.retries:
        retries += 1
        try:
          if args.user_data_dir:
            user_data_dir = args.user_data_dir
          else:
            user_data_dir = tempfile.mkdtemp(prefix="chr_")
          js_flags = "--runtime-call-stats"
          if args.replay_wpr: js_flags += " --allow-natives-syntax"
          if args.js_flags: js_flags += " " + args.js_flags
          chrome_flags = get_chrome_flags(js_flags, user_data_dir)
          if args.replay_wpr:
            chrome_flags += get_chrome_replay_flags(args)
          else:
            chrome_flags += [ "--single-process", ]
          if args.chrome_flags:
            chrome_flags += args.chrome_flags.split()
          cmd_args = [
              "timeout", str(timeout),
              args.with_chrome
          ] + chrome_flags + [ site ]
          print("- " * 40)
          print_command(cmd_args)
          print("- " * 40)
          with open(result, "wt") as f:
            with open(args.log_stderr or os.devnull, 'at') as err:
              status = subprocess.call(cmd_args, stdout=f, stderr=err)
          # 124 means timeout killed chrome, 0 means the user was bored first!
          # If none of these two happened, then chrome apparently crashed, so
          # it must be called again.
          if status != 124 and status != 0:
            print("CHROME CRASHED, REPEATING RUN");
            continue
          # If the stats file is empty, chrome must be called again.
          if os.path.isfile(result) and os.path.getsize(result) > 0:
            if args.print_url:
              with open(result, "at") as f:
                print(file=f)
                print("URL: {}".format(site), file=f)
            retries_since_good_run = 0
            break
          if retries_since_good_run > MAX_NOF_RETRIES:
            # Abort after too many retries, no point in ever increasing the
            # timeout.
            print("TOO MANY EMPTY RESULTS ABORTING RUN")
            return
          timeout += 2 ** retries_since_good_run
          retries_since_good_run += 1
          print("EMPTY RESULT, REPEATING RUN ({})".format(
              retries_since_good_run));
        finally:
          if not args.user_data_dir:
            shutil.rmtree(user_data_dir)
  
  
  def read_sites_file(args):
    try:
      sites = []
      try:
        with open(args.sites_file, "rt") as f:
          for item in json.load(f):
            if 'timeout' not in item:
              # This is more-or-less arbitrary.
              item['timeout'] = int(1.5 * item['timeline'] + 7)
            if item['timeout'] > args.timeout: item['timeout'] = args.timeout
            sites.append(item)
      except ValueError:
        args.error("Warning: Could not read sites file as JSON, falling back to "
                   "primitive file")
        with open(args.sites_file, "rt") as f:
          for line in f:
            line = line.strip()
            if not line or line.startswith('#'): continue
            sites.append({'url': line, 'timeout': args.timeout})
      return sites
    except IOError as e:
      args.error("Cannot read from {}. {}.".format(args.sites_file, e.strerror))
      sys.exit(1)
  
  
  def read_sites(args):
    # Determine the websites to benchmark.
    if args.sites_file:
      return read_sites_file(args)
    return [{'url': site, 'timeout': args.timeout} for site in args.sites]
  
  def do_run(args):
    sites = read_sites(args)
    replay_server = start_replay_server(args, sites) if args.replay_wpr else None
    # Disambiguate domains, if needed.
    L = []
    domains = {}
    for item in sites:
      site = item['url']
      domain = None
      if args.domain:
        domain = args.domain
      elif 'domain' in item:
        domain = item['domain']
      else:
        m = re.match(r'^(https?://)?([^/]+)(/.*)?$', site)
        if not m:
          args.error("Invalid URL {}.".format(site))
          continue
        domain = m.group(2)
      entry = [site, domain, None, item['timeout']]
      if domain not in domains:
        domains[domain] = entry
      else:
        if not isinstance(domains[domain], int):
          domains[domain][2] = 1
          domains[domain] = 1
        domains[domain] += 1
        entry[2] = domains[domain]
      L.append(entry)
    try:
      # Run them.
      for site, domain, count, timeout in L:
        if count is not None: domain = "{}%{}".format(domain, count)
        print((site, domain, timeout))
        run_site(site, domain, args, timeout)
    finally:
      if replay_server:
        stop_replay_server(replay_server)
  
  
  def do_run_replay_server(args):
    sites = read_sites(args)
    print("- " * 40)
    print("Available URLs:")
    for site in sites:
      print("    "+site['url'])
    print("- " * 40)
    print("Launch chromium with the following commands for debugging:")
    flags = get_chrome_flags("--runtime-call-stats --allow-natives-syntax",
                             "/var/tmp/`date +%s`", '"')
    flags += get_chrome_replay_flags(args, "'")
    print("    $CHROMIUM_DIR/out/Release/chrome " + (" ".join(flags)) + " <URL>")
    print("- " * 40)
    replay_server = start_replay_server(args, sites, discard_output=False)
    try:
      replay_server['process'].wait()
    finally:
     stop_replay_server(replay_server)
  
  
  # Calculate statistics.
  
  def statistics(data):
    # NOTE(V8:10269): imports moved here to mitigate the outage.
    import scipy
    import scipy.stats
  
    N = len(data)
    average = numpy.average(data)
    median = numpy.median(data)
    low = numpy.min(data)
    high= numpy.max(data)
    if N > 1:
      # evaluate sample variance by setting delta degrees of freedom (ddof) to
      # 1. The degree used in calculations is N - ddof
      stddev = numpy.std(data, ddof=1)
      # Get the endpoints of the range that contains 95% of the distribution
      t_bounds = scipy.stats.t.interval(0.95, N-1)
      #assert abs(t_bounds[0] + t_bounds[1]) < 1e-6
      # sum mean to the confidence interval
      ci = {
          'abs': t_bounds[1] * stddev / sqrt(N),
          'low': average + t_bounds[0] * stddev / sqrt(N),
          'high': average + t_bounds[1] * stddev / sqrt(N)
      }
    else:
      stddev = 0
      ci = { 'abs': 0, 'low': average, 'high': average }
    if abs(stddev) > 0.0001 and abs(average) > 0.0001:
      ci['perc'] = t_bounds[1] * stddev / sqrt(N) / average * 100
    else:
      ci['perc'] = 0
    return { 'samples': N, 'average': average, 'median': median,
             'stddev': stddev, 'min': low, 'max': high, 'ci': ci }
  
  
  def add_category_total(entries, groups, category_prefix):
    group_data = { 'time': 0, 'count': 0 }
    for group_name, regexp in groups:
      if not group_name.startswith('Group-' + category_prefix): continue
      group_data['time'] += entries[group_name]['time']
      group_data['count'] += entries[group_name]['count']
    entries['Group-' + category_prefix + '-Total'] = group_data
  
  
  def read_stats(path, domain, args):
    groups = [];
    if args.aggregate:
      groups = [
          ('Group-IC', re.compile(".*IC_.*")),
          ('Group-OptimizeBackground', re.compile(".*OptimizeBackground.*")),
          ('Group-Optimize',
           re.compile("StackGuard|.*Optimize.*|.*Deoptimize.*|Recompile.*")),
          ('Group-CompileBackground', re.compile("(.*CompileBackground.*)")),
          ('Group-Compile', re.compile("(^Compile.*)|(.*_Compile.*)")),
          ('Group-ParseBackground', re.compile(".*ParseBackground.*")),
          ('Group-Parse', re.compile(".*Parse.*")),
          ('Group-Callback', re.compile(".*Callback.*")),
          ('Group-API', re.compile(".*API.*")),
          ('Group-GC-Custom', re.compile("GC_Custom_.*")),
          ('Group-GC-Background', re.compile(".*GC.*BACKGROUND.*")),
          ('Group-GC', re.compile("GC_.*|AllocateInTargetSpace")),
          ('Group-JavaScript', re.compile("JS_Execution")),
          ('Group-Runtime', re.compile(".*"))]
    with open(path, "rt") as f:
      # Process the whole file and sum repeating entries.
      entries = { 'Sum': {'time': 0, 'count': 0} }
      for group_name, regexp in groups:
        entries[group_name] = { 'time': 0, 'count': 0 }
      for line in f:
        line = line.strip()
        # Discard headers and footers.
        if not line: continue
        if line.startswith("Runtime Function"): continue
        if line.startswith("===="): continue
        if line.startswith("----"): continue
        if line.startswith("URL:"): continue
        if line.startswith("STATS:"): continue
        # We have a regular line.
        fields = line.split()
        key = fields[0]
        time = float(fields[1].replace("ms", ""))
        count = int(fields[3])
        if key not in entries: entries[key] = { 'time': 0, 'count': 0 }
        entries[key]['time'] += time
        entries[key]['count'] += count
        # We calculate the sum, if it's not the "total" line.
        if key != "Total":
          entries['Sum']['time'] += time
          entries['Sum']['count'] += count
          for group_name, regexp in groups:
            if not regexp.match(key): continue
            entries[group_name]['time'] += time
            entries[group_name]['count'] += count
            break
      # Calculate the V8-Total (all groups except Callback)
      group_data = { 'time': 0, 'count': 0 }
      for group_name, regexp in groups:
        if group_name == 'Group-Callback': continue
        group_data['time'] += entries[group_name]['time']
        group_data['count'] += entries[group_name]['count']
      entries['Group-Total-V8'] = group_data
      # Calculate the Parse-Total, Compile-Total and Optimize-Total groups
      add_category_total(entries, groups, 'Parse')
      add_category_total(entries, groups, 'Compile')
      add_category_total(entries, groups, 'Optimize')
      # Append the sums as single entries to domain.
      for key in entries:
        if key not in domain: domain[key] = { 'time_list': [], 'count_list': [] }
        domain[key]['time_list'].append(entries[key]['time'])
        domain[key]['count_list'].append(entries[key]['count'])
  
  
  def print_stats(S, args):
    # Sort by ascending/descending time average, then by ascending/descending
    # count average, then by ascending name.
    def sort_asc_func(item):
      return (item[1]['time_stat']['average'],
              item[1]['count_stat']['average'],
              item[0])
    def sort_desc_func(item):
      return (-item[1]['time_stat']['average'],
              -item[1]['count_stat']['average'],
              item[0])
    # Sorting order is in the commend-line arguments.
    sort_func = sort_asc_func if args.sort == "asc" else sort_desc_func
    # Possibly limit how many elements to print.
    L = [item for item in sorted(S.items(), key=sort_func)
         if item[0] not in ["Total", "Sum"]]
    N = len(L)
    if args.limit == 0:
      low, high = 0, N
    elif args.sort == "desc":
      low, high = 0, args.limit
    else:
      low, high = N-args.limit, N
    # How to print entries.
    def print_entry(key, value):
      def stats(s, units=""):
        conf = "{:0.1f}({:0.2f}%)".format(s['ci']['abs'], s['ci']['perc'])
        return "{:8.1f}{} +/- {:15s}".format(s['average'], units, conf)
      print("{:>50s}  {}  {}".format(
        key,
        stats(value['time_stat'], units="ms"),
        stats(value['count_stat'])
      ))
    # Print and calculate partial sums, if necessary.
    for i in range(low, high):
      print_entry(*L[i])
      if args.totals and args.limit != 0 and not args.aggregate:
        if i == low:
          partial = { 'time_list': [0] * len(L[i][1]['time_list']),
                      'count_list': [0] * len(L[i][1]['count_list']) }
        assert len(partial['time_list']) == len(L[i][1]['time_list'])
        assert len(partial['count_list']) == len(L[i][1]['count_list'])
        for j, v in enumerate(L[i][1]['time_list']):
          partial['time_list'][j] += v
        for j, v in enumerate(L[i][1]['count_list']):
          partial['count_list'][j] += v
    # Print totals, if necessary.
    if args.totals:
      print('-' * 80)
      if args.limit != 0 and not args.aggregate:
        partial['time_stat'] = statistics(partial['time_list'])
        partial['count_stat'] = statistics(partial['count_list'])
        print_entry("Partial", partial)
      print_entry("Sum", S["Sum"])
      print_entry("Total", S["Total"])
  
  
  def do_stats(args):
    domains = {}
    for path in args.logfiles:
      filename = os.path.basename(path)
      m = re.match(r'^([^#]+)(#.*)?$', filename)
      domain = m.group(1)
      if domain not in domains: domains[domain] = {}
      read_stats(path, domains[domain], args)
    if args.aggregate:
      create_total_page_stats(domains, args)
    for i, domain in enumerate(sorted(domains)):
      if len(domains) > 1:
        if i > 0: print()
        print("{}:".format(domain))
        print('=' * 80)
      domain_stats = domains[domain]
      for key in domain_stats:
        domain_stats[key]['time_stat'] = \
            statistics(domain_stats[key]['time_list'])
        domain_stats[key]['count_stat'] = \
            statistics(domain_stats[key]['count_list'])
      print_stats(domain_stats, args)
  
  
  # Create a Total page with all entries summed up.
  def create_total_page_stats(domains, args):
    total = {}
    def sum_up(parent, key, other):
      sums = parent[key]
      for i, item in enumerate(other[key]):
        if i >= len(sums):
          sums.extend([0] * (i - len(sums) + 1))
        if item is not None:
          sums[i] += item
    # Exclude adwords and speedometer pages from aggrigate total, since adwords
    # dominates execution time and speedometer is measured elsewhere.
    excluded_domains = ['adwords.google.com', 'speedometer-angular',
                        'speedometer-jquery', 'speedometer-backbone',
                        'speedometer-ember', 'speedometer-vanilla'];
    # Sum up all the entries/metrics from all non-excluded domains
    for domain, entries in domains.items():
      if domain in excluded_domains:
        continue;
      for key, domain_stats in entries.items():
        if key not in total:
          total[key] = {}
          total[key]['time_list'] = list(domain_stats['time_list'])
          total[key]['count_list'] = list(domain_stats['count_list'])
        else:
          sum_up(total[key], 'time_list', domain_stats)
          sum_up(total[key], 'count_list', domain_stats)
    # Add a new "Total" page containing the summed up metrics.
    domains['Total'] = total
  
  # Generate Raw JSON file.
  
  def _read_logs(args):
    versions = {}
    for path in args.logdirs:
      if os.path.isdir(path):
        for root, dirs, files in os.walk(path):
          version = os.path.basename(root)
          if version not in versions: versions[version] = {}
          for filename in files:
            if filename.endswith(".txt"):
              m = re.match(r'^([^#]+)(#.*)?\.txt$', filename)
              domain = m.group(1)
              if domain not in versions[version]: versions[version][domain] = {}
              read_stats(os.path.join(root, filename),
                         versions[version][domain], args)
  
    return versions
  
  def do_raw_json(args):
    versions = _read_logs(args)
  
    for version, domains in versions.items():
      if args.aggregate:
        create_total_page_stats(domains, args)
      for domain, entries in domains.items():
        raw_entries = []
        for name, value in entries.items():
          # We don't want the calculated sum in the JSON file.
          if name == "Sum": continue
          raw_entries.append({
            'name': name,
            'duration': value['time_list'],
            'count': value['count_list'],
          })
  
        domains[domain] = raw_entries
  
    print(json.dumps(versions, separators=(',', ':')))
  
  
  # Generate JSON file.
  
  def do_json(args):
    versions = _read_logs(args)
  
    for version, domains in versions.items():
      if args.aggregate:
        create_total_page_stats(domains, args)
      for domain, entries in domains.items():
        stats = []
        for name, value in entries.items():
          # We don't want the calculated sum in the JSON file.
          if name == "Sum": continue
          entry = [name]
          for x in ['time_list', 'count_list']:
            s = statistics(entries[name][x])
            entry.append(round(s['average'], 1))
            entry.append(round(s['ci']['abs'], 1))
            entry.append(round(s['ci']['perc'], 2))
          stats.append(entry)
        domains[domain] = stats
    print(json.dumps(versions, separators=(',', ':')))
  
  
  # Help.
  
  def do_help(parser, subparsers, args):
    if args.help_cmd:
      if args.help_cmd in subparsers:
        subparsers[args.help_cmd].print_help()
      else:
        args.error("Unknown command '{}'".format(args.help_cmd))
    else:
      parser.print_help()
  
  
  # Main program, parse command line and execute.
  
  def coexist(*l):
    given = sum(1 for x in l if x)
    return given == 0 or given == len(l)
  
  def main():
    parser = argparse.ArgumentParser()
    subparser_adder = parser.add_subparsers(title="commands", dest="command",
                                            metavar="<command>")
    subparsers = {}
    # Command: run.
    subparsers["run"] = subparser_adder.add_parser(
        "run", help="Replay websites and collect runtime stats data.")
    subparsers["run"].set_defaults(
        func=do_run, error=subparsers["run"].error)
    subparsers["run"].add_argument(
        "--chrome-flags", type=str, default="",
        help="specify additional chrome flags")
    subparsers["run"].add_argument(
        "--js-flags", type=str, default="",
        help="specify additional V8 flags")
    subparsers["run"].add_argument(
        "-u", "--user-data-dir", type=str, metavar="<path>",
        help="specify user data dir (default is temporary)")
    subparsers["run"].add_argument(
        "-c", "--with-chrome", type=str, metavar="<path>",
        default="/usr/bin/google-chrome",
        help="specify chrome executable to use")
    subparsers["run"].add_argument(
        "-r", "--retries", type=int, metavar="<num>",
        help="specify retries if website is down (default: forever)")
    subparsers["run"].add_argument(
        "--no-url", dest="print_url", action="store_false", default=True,
        help="do not include url in statistics file")
    subparsers["run"].add_argument(
        "--domain", type=str, default="",
        help="specify the output file domain name")
    subparsers["run"].add_argument(
        "-n", "--repeat", type=int, metavar="<num>",
        help="specify iterations for each website (default: once)")
  
    def add_replay_args(subparser):
      subparser.add_argument(
          "-k", "--refresh", type=int, metavar="<num>", default=0,
          help="specify refreshes for each iteration (default: 0)")
      subparser.add_argument(
          "--replay-wpr", type=str, metavar="<path>",
          help="use the specified web page replay (.wpr) archive")
      subparser.add_argument(
          "--replay-bin", type=str, metavar="<path>",
          help="specify the replay.py script typically located in " \
               "$CHROMIUM/src/third_party/webpagereplay/replay.py")
      subparser.add_argument(
          "-f", "--sites-file", type=str, metavar="<path>",
          help="specify file containing benchmark websites")
      subparser.add_argument(
          "-t", "--timeout", type=int, metavar="<seconds>", default=60,
          help="specify seconds before chrome is killed")
      subparser.add_argument(
          "-p", "--port-offset", type=int, metavar="<offset>", default=0,
          help="specify the offset for the replay server's default ports")
      subparser.add_argument(
          "-l", "--log-stderr", type=str, metavar="<path>",
          help="specify where chrome's stderr should go (default: /dev/null)")
      subparser.add_argument(
          "--sites", type=str, metavar="<URL>", nargs="*",
          help="specify benchmark website")
    add_replay_args(subparsers["run"])
  
    # Command: replay-server
    subparsers["replay"] = subparser_adder.add_parser(
        "replay", help="Run the replay server for debugging purposes")
    subparsers["replay"].set_defaults(
        func=do_run_replay_server, error=subparsers["replay"].error)
    add_replay_args(subparsers["replay"])
  
    # Command: stats.
    subparsers["stats"] = subparser_adder.add_parser(
        "stats", help="Analize the results file create by the 'run' command.")
    subparsers["stats"].set_defaults(
        func=do_stats, error=subparsers["stats"].error)
    subparsers["stats"].add_argument(
        "-l", "--limit", type=int, metavar="<num>", default=0,
        help="limit how many items to print (default: none)")
    subparsers["stats"].add_argument(
        "-s", "--sort", choices=["asc", "desc"], default="asc",
        help="specify sorting order (default: ascending)")
    subparsers["stats"].add_argument(
        "-n", "--no-total", dest="totals", action="store_false", default=True,
        help="do not print totals")
    subparsers["stats"].add_argument(
        "logfiles", type=str, metavar="<logfile>", nargs="*",
        help="specify log files to parse")
    subparsers["stats"].add_argument(
        "--aggregate", dest="aggregate", action="store_true", default=False,
        help="Create aggregated entries. Adds Group-* entries at the toplevel. " \
        "Additionally creates a Total page with all entries.")
  
    # Command: json.
    subparsers["json"] = subparser_adder.add_parser(
        "json", help="Collect results file created by the 'run' command into" \
            "a single json file.")
    subparsers["json"].set_defaults(
        func=do_json, error=subparsers["json"].error)
    subparsers["json"].add_argument(
        "logdirs", type=str, metavar="<logdir>", nargs="*",
        help="specify directories with log files to parse")
    subparsers["json"].add_argument(
        "--aggregate", dest="aggregate", action="store_true", default=False,
        help="Create aggregated entries. Adds Group-* entries at the toplevel. " \
        "Additionally creates a Total page with all entries.")
  
    # Command: raw-json.
    subparsers["raw-json"] = subparser_adder.add_parser(
        "raw-json", help="Collect raw results from 'run' command into" \
            "a single json file.")
    subparsers["raw-json"].set_defaults(
        func=do_raw_json, error=subparsers["json"].error)
    subparsers["raw-json"].add_argument(
        "logdirs", type=str, metavar="<logdir>", nargs="*",
        help="specify directories with log files to parse")
    subparsers["raw-json"].add_argument(
        "--aggregate", dest="aggregate", action="store_true", default=False,
        help="Create aggregated entries. Adds Group-* entries at the toplevel. " \
        "Additionally creates a Total page with all entries.")
  
    # Command: help.
    subparsers["help"] = subparser_adder.add_parser(
        "help", help="help information")
    subparsers["help"].set_defaults(
        func=lambda args: do_help(parser, subparsers, args),
        error=subparsers["help"].error)
    subparsers["help"].add_argument(
        "help_cmd", type=str, metavar="<command>", nargs="?",
        help="command for which to display help")
  
    # Execute the command.
    args = parser.parse_args()
    setattr(args, 'script_path', os.path.dirname(sys.argv[0]))
    if args.command == "run" and coexist(args.sites_file, args.sites):
      args.error("use either option --sites-file or site URLs")
      sys.exit(1)
    elif args.command == "run" and not coexist(args.replay_wpr, args.replay_bin):
      args.error("options --replay-wpr and --replay-bin must be used together")
      sys.exit(1)
    else:
      args.func(args)
  
  if __name__ == "__main__":
    sys.exit(main())
  
apis: scipy.stats.t.interval numpy.median numpy.max numpy.min numpy.std numpy.average