• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7import logging
8import optparse
9import os
10import sys
11import webbrowser
12
13from adb_profile_chrome import chrome_controller
14from adb_profile_chrome import perf_controller
15from adb_profile_chrome import profiler
16from adb_profile_chrome import systrace_controller
17from adb_profile_chrome import ui
18
19from pylib import android_commands
20from pylib.device import device_utils
21
22
23_DEFAULT_CHROME_CATEGORIES = '_DEFAULT_CHROME_CATEGORIES'
24
25
26def _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
43def _ComputeSystraceCategories(options):
44  if not options.systrace_categories:
45    return []
46  return options.systrace_categories.split(',')
47
48
49def _ComputePerfCategories(options):
50  if not options.perf_categories:
51    return []
52  return options.perf_categories.split(',')
53
54
55def _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
64def _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
153def main():
154  parser = _CreateOptionParser()
155  options, _args = parser.parse_args()
156  if options.trace_cc:
157    parser.parse_error("""--trace-cc is deprecated.
158
159For basic jank busting uses, use  --trace-frame-viewer
160For detailed study of ubercompositor, pass --trace-ubercompositor.
161
162When 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