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

Unified Diff: build/android/adb_profile_chrome_startup.py

Issue 263163002: Add adb_profile_chrome_startup.py. Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: 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 | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: build/android/adb_profile_chrome_startup.py
diff --git a/build/android/adb_profile_chrome_startup.py b/build/android/adb_profile_chrome_startup.py
new file mode 100755
index 0000000000000000000000000000000000000000..83428edafea46a935b66b2945ea90cd09845002c
--- /dev/null
+++ b/build/android/adb_profile_chrome_startup.py
@@ -0,0 +1,294 @@
+#!/usr/bin/env python
+#
+# Copyright 2013 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 gzip
+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
+
+_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
+
+
+def _GetTraceTimestamp():
+ return time.strftime('%Y-%m-%d-%H%M%S', time.localtime())
+
+
+class ChromeTracingController(object):
+ def __init__(self, adb):
+ self._adb = adb
+ self._trace_start_re = \
+ re.compile(r'Logging performance trace to file: (.*)')
+ self._trace_finish_re = \
+ re.compile(r'Profiler finished[.] Results are in (.*)[.]')
+
+ def __str__(self):
+ return 'chrome trace'
+
+ def StartTracing(self):
+ pass
+
+ def StopTracing(self):
+ pass
+
+ def PullTrace(self):
+ # Wait a bit for the browser to finish writing the trace file.
+ time.sleep(3)
+
+ trace_file = '/data/data/com.google.android.apps.chrome/cache/chrome-trace'
+ host_file = os.path.join(os.path.curdir, os.path.basename(trace_file))
+ self._adb.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, adb, categories):
+ self._adb = adb
+ self._categories = categories
+ self._done = threading.Event()
+ self._thread = None
+ self._trace_data = None
+
+ def __str__(self):
+ return 'systrace'
+
+ @staticmethod
+ def GetCategories(adb):
+ return adb.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):
+ # We use a separate interface to adb because the one from AndroidCommands
+ # isn't re-entrant.
+ device = ['-s', self._adb.GetDevice()] if self._adb.GetDevice() else []
+ cmd = ['adb'] + device + ['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 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 _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):
+ print "_ArchiveFiles"
+ 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):
+ for controller in controllers:
+ controller.StartTracing()
+ time.sleep(1)
+
+
+def _StopTracing(controllers):
+ for controller in controllers:
+ controller.StopTracing()
+
+
+def _PullTraces(controllers, output, 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 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(adb, controllers, output, write_json):
+ trace_type = ' + '.join(map(str, controllers))
+ try:
+ adb.RunShellCommand(
+ 'echo "chrome --trace-startup '
+ '--trace-startup-file='
+ '/data/data/com.google.android.apps.chrome/cache/chrome-trace"'
+ '> /data/local/chrome-command-line')
+
+ adb.RunShellCommand('echo 1 > /proc/sys/vm/drop_caches')
+ _StartTracing(controllers)
+ adb.RunShellCommand(
+ 'am start -a android.intent.action.VIEW -n '
+ 'com.google.android.apps.chrome/.Main -d google.com')
+ _PrintMessage('Capturing %s. Press Enter to stop...' % trace_type, eol='')
+ raw_input()
+ finally:
+ _StopTracing(controllers)
+ _PrintMessage('done')
+
+ return _PullTraces(controllers, output, write_json)
+
+
+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.')
+
+
+ categories = optparse.OptionGroup(parser, 'Trace 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-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)
+
+ parser.add_option('-v', '--verbose', help='Verbose logging.',
+ action='store_true')
+ options, _args = parser.parse_args()
+
+ if options.verbose:
+ logging.getLogger().setLevel(logging.DEBUG)
+
+ adb = android_commands.AndroidCommands()
+ if options.systrace_categories in ['list', 'help']:
+ _PrintMessage('\n'.join(SystraceController.GetCategories(adb)))
+ return 0
+
+ systrace_categories = _ComputeSystraceCategories(options)
+
+ controllers = []
+ controllers.append(SystraceController(adb, systrace_categories))
+ controllers.append(ChromeTracingController(adb))
+
+ if options.output:
+ options.output = os.path.expanduser(options.output)
+ result = _CaptureAndPullTrace(adb, controllers, options.output, options.json)
+ if options.view:
+ if sys.platform == 'darwin':
+ os.system('/usr/bin/open %s' % os.path.abspath(result))
+ else:
+ webbrowser.open(result)
+
+
+if __name__ == '__main__':
+ sys.exit(main())
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698