1 #!/usr/bin/env python 2 # 3 # Copyright 2014 The Chromium Authors. All rights reserved. 4 # Use of this source code is governed by a BSD-style license that can be 5 # found in the LICENSE file. 6 7 import logging 8 import optparse 9 import os 10 import sys 11 import webbrowser 12 13 from adb_profile_chrome import chrome_controller 14 from adb_profile_chrome import perf_controller 15 from adb_profile_chrome import profiler 16 from adb_profile_chrome import systrace_controller 17 from adb_profile_chrome import ui 18 19 from pylib import android_commands 20 from pylib.device import device_utils 21 22 23 _DEFAULT_CHROME_CATEGORIES = '_DEFAULT_CHROME_CATEGORIES' 24 25 26 def _ComputeChromeCategories(options): 27 categories = [] 28 if options.trace_frame_viewer: 29 categories.append('disabled-by-default-cc.debug') 30 if options.trace_ubercompositor: 31 categories.append('disabled-by-default-cc.debug*') 32 if options.trace_gpu: 33 categories.append('disabled-by-default-gpu.debug*') 34 if options.trace_flow: 35 categories.append('disabled-by-default-toplevel.flow') 36 if options.trace_memory: 37 categories.append('disabled-by-default-memory') 38 if options.chrome_categories: 39 categories += options.chrome_categories.split(',') 40 return categories 41 42 43 def _ComputeSystraceCategories(options): 44 if not options.systrace_categories: 45 return [] 46 return options.systrace_categories.split(',') 47 48 49 def _ComputePerfCategories(options): 50 if not options.perf_categories: 51 return [] 52 return options.perf_categories.split(',') 53 54 55 def _OptionalValueCallback(default_value): 56 def callback(option, _, __, parser): 57 value = default_value 58 if parser.rargs and not parser.rargs[0].startswith('-'): 59 value = parser.rargs.pop(0) 60 setattr(parser.values, option.dest, value) 61 return callback 62 63 64 def _CreateOptionParser(): 65 parser = optparse.OptionParser(description='Record about://tracing profiles ' 66 'from Android browsers. See http://dev.' 67 'chromium.org/developers/how-tos/trace-event-' 68 'profiling-tool for detailed instructions for ' 69 'profiling.') 70 71 timed_options = optparse.OptionGroup(parser, 'Timed tracing') 72 timed_options.add_option('-t', '--time', help='Profile for N seconds and ' 73 'download the resulting trace.', metavar='N', 74 type='float') 75 parser.add_option_group(timed_options) 76 77 cont_options = optparse.OptionGroup(parser, 'Continuous tracing') 78 cont_options.add_option('--continuous', help='Profile continuously until ' 79 'stopped.', action='store_true') 80 cont_options.add_option('--ring-buffer', help='Use the trace buffer as a ' 81 'ring buffer and save its contents when stopping ' 82 'instead of appending events into one long trace.', 83 action='store_true') 84 parser.add_option_group(cont_options) 85 86 chrome_opts = optparse.OptionGroup(parser, 'Chrome tracing options') 87 chrome_opts.add_option('-c', '--categories', help='Select Chrome tracing ' 88 'categories with comma-delimited wildcards, ' 89 'e.g., "*", "cat1*,-cat1a". Omit this option to trace ' 90 'Chrome\'s default categories. Chrome tracing can be ' 91 'disabled with "--categories=\'\'". Use "list" to ' 92 'see the available categories.', 93 metavar='CHROME_CATEGORIES', dest='chrome_categories', 94 default=_DEFAULT_CHROME_CATEGORIES) 95 chrome_opts.add_option('--trace-cc', 96 help='Deprecated, use --trace-frame-viewer.', 97 action='store_true') 98 chrome_opts.add_option('--trace-frame-viewer', 99 help='Enable enough trace categories for ' 100 'compositor frame viewing.', action='store_true') 101 chrome_opts.add_option('--trace-ubercompositor', 102 help='Enable enough trace categories for ' 103 'ubercompositor frame data.', action='store_true') 104 chrome_opts.add_option('--trace-gpu', help='Enable extra trace categories ' 105 'for GPU data.', action='store_true') 106 chrome_opts.add_option('--trace-flow', help='Enable extra trace categories ' 107 'for IPC message flows.', action='store_true') 108 chrome_opts.add_option('--trace-memory', help='Enable extra trace categories ' 109 'for memory profile. (tcmalloc required)', 110 action='store_true') 111 parser.add_option_group(chrome_opts) 112 113 systrace_opts = optparse.OptionGroup(parser, 'Systrace tracing options') 114 systrace_opts.add_option('-s', '--systrace', help='Capture a systrace with ' 115 'the chosen comma-delimited systrace categories. You ' 116 'can also capture a combined Chrome + systrace by ' 117 'enable both types of categories. Use "list" to see ' 118 'the available categories. Systrace is disabled by ' 119 'default.', metavar='SYS_CATEGORIES', 120 dest='systrace_categories', default='') 121 parser.add_option_group(systrace_opts) 122 123 if perf_controller.PerfProfilerController.IsSupported(): 124 perf_opts = optparse.OptionGroup(parser, 'Perf profiling options') 125 perf_opts.add_option('-p', '--perf', help='Capture a perf profile with ' 126 'the chosen comma-delimited event categories. ' 127 'Samples CPU cycles by default. Use "list" to see ' 128 'the available sample types.', action='callback', 129 default='', callback=_OptionalValueCallback('cycles'), 130 metavar='PERF_CATEGORIES', dest='perf_categories') 131 parser.add_option_group(perf_opts) 132 133 output_options = optparse.OptionGroup(parser, 'Output options') 134 output_options.add_option('-o', '--output', help='Save trace output to file.') 135 output_options.add_option('--json', help='Save trace as raw JSON instead of ' 136 'HTML.', action='store_true') 137 output_options.add_option('--view', help='Open resulting trace file in a ' 138 'browser.', action='store_true') 139 parser.add_option_group(output_options) 140 141 browsers = sorted(profiler.GetSupportedBrowsers().keys()) 142 parser.add_option('-b', '--browser', help='Select among installed browsers. ' 143 'One of ' + ', '.join(browsers) + ', "stable" is used by ' 144 'default.', type='choice', choices=browsers, 145 default='stable') 146 parser.add_option('-v', '--verbose', help='Verbose logging.', 147 action='store_true') 148 parser.add_option('-z', '--compress', help='Compress the resulting trace ' 149 'with gzip. ', action='store_true') 150 return parser 151 152 153 def main(): 154 parser = _CreateOptionParser() 155 options, _args = parser.parse_args() 156 if options.trace_cc: 157 parser.parse_error("""--trace-cc is deprecated. 158 159 For basic jank busting uses, use --trace-frame-viewer 160 For detailed study of ubercompositor, pass --trace-ubercompositor. 161 162 When in doubt, just try out --trace-frame-viewer. 163 """) 164 165 if options.verbose: 166 logging.getLogger().setLevel(logging.DEBUG) 167 168 devices = android_commands.GetAttachedDevices() 169 if len(devices) != 1: 170 parser.error('Exactly 1 device must be attached.') 171 device = device_utils.DeviceUtils(devices[0]) 172 package_info = profiler.GetSupportedBrowsers()[options.browser] 173 174 if options.chrome_categories in ['list', 'help']: 175 ui.PrintMessage('Collecting record categories list...', eol='') 176 record_categories = [] 177 disabled_by_default_categories = [] 178 record_categories, disabled_by_default_categories = \ 179 chrome_controller.ChromeTracingController.GetCategories( 180 device, package_info) 181 182 ui.PrintMessage('done') 183 ui.PrintMessage('Record Categories:') 184 ui.PrintMessage('\n'.join('\t%s' % item \ 185 for item in sorted(record_categories))) 186 187 ui.PrintMessage('\nDisabled by Default Categories:') 188 ui.PrintMessage('\n'.join('\t%s' % item \ 189 for item in sorted(disabled_by_default_categories))) 190 191 return 0 192 193 if options.systrace_categories in ['list', 'help']: 194 ui.PrintMessage('\n'.join( 195 systrace_controller.SystraceController.GetCategories(device))) 196 return 0 197 198 if options.perf_categories in ['list', 'help']: 199 ui.PrintMessage('\n'.join( 200 perf_controller.PerfProfilerController.GetCategories(device))) 201 return 0 202 203 if not options.time and not options.continuous: 204 ui.PrintMessage('Time interval or continuous tracing should be specified.') 205 return 1 206 207 chrome_categories = _ComputeChromeCategories(options) 208 systrace_categories = _ComputeSystraceCategories(options) 209 perf_categories = _ComputePerfCategories(options) 210 211 if chrome_categories and 'webview' in systrace_categories: 212 logging.warning('Using the "webview" category in systrace together with ' 213 'Chrome tracing results in duplicate trace events.') 214 215 enabled_controllers = [] 216 if chrome_categories: 217 enabled_controllers.append( 218 chrome_controller.ChromeTracingController(device, 219 package_info, 220 chrome_categories, 221 options.ring_buffer, 222 options.trace_memory)) 223 if systrace_categories: 224 enabled_controllers.append( 225 systrace_controller.SystraceController(device, 226 systrace_categories, 227 options.ring_buffer)) 228 229 if perf_categories: 230 enabled_controllers.append( 231 perf_controller.PerfProfilerController(device, 232 perf_categories)) 233 234 if not enabled_controllers: 235 ui.PrintMessage('No trace categories enabled.') 236 return 1 237 238 if options.output: 239 options.output = os.path.expanduser(options.output) 240 result = profiler.CaptureProfile( 241 enabled_controllers, 242 options.time if not options.continuous else 0, 243 output=options.output, 244 compress=options.compress, 245 write_json=options.json) 246 if options.view: 247 if sys.platform == 'darwin': 248 os.system('/usr/bin/open %s' % os.path.abspath(result)) 249 else: 250 webbrowser.open(result) 251