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()