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