| #!/usr/bin/env python |
| # |
| # Copyright 2014 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| import optparse |
| import os |
| import sys |
| import webbrowser |
| |
| from profile_chrome import chrome_controller |
| from profile_chrome import perf_controller |
| from profile_chrome import profiler |
| from profile_chrome import systrace_controller |
| from profile_chrome import ui |
| |
| from pylib import android_commands |
| from pylib.device import device_utils |
| |
| |
| _DEFAULT_CHROME_CATEGORIES = '_DEFAULT_CHROME_CATEGORIES' |
| |
| |
| def _ComputeChromeCategories(options): |
| categories = [] |
| if options.trace_frame_viewer: |
| categories.append('disabled-by-default-cc.debug') |
| if options.trace_ubercompositor: |
| categories.append('disabled-by-default-cc.debug*') |
| if options.trace_gpu: |
| categories.append('disabled-by-default-gpu.debug*') |
| if options.trace_flow: |
| categories.append('disabled-by-default-toplevel.flow') |
| if options.trace_memory: |
| categories.append('disabled-by-default-memory') |
| if options.chrome_categories: |
| categories += options.chrome_categories.split(',') |
| return categories |
| |
| |
| def _ComputeSystraceCategories(options): |
| if not options.systrace_categories: |
| return [] |
| return options.systrace_categories.split(',') |
| |
| |
| def _ComputePerfCategories(options): |
| if not perf_controller.PerfProfilerController.IsSupported(): |
| return [] |
| if not options.perf_categories: |
| return [] |
| return options.perf_categories.split(',') |
| |
| |
| def _OptionalValueCallback(default_value): |
| def callback(option, _, __, parser): |
| value = default_value |
| if parser.rargs and not parser.rargs[0].startswith('-'): |
| value = parser.rargs.pop(0) |
| setattr(parser.values, option.dest, value) |
| return callback |
| |
| |
| def _CreateOptionParser(): |
| parser = optparse.OptionParser(description='Record about://tracing profiles ' |
| 'from Android browsers. See http://dev.' |
| 'chromium.org/developers/how-tos/trace-event-' |
| 'profiling-tool for detailed instructions for ' |
| 'profiling.') |
| |
| timed_options = optparse.OptionGroup(parser, 'Timed tracing') |
| timed_options.add_option('-t', '--time', help='Profile for N seconds and ' |
| 'download the resulting trace.', metavar='N', |
| type='float') |
| parser.add_option_group(timed_options) |
| |
| cont_options = optparse.OptionGroup(parser, 'Continuous tracing') |
| cont_options.add_option('--continuous', help='Profile continuously until ' |
| 'stopped.', action='store_true') |
| cont_options.add_option('--ring-buffer', help='Use the trace buffer as a ' |
| 'ring buffer and save its contents when stopping ' |
| 'instead of appending events into one long trace.', |
| action='store_true') |
| parser.add_option_group(cont_options) |
| |
| chrome_opts = optparse.OptionGroup(parser, 'Chrome tracing options') |
| chrome_opts.add_option('-c', '--categories', help='Select Chrome tracing ' |
| 'categories with comma-delimited wildcards, ' |
| 'e.g., "*", "cat1*,-cat1a". Omit this option to trace ' |
| 'Chrome\'s default categories. Chrome tracing can be ' |
| 'disabled with "--categories=\'\'". Use "list" to ' |
| 'see the available categories.', |
| metavar='CHROME_CATEGORIES', dest='chrome_categories', |
| default=_DEFAULT_CHROME_CATEGORIES) |
| chrome_opts.add_option('--trace-cc', |
| help='Deprecated, use --trace-frame-viewer.', |
| action='store_true') |
| chrome_opts.add_option('--trace-frame-viewer', |
| help='Enable enough trace categories for ' |
| 'compositor frame viewing.', action='store_true') |
| chrome_opts.add_option('--trace-ubercompositor', |
| help='Enable enough trace categories for ' |
| 'ubercompositor frame data.', action='store_true') |
| chrome_opts.add_option('--trace-gpu', help='Enable extra trace categories ' |
| 'for GPU data.', action='store_true') |
| chrome_opts.add_option('--trace-flow', help='Enable extra trace categories ' |
| 'for IPC message flows.', action='store_true') |
| chrome_opts.add_option('--trace-memory', help='Enable extra trace categories ' |
| 'for memory profile. (tcmalloc required)', |
| action='store_true') |
| parser.add_option_group(chrome_opts) |
| |
| systrace_opts = optparse.OptionGroup(parser, 'Systrace tracing options') |
| systrace_opts.add_option('-s', '--systrace', help='Capture a systrace with ' |
| 'the chosen comma-delimited systrace categories. You ' |
| 'can also capture a combined Chrome + systrace by ' |
| 'enable both types of categories. Use "list" to see ' |
| 'the available categories. Systrace is disabled by ' |
| 'default.', metavar='SYS_CATEGORIES', |
| dest='systrace_categories', default='') |
| parser.add_option_group(systrace_opts) |
| |
| if perf_controller.PerfProfilerController.IsSupported(): |
| perf_opts = optparse.OptionGroup(parser, 'Perf profiling options') |
| perf_opts.add_option('-p', '--perf', help='Capture a perf profile with ' |
| 'the chosen comma-delimited event categories. ' |
| 'Samples CPU cycles by default. Use "list" to see ' |
| 'the available sample types.', action='callback', |
| default='', callback=_OptionalValueCallback('cycles'), |
| metavar='PERF_CATEGORIES', dest='perf_categories') |
| parser.add_option_group(perf_opts) |
| |
| output_options = optparse.OptionGroup(parser, 'Output options') |
| output_options.add_option('-o', '--output', help='Save trace output to file.') |
| output_options.add_option('--json', help='Save trace as raw JSON instead of ' |
| 'HTML.', action='store_true') |
| output_options.add_option('--view', help='Open resulting trace file in a ' |
| 'browser.', action='store_true') |
| parser.add_option_group(output_options) |
| |
| browsers = sorted(profiler.GetSupportedBrowsers().keys()) |
| parser.add_option('-b', '--browser', help='Select among installed browsers. ' |
| 'One of ' + ', '.join(browsers) + ', "stable" is used by ' |
| 'default.', type='choice', choices=browsers, |
| default='stable') |
| parser.add_option('-v', '--verbose', help='Verbose logging.', |
| action='store_true') |
| parser.add_option('-z', '--compress', help='Compress the resulting trace ' |
| 'with gzip. ', action='store_true') |
| return parser |
| |
| |
| def main(): |
| parser = _CreateOptionParser() |
| options, _args = parser.parse_args() |
| if options.trace_cc: |
| parser.parse_error("""--trace-cc is deprecated. |
| |
| For basic jank busting uses, use --trace-frame-viewer |
| For detailed study of ubercompositor, pass --trace-ubercompositor. |
| |
| When in doubt, just try out --trace-frame-viewer. |
| """) |
| |
| if options.verbose: |
| logging.getLogger().setLevel(logging.DEBUG) |
| |
| devices = android_commands.GetAttachedDevices() |
| if len(devices) != 1: |
| parser.error('Exactly 1 device must be attached.') |
| device = device_utils.DeviceUtils(devices[0]) |
| package_info = profiler.GetSupportedBrowsers()[options.browser] |
| |
| if options.chrome_categories in ['list', 'help']: |
| ui.PrintMessage('Collecting record categories list...', eol='') |
| record_categories = [] |
| disabled_by_default_categories = [] |
| record_categories, disabled_by_default_categories = \ |
| chrome_controller.ChromeTracingController.GetCategories( |
| device, package_info) |
| |
| ui.PrintMessage('done') |
| ui.PrintMessage('Record Categories:') |
| ui.PrintMessage('\n'.join('\t%s' % item \ |
| for item in sorted(record_categories))) |
| |
| ui.PrintMessage('\nDisabled by Default Categories:') |
| ui.PrintMessage('\n'.join('\t%s' % item \ |
| for item in sorted(disabled_by_default_categories))) |
| |
| return 0 |
| |
| if options.systrace_categories in ['list', 'help']: |
| ui.PrintMessage('\n'.join( |
| systrace_controller.SystraceController.GetCategories(device))) |
| return 0 |
| |
| if (perf_controller.PerfProfilerController.IsSupported() and |
| options.perf_categories in ['list', 'help']): |
| ui.PrintMessage('\n'.join( |
| perf_controller.PerfProfilerController.GetCategories(device))) |
| return 0 |
| |
| if not options.time and not options.continuous: |
| ui.PrintMessage('Time interval or continuous tracing should be specified.') |
| return 1 |
| |
| chrome_categories = _ComputeChromeCategories(options) |
| systrace_categories = _ComputeSystraceCategories(options) |
| perf_categories = _ComputePerfCategories(options) |
| |
| if chrome_categories and 'webview' in systrace_categories: |
| logging.warning('Using the "webview" category in systrace together with ' |
| 'Chrome tracing results in duplicate trace events.') |
| |
| enabled_controllers = [] |
| if chrome_categories: |
| enabled_controllers.append( |
| chrome_controller.ChromeTracingController(device, |
| package_info, |
| chrome_categories, |
| options.ring_buffer, |
| options.trace_memory)) |
| if systrace_categories: |
| enabled_controllers.append( |
| systrace_controller.SystraceController(device, |
| systrace_categories, |
| options.ring_buffer)) |
| |
| if perf_categories: |
| enabled_controllers.append( |
| perf_controller.PerfProfilerController(device, |
| perf_categories)) |
| |
| if not enabled_controllers: |
| ui.PrintMessage('No trace categories enabled.') |
| return 1 |
| |
| if options.output: |
| options.output = os.path.expanduser(options.output) |
| result = profiler.CaptureProfile( |
| enabled_controllers, |
| options.time if not options.continuous else 0, |
| output=options.output, |
| compress=options.compress, |
| write_json=options.json) |
| if options.view: |
| if sys.platform == 'darwin': |
| os.system('/usr/bin/open %s' % os.path.abspath(result)) |
| else: |
| webbrowser.open(result) |