tor-browser

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

setup-startup-profiling.py (3891B)


      1 #!/usr/bin/env python3
      2 # This Source Code Form is subject to the terms of the Mozilla Public
      3 # License, v. 2.0. If a copy of the MPL was not distributed with this
      4 # file, You can obtain one at https://mozilla.org/MPL/2.0/.
      5 
      6 """
      7 A script to set up startup profiling with the Firefox Profiler. See
      8 https://profiler.firefox.com/docs/#/./guide-remote-profiling?id=startup-profiling
      9 for more information.
     10 """
     11 
     12 import argparse
     13 import os
     14 import tempfile
     15 from subprocess import run
     16 
     17 PATH_PREFIX = "/data/local/tmp"
     18 
     19 PROD_FENIX = "fenix"
     20 PROD_GVE = "geckoview_example"
     21 PRODUCTS = [PROD_FENIX, PROD_GVE]
     22 
     23 GV_CONFIG = b"""env:
     24  MOZ_PROFILER_STARTUP: 1
     25  MOZ_PROFILER_STARTUP_INTERVAL: 5
     26  MOZ_PROFILER_STARTUP_FEATURES: js,stackwalk,screenshots,ipcmessages,java,cpu,memory
     27  MOZ_PROFILER_STARTUP_FILTERS: GeckoMain,Compositor,Renderer,IPDL Background
     28 """
     29 
     30 
     31 def parse_args():
     32    p = argparse.ArgumentParser(
     33        description=(
     34            "Easily enable start up profiling using the Firefox Profiler. Finish capturing the profile in "
     35            "about:debugging on desktop. See "
     36            "https://profiler.firefox.com/docs/#/./guide-remote-profiling?id=startup-profiling for "
     37            "details."
     38        )
     39    )
     40    p.add_argument(
     41        "command",
     42        choices=["activate", "deactivate"],
     43        help=(
     44            "whether to activate or deactive start up "
     45            "profiling for the given release channel"
     46        ),
     47    )
     48    p.add_argument(
     49        "release_channel",
     50        choices=["nightly", "beta", "release", "debug"],
     51        help=(
     52            "the release channel to "
     53            "change the startup profiling state of the command on"
     54        ),
     55    )
     56 
     57    p.add_argument(
     58        "-p",
     59        "--product",
     60        choices=PRODUCTS,
     61        default=PROD_FENIX,
     62        help="which product to work on",
     63    )
     64    return p.parse_args()
     65 
     66 
     67 def push(id, filename):
     68    config = tempfile.NamedTemporaryFile(delete=False)
     69    try:
     70        # I think the file needs to be closed to save its contents for adb push to
     71        # work correctly so we close it here and later delete it manually.
     72        with config.file as f:
     73            f.write(GV_CONFIG)
     74 
     75        print(f"Pushing {filename} to device.")
     76        run(
     77            ["adb", "push", config.name, os.path.join(PATH_PREFIX, filename)],
     78            check=True,
     79        )
     80        run(["adb", "shell", "am", "set-debug-app", "--persistent", id], check=True)
     81        print(
     82            "\nStartup profiling enabled on all future start ups, possibly even after reinstall."
     83        )
     84        print("Call script with `deactivate` to disable it.")
     85        print(
     86            "DISABLE 'Remote debugging via USB' IN THE APP SETTINGS BEFORE STARTING THE APP & RE-ENABLE TO CAPTURE THE PROFILE.",
     87            "This avoids the additional overhead added when 'Remote debugging via USB' is enabled during start up.",
     88            sep=os.linesep,
     89        )
     90    finally:
     91        os.remove(config.name)
     92 
     93 
     94 def remove(filename):
     95    print(f"Removing {filename} from device.")
     96    run(["adb", "shell", "rm", PATH_PREFIX + "/" + filename], check=True)
     97    run(["adb", "shell", "am", "clear-debug-app"], check=True)
     98 
     99 
    100 def convert_channel_to_id(product, channel):
    101    if product == PROD_FENIX:
    102        mapping = {
    103            "release": "org.mozilla.firefox",
    104            "beta": "org.mozilla.firefox_beta",
    105            "nightly": "org.mozilla.fenix",
    106            "debug": "org.mozilla.fenix.debug",
    107        }
    108        return mapping[channel]
    109    elif product == PROD_GVE:
    110        return "org.mozilla.geckoview_example"
    111 
    112 
    113 def main():
    114    args = parse_args()
    115 
    116    id = convert_channel_to_id(args.product, args.release_channel)
    117    filename = id + "-geckoview-config.yaml"
    118 
    119    if args.command == "activate":
    120        push(id, filename)
    121    elif args.command == "deactivate":
    122        remove(filename)
    123 
    124 
    125 if __name__ == "__main__":
    126    main()