tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

profileserver.py (9334B)


      1 #!/usr/bin/python
      2 #
      3 # This Source Code Form is subject to the terms of the Mozilla Public
      4 # License, v. 2.0. If a copy of the MPL was not distributed with this
      5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      6 
      7 import glob
      8 import os
      9 import subprocess
     10 import sys
     11 
     12 import mozcrash
     13 from mozbuild.base import BinaryNotFoundException, MozbuildObject
     14 from mozfile import TemporaryDirectory, json
     15 from mozhttpd import MozHttpd
     16 from mozprofile import FirefoxProfile, Preferences
     17 from mozprofile.permissions import ServerLocations
     18 from mozrunner import CLI, FirefoxRunner
     19 
     20 PORT = 8888
     21 
     22 PATH_MAPPINGS = {
     23    "/webkit/PerformanceTests": "third_party/webkit/PerformanceTests",
     24    # It is tempting to map to `testing/talos/talos/tests` instead, to avoid
     25    # writing `tests/` in every path, but we can't do that because some files
     26    # refer to scripts located in `../..`.
     27    "/talos": "testing/talos/talos",
     28 }
     29 
     30 
     31 def get_crashreports(directory, name=None):
     32    rc = 0
     33    upload_path = os.environ.get("UPLOAD_PATH")
     34    if not upload_path:
     35        upload_path = os.environ.get("UPLOAD_DIR")
     36    if upload_path:
     37        # For automation, log the minidumps with stackwalk and get them moved to
     38        # the artifacts directory.
     39        fetches_dir = os.environ.get("MOZ_FETCHES_DIR")
     40        if not fetches_dir:
     41            raise Exception(
     42                "Unable to process minidump in automation because "
     43                "$MOZ_FETCHES_DIR is not set in the environment"
     44            )
     45        stackwalk_binary = os.path.join(
     46            fetches_dir, "minidump-stackwalk", "minidump-stackwalk"
     47        )
     48        if sys.platform == "win32":
     49            stackwalk_binary += ".exe"
     50        minidump_path = os.path.join(directory, "minidumps")
     51        rc = mozcrash.check_for_crashes(
     52            minidump_path,
     53            symbols_path=fetches_dir,
     54            stackwalk_binary=stackwalk_binary,
     55            dump_save_path=upload_path,
     56            test_name=name,
     57        )
     58    return rc
     59 
     60 
     61 if __name__ == "__main__":
     62    cli = CLI()
     63    debug_args, interactive = cli.debugger_arguments()
     64    runner_args = cli.runner_args()
     65 
     66    build = MozbuildObject.from_environment()
     67 
     68    binary = runner_args.get("binary")
     69    if not binary:
     70        try:
     71            binary = build.get_binary_path(where="staged-package")
     72        except BinaryNotFoundException as e:
     73            print(f"{e}\n\n{e.help()}\n")
     74            sys.exit(1)
     75    binary = os.path.normpath(os.path.abspath(binary))
     76 
     77    path_mappings = {
     78        k: os.path.join(build.topsrcdir, v) for k, v in PATH_MAPPINGS.items()
     79    }
     80    httpd = MozHttpd(
     81        port=PORT,
     82        docroot=os.path.join(build.topsrcdir, "build", "pgo"),
     83        path_mappings=path_mappings,
     84    )
     85    httpd.start(block=False)
     86 
     87    sp3_httpd = MozHttpd(
     88        port=8000,
     89        docroot=os.path.join(
     90            build.topsrcdir, "third_party", "webkit", "PerformanceTests", "Speedometer3"
     91        ),
     92        path_mappings=path_mappings,
     93    )
     94    sp3_httpd.start(block=False)
     95    print("started SP3 server on port 8000")
     96    locations = ServerLocations()
     97    locations.add_host(host="127.0.0.1", port=PORT, options="primary,privileged")
     98 
     99    old_profraw_files = glob.glob("*.profraw")
    100    for f in old_profraw_files:
    101        os.remove(f)
    102 
    103    with TemporaryDirectory() as profilePath:
    104        # TODO: refactor this into mozprofile
    105        profile_data_dir = os.path.join(build.topsrcdir, "testing", "profiles")
    106        with open(os.path.join(profile_data_dir, "profiles.json")) as fh:
    107            base_profiles = json.load(fh)["profileserver"]
    108 
    109        prefpaths = [
    110            os.path.join(profile_data_dir, profile, "user.js")
    111            for profile in base_profiles
    112        ]
    113 
    114        prefs = {}
    115        for path in prefpaths:
    116            prefs.update(Preferences.read_prefs(path))
    117 
    118        interpolation = {"server": "%s:%d" % httpd.httpd.server_address}
    119        sp3_interpolation = {"server": "%s:%d" % sp3_httpd.httpd.server_address}
    120        for k, v in prefs.items():
    121            if isinstance(v, str):
    122                v = v.format(**interpolation)
    123            prefs[k] = Preferences.cast(v)
    124 
    125        profile = FirefoxProfile(
    126            profile=profilePath,
    127            preferences=prefs,
    128            addons=[
    129                os.path.join(
    130                    build.topsrcdir, "tools", "quitter", "quitter@mozilla.org.xpi"
    131                )
    132            ],
    133            locations=locations,
    134        )
    135 
    136        env = os.environ.copy()
    137        env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
    138        env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
    139        env["XPCOM_DEBUG_BREAK"] = "warn"
    140 
    141        # Ensure different pids write to different files
    142        # Use absolute path to ensure that Sandbox computes the correct permissions
    143        env["LLVM_PROFILE_FILE"] = os.path.join(
    144            os.getcwd(), "default_%p_random_%m.profraw"
    145        )
    146 
    147        # Write to an output file if we're running in automation
    148        process_args = {"universal_newlines": True}
    149        if "UPLOAD_PATH" in env:
    150            process_args["logfile"] = os.path.join(
    151                env["UPLOAD_PATH"], "profile-run-1.log"
    152            )
    153 
    154        # Run Firefox a first time to initialize its profile
    155        runner = FirefoxRunner(
    156            profile=profile,
    157            binary=binary,
    158            cmdargs=["data:text/html,<script>Quitter.quit()</script>"],
    159            env=env,
    160            process_args=process_args,
    161        )
    162        runner.start()
    163        ret = runner.wait()
    164        if ret:
    165            print("Firefox exited with code %d during profile initialization" % ret)
    166            logfile = process_args.get("logfile")
    167            if logfile:
    168                print("Firefox output (%s):" % logfile)
    169                with open(logfile) as f:
    170                    print(f.read())
    171            sp3_httpd.stop()
    172            httpd.stop()
    173            get_crashreports(profilePath, name="Profile initialization")
    174            sys.exit(ret)
    175 
    176        jarlog = os.getenv("JARLOG_FILE")
    177        if jarlog:
    178            env["MOZ_JAR_LOG_FILE"] = os.path.abspath(jarlog)
    179            print("jarlog: %s" % env["MOZ_JAR_LOG_FILE"])
    180            if os.path.exists(jarlog):
    181                os.remove(jarlog)
    182 
    183        if "UPLOAD_PATH" in env:
    184            process_args["logfile"] = os.path.join(
    185                env["UPLOAD_PATH"], "profile-run-2.log"
    186            )
    187        cmdargs = ["http://localhost:%d/index.html" % PORT]
    188        runner = FirefoxRunner(
    189            profile=profile,
    190            binary=binary,
    191            cmdargs=cmdargs,
    192            env=env,
    193            process_args=process_args,
    194        )
    195        runner.start(debug_args=debug_args, interactive=interactive)
    196        ret = runner.wait()
    197        sp3_httpd.stop()
    198        httpd.stop()
    199        if ret:
    200            print("Firefox exited with code %d during profiling" % ret)
    201            logfile = process_args.get("logfile")
    202            if logfile:
    203                print("Firefox output (%s):" % logfile)
    204                with open(logfile) as f:
    205                    print(f.read())
    206            get_crashreports(profilePath, name="Profiling run")
    207            sys.exit(ret)
    208 
    209        if "UPLOAD_PATH" in env:
    210            should_err = False
    211            print("Verify log for LLVM Profile Error")
    212            for n in range(1, 2):
    213                log = os.path.join(env["UPLOAD_PATH"], f"profile-run-{n}.log")
    214                with open(log) as f:
    215                    for line in f.readlines():
    216                        if "LLVM Profile Error" in line:
    217                            print(f"Error [{log}]: '{line.strip()}'")
    218                            should_err = True
    219 
    220            if should_err:
    221                print("Found some LLVM Profile Error in logs, see above.")
    222                sys.exit(1)
    223 
    224        # Try to move the crash reports to the artifacts even if Firefox appears
    225        # to exit successfully, in case there's a crash that doesn't set the
    226        # return code to non-zero for some reason.
    227        if get_crashreports(profilePath, name="Firefox exited successfully?") != 0:
    228            print("Firefox exited successfully, but produced a crashreport")
    229            sys.exit(1)
    230 
    231        llvm_profdata = env.get("LLVM_PROFDATA")
    232        if llvm_profdata:
    233            profraw_files = glob.glob("*.profraw")
    234            if not profraw_files:
    235                print(
    236                    "Could not find profraw files in the current directory: %s"
    237                    % os.getcwd()
    238                )
    239                sys.exit(1)
    240 
    241            merged_profdata = "merged.profdata"
    242            merge_cmd = [
    243                llvm_profdata,
    244                "merge",
    245                "-o",
    246                merged_profdata,
    247            ] + profraw_files
    248            rc = subprocess.call(merge_cmd)
    249            if rc != 0:
    250                print("INFRA-ERROR: Failed to merge profile data. Corrupt profile?")
    251                # exit with TBPL_RETRY
    252                sys.exit(4)
    253 
    254            # llvm-profdata may fail while still exiting without an error.
    255            if not os.path.isfile(merged_profdata):
    256                print(merged_profdata, "was not created", file=sys.stderr)
    257                sys.exit(1)
    258 
    259            if os.path.getsize(merged_profdata) == 0:
    260                print(merged_profdata, "was created but it is empty", file=sys.stderr)
    261                sys.exit(1)