#!/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 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()