#!/usr/bin/env python3 # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. """ A script to set up startup profiling with the Firefox Profiler. See https://profiler.firefox.com/docs/#/./guide-remote-profiling?id=startup-profiling for more information. """ import argparse import os import tempfile from subprocess import run PATH_PREFIX = "/data/local/tmp" PROD_FENIX = "fenix" PROD_GVE = "geckoview_example" PRODUCTS = [PROD_FENIX, PROD_GVE] GV_CONFIG = b"""env: MOZ_PROFILER_STARTUP: 1 MOZ_PROFILER_STARTUP_INTERVAL: 5 MOZ_PROFILER_STARTUP_FEATURES: js,stackwalk,leaf,screenshots,ipcmessages,java,cpu,memory MOZ_PROFILER_STARTUP_FILTERS: GeckoMain,Compositor,Renderer,IPDL Background """ def parse_args(): p = argparse.ArgumentParser( description=( "Easily enable start up profiling using the Firefox Profiler. Finish capturing the profile in " "about:debugging on desktop. See " "https://profiler.firefox.com/docs/#/./guide-remote-profiling?id=startup-profiling for " "details." ) ) p.add_argument( "command", choices=["activate", "deactivate"], help=( "whether to activate or deactive start up " "profiling for the given release channel" ), ) p.add_argument( "release_channel", choices=["nightly", "beta", "release", "debug"], help=( "the release channel to " "change the startup profiling state of the command on" ), ) p.add_argument( "-p", "--product", choices=PRODUCTS, default=PROD_FENIX, help="which product to work on", ) return p.parse_args() def push(id, filename): config = tempfile.NamedTemporaryFile(delete=False) try: # I think the file needs to be closed to save its contents for adb push to # work correctly so we close it here and later delete it manually. with config.file as f: f.write(GV_CONFIG) print("Pushing {} to device.".format(filename)) run(["adb", "push", config.name, os.path.join(PATH_PREFIX, filename)]) run(["adb", "shell", "am", "set-debug-app", "--persistent", id]) print( "\nStartup profiling enabled on all future start ups, possibly even after reinstall." ) print("Call script with `deactivate` to disable it.") print( "DISABLE 'Remote debugging via USB' IN THE APP SETTINGS BEFORE STARTING THE APP & RE-ENABLE TO CAPTURE THE PROFILE.", "This avoids the additional overhead added when 'Remote debugging via USB' is enabled during start up.", sep=os.linesep, ) finally: os.remove(config.name) def remove(filename): print("Removing {} from device.".format(filename)) run(["adb", "shell", "rm", PATH_PREFIX + "/" + filename]) run(["adb", "shell", "am", "clear-debug-app"]) def convert_channel_to_id(product, channel): if product == PROD_FENIX: mapping = { "release": "org.mozilla.firefox", "beta": "org.mozilla.firefox_beta", "nightly": "org.mozilla.fenix", "debug": "org.mozilla.fenix.debug", } return mapping[channel] elif product == PROD_GVE: return "org.mozilla.geckoview_example" def main(): args = parse_args() id = convert_channel_to_id(args.product, args.release_channel) filename = id + "-geckoview-config.yaml" if args.command == "activate": push(id, filename) elif args.command == "deactivate": remove(filename) if __name__ == "__main__": main()