Home | History | Annotate | Download | only in profile_chrome
      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