Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(55)

Unified Diff: build/android/adb_profile_chrome.py

Issue 290013006: adb_profile_chrome: Refactor into multiple modules and add tests (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Review comments. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | build/android/chrome_profiler/__init__.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: build/android/adb_profile_chrome.py
diff --git a/build/android/adb_profile_chrome.py b/build/android/adb_profile_chrome.py
index 2e25d9c40779229b1e7e0db57168981e0c4dd2b7..0270a62f153a33621bc2ac408c6743cf715f702a 100755
--- a/build/android/adb_profile_chrome.py
+++ b/build/android/adb_profile_chrome.py
@@ -4,472 +4,10 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import gzip
-import json
-import logging
-import optparse
-import os
-import re
-import select
-import shutil
import sys
-import threading
-import time
-import webbrowser
-import zipfile
-import zlib
-from pylib import android_commands
-from pylib import cmd_helper
-from pylib import constants
-from pylib import pexpect
-from pylib.device import device_utils
-
-_TRACE_VIEWER_ROOT = os.path.join(constants.DIR_SOURCE_ROOT,
- 'third_party', 'trace-viewer')
-sys.path.append(_TRACE_VIEWER_ROOT)
-from trace_viewer.build import trace2html # pylint: disable=F0401
-
-_DEFAULT_CHROME_CATEGORIES = '_DEFAULT_CHROME_CATEGORIES'
-
-
-def _GetTraceTimestamp():
- return time.strftime('%Y-%m-%d-%H%M%S', time.localtime())
-
-
-class ChromeTracingController(object):
- def __init__(self, device, package_info, categories, ring_buffer):
- self._device = device
- self._package_info = package_info
- self._categories = categories
- self._ring_buffer = ring_buffer
- self._trace_file = None
- self._trace_interval = None
- self._trace_start_re = \
- re.compile(r'Logging performance trace to file')
- self._trace_finish_re = \
- re.compile(r'Profiler finished[.] Results are in (.*)[.]')
- self._device.old_interface.StartMonitoringLogcat(clear=False)
-
- def __str__(self):
- return 'chrome trace'
-
- @staticmethod
- def GetCategories(device, package_info):
- device.old_interface.BroadcastIntent(
- package_info.package, 'GPU_PROFILER_LIST_CATEGORIES')
- try:
- json_category_list = device.old_interface.WaitForLogMatch(
- re.compile(r'{"traceCategoriesList(.*)'), None, timeout=5).group(0)
- except pexpect.TIMEOUT:
- raise RuntimeError('Performance trace category list marker not found. '
- 'Is the correct version of the browser running?')
-
- record_categories = []
- disabled_by_default_categories = []
- json_data = json.loads(json_category_list)['traceCategoriesList']
- for item in json_data:
- if item.startswith('disabled-by-default'):
- disabled_by_default_categories.append(item)
- else:
- record_categories.append(item)
-
- return record_categories, disabled_by_default_categories
-
- def StartTracing(self, interval):
- self._trace_interval = interval
- self._device.old_interface.SyncLogCat()
- self._device.old_interface.BroadcastIntent(
- self._package_info.package, 'GPU_PROFILER_START',
- '-e categories "%s"' % ','.join(self._categories),
- '-e continuous' if self._ring_buffer else '')
- # Chrome logs two different messages related to tracing:
- #
- # 1. "Logging performance trace to file"
- # 2. "Profiler finished. Results are in [...]"
- #
- # The first one is printed when tracing starts and the second one indicates
- # that the trace file is ready to be pulled.
- try:
- self._device.old_interface.WaitForLogMatch(
- self._trace_start_re, None, timeout=5)
- except pexpect.TIMEOUT:
- raise RuntimeError('Trace start marker not found. Is the correct version '
- 'of the browser running?')
-
- def StopTracing(self):
- self._device.old_interface.BroadcastIntent(
- self._package_info.package,
- 'GPU_PROFILER_STOP')
- self._trace_file = self._device.old_interface.WaitForLogMatch(
- self._trace_finish_re, None, timeout=120).group(1)
-
- def PullTrace(self):
- # Wait a bit for the browser to finish writing the trace file.
- time.sleep(self._trace_interval / 4 + 1)
-
- trace_file = self._trace_file.replace('/storage/emulated/0/', '/sdcard/')
- host_file = os.path.join(os.path.curdir, os.path.basename(trace_file))
- self._device.old_interface.PullFileFromDevice(trace_file, host_file)
- return host_file
-
-
-_SYSTRACE_OPTIONS = [
- # Compress the trace before sending it over USB.
- '-z',
- # Use a large trace buffer to increase the polling interval.
- '-b', '16384'
-]
-
-# Interval in seconds for sampling systrace data.
-_SYSTRACE_INTERVAL = 15
-
-
-class SystraceController(object):
- def __init__(self, device, categories, ring_buffer):
- self._device = device
- self._categories = categories
- self._ring_buffer = ring_buffer
- self._done = threading.Event()
- self._thread = None
- self._trace_data = None
-
- def __str__(self):
- return 'systrace'
-
- @staticmethod
- def GetCategories(device):
- return device.old_interface.RunShellCommand('atrace --list_categories')
-
- def StartTracing(self, _):
- self._thread = threading.Thread(target=self._CollectData)
- self._thread.start()
-
- def StopTracing(self):
- self._done.set()
-
- def PullTrace(self):
- self._thread.join()
- self._thread = None
- if self._trace_data:
- output_name = 'systrace-%s' % _GetTraceTimestamp()
- with open(output_name, 'w') as out:
- out.write(self._trace_data)
- return output_name
-
- def _RunATraceCommand(self, command):
- # TODO(jbudorick) can this be made work with DeviceUtils?
- # We use a separate interface to adb because the one from AndroidCommands
- # isn't re-entrant.
- device_param = (['-s', self._device.old_interface.GetDevice()]
- if self._device.old_interface.GetDevice() else [])
- cmd = ['adb'] + device_param + ['shell', 'atrace', '--%s' % command] + \
- _SYSTRACE_OPTIONS + self._categories
- return cmd_helper.GetCmdOutput(cmd)
-
- def _CollectData(self):
- trace_data = []
- self._RunATraceCommand('async_start')
- try:
- while not self._done.is_set():
- self._done.wait(_SYSTRACE_INTERVAL)
- if not self._ring_buffer or self._done.is_set():
- trace_data.append(
- self._DecodeTraceData(self._RunATraceCommand('async_dump')))
- finally:
- trace_data.append(
- self._DecodeTraceData(self._RunATraceCommand('async_stop')))
- self._trace_data = ''.join([zlib.decompress(d) for d in trace_data])
-
- @staticmethod
- def _DecodeTraceData(trace_data):
- try:
- trace_start = trace_data.index('TRACE:')
- except ValueError:
- raise RuntimeError('Systrace start marker not found')
- trace_data = trace_data[trace_start + 6:]
-
- # Collapse CRLFs that are added by adb shell.
- if trace_data.startswith('\r\n'):
- trace_data = trace_data.replace('\r\n', '\n')
-
- # Skip the initial newline.
- return trace_data[1:]
-
-
-def _GetSupportedBrowsers():
- # Add aliases for backwards compatibility.
- supported_browsers = {
- 'stable': constants.PACKAGE_INFO['chrome_stable'],
- 'beta': constants.PACKAGE_INFO['chrome_beta'],
- 'dev': constants.PACKAGE_INFO['chrome_dev'],
- 'build': constants.PACKAGE_INFO['chrome'],
- }
- supported_browsers.update(constants.PACKAGE_INFO)
- unsupported_browsers = ['content_browsertests', 'gtest', 'legacy_browser']
- for browser in unsupported_browsers:
- del supported_browsers[browser]
- return supported_browsers
-
-
-def _CompressFile(host_file, output):
- with gzip.open(output, 'wb') as out:
- with open(host_file, 'rb') as input_file:
- out.write(input_file.read())
- os.unlink(host_file)
-
-
-def _ArchiveFiles(host_files, output):
- with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as z:
- for host_file in host_files:
- z.write(host_file)
- os.unlink(host_file)
-
-
-def _PackageTracesAsHtml(trace_files, html_file):
- with open(html_file, 'w') as f:
- trace2html.WriteHTMLForTracesToFile(trace_files, f)
- for trace_file in trace_files:
- os.unlink(trace_file)
-
-
-def _PrintMessage(heading, eol='\n'):
- sys.stdout.write('%s%s' % (heading, eol))
- sys.stdout.flush()
-
-
-def _WaitForEnter(timeout):
- select.select([sys.stdin], [], [], timeout)
-
-
-def _StartTracing(controllers, interval):
- for controller in controllers:
- controller.StartTracing(interval)
-
-
-def _StopTracing(controllers):
- for controller in controllers:
- controller.StopTracing()
-
-
-def _PullTraces(controllers, output, compress, write_json):
- _PrintMessage('Downloading...', eol='')
- trace_files = []
- for controller in controllers:
- trace_files.append(controller.PullTrace())
-
- if not write_json:
- html_file = os.path.splitext(trace_files[0])[0] + '.html'
- _PackageTracesAsHtml(trace_files, html_file)
- trace_files = [html_file]
-
- if compress and len(trace_files) == 1:
- result = output or trace_files[0] + '.gz'
- _CompressFile(trace_files[0], result)
- elif len(trace_files) > 1:
- result = output or 'chrome-combined-trace-%s.zip' % _GetTraceTimestamp()
- _ArchiveFiles(trace_files, result)
- elif output:
- result = output
- shutil.move(trace_files[0], result)
- else:
- result = trace_files[0]
-
- _PrintMessage('done')
- _PrintMessage('Trace written to file://%s' % os.path.abspath(result))
- return result
-
-
-def _CaptureAndPullTrace(controllers, interval, output, compress, write_json):
- trace_type = ' + '.join(map(str, controllers))
- try:
- _StartTracing(controllers, interval)
- if interval:
- _PrintMessage('Capturing %d-second %s. Press Enter to stop early...' % \
- (interval, trace_type), eol='')
- _WaitForEnter(interval)
- else:
- _PrintMessage('Capturing %s. Press Enter to stop...' % trace_type, eol='')
- raw_input()
- finally:
- _StopTracing(controllers)
- if interval:
- _PrintMessage('done')
-
- return _PullTraces(controllers, output, compress, write_json)
-
-
-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.chrome_categories:
- categories += options.chrome_categories.split(',')
- return categories
-
-
-def _ComputeSystraceCategories(options):
- if not options.systrace_categories:
- return []
- return options.systrace_categories.split(',')
-
-
-def main():
- 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)
-
- categories = optparse.OptionGroup(parser, 'Trace categories')
- categories.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)
- categories.add_option('-s', '--systrace', help='Capture a systrace with the '
- 'chosen comma-delimited systrace categories. You can '
- 'also capture a combined Chrome + systrace by enabling '
- 'both types of categories. Use "list" to see the '
- 'available categories. Systrace is disabled by '
- 'default.', metavar='SYS_CATEGORIES',
- dest='systrace_categories', default='')
- categories.add_option('--trace-cc',
- help='Deprecated, use --trace-frame-viewer.',
- action='store_true')
- categories.add_option('--trace-frame-viewer',
- help='Enable enough trace categories for '
- 'compositor frame viewing.', action='store_true')
- categories.add_option('--trace-ubercompositor',
- help='Enable enough trace categories for '
- 'ubercompositor frame data.', action='store_true')
- categories.add_option('--trace-gpu', help='Enable extra trace categories for '
- 'GPU data.', action='store_true')
- categories.add_option('--trace-flow', help='Enable extra trace categories '
- 'for IPC message flows.', action='store_true')
- parser.add_option_group(categories)
-
- 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(_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')
- 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 much be attached.')
- device = device_utils.DeviceUtils(devices[0])
- package_info = _GetSupportedBrowsers()[options.browser]
-
- if options.chrome_categories in ['list', 'help']:
- _PrintMessage('Collecting record categories list...', eol='')
- record_categories = []
- disabled_by_default_categories = []
- record_categories, disabled_by_default_categories = \
- ChromeTracingController.GetCategories(device, package_info)
-
- _PrintMessage('done')
- _PrintMessage('Record Categories:')
- _PrintMessage('\n'.join('\t%s' % item \
- for item in sorted(record_categories)))
-
- _PrintMessage('\nDisabled by Default Categories:')
- _PrintMessage('\n'.join('\t%s' % item \
- for item in sorted(disabled_by_default_categories)))
-
- return 0
-
- if options.systrace_categories in ['list', 'help']:
- _PrintMessage('\n'.join(SystraceController.GetCategories(device)))
- return 0
-
- if not options.time and not options.continuous:
- _PrintMessage('Time interval or continuous tracing should be specified.')
- return 1
-
- chrome_categories = _ComputeChromeCategories(options)
- systrace_categories = _ComputeSystraceCategories(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.')
-
- controllers = []
- if chrome_categories:
- controllers.append(ChromeTracingController(device,
- package_info,
- chrome_categories,
- options.ring_buffer))
- if systrace_categories:
- controllers.append(SystraceController(device,
- systrace_categories,
- options.ring_buffer))
-
- if not controllers:
- _PrintMessage('No trace categories enabled.')
- return 1
-
- if options.output:
- options.output = os.path.expanduser(options.output)
- result = _CaptureAndPullTrace(controllers,
- options.time if not options.continuous else 0,
- options.output,
- options.compress,
- options.json)
- if options.view:
- if sys.platform == 'darwin':
- os.system('/usr/bin/open %s' % os.path.abspath(result))
- else:
- webbrowser.open(result)
+from chrome_profiler import main
if __name__ == '__main__':
- sys.exit(main())
+ sys.exit(main.main())
« no previous file with comments | « no previous file | build/android/chrome_profiler/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698