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

Side by Side Diff: build/android/adb_profile_chrome_startup.py

Issue 686413002: Add a script to profile chrome startup over adb, optionally with a combined systrace. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 1 month 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 #
3 # Copyright 2013 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 gzip
8 import logging
9 import optparse
10 import os
11 import re
12 import select
13 import shutil
14 import sys
15 import threading
16 import time
17 import webbrowser
18 import zipfile
19 import zlib
20
21 from pylib import android_commands
22 from pylib import cmd_helper
23 from pylib import constants
24
25 _TRACE_VIEWER_ROOT = os.path.join(constants.DIR_SOURCE_ROOT,
26 'third_party', 'trace-viewer')
27 sys.path.append(_TRACE_VIEWER_ROOT)
28 from trace_viewer.build import trace2html # pylint: disable=F0401
29
30
31 def _GetTraceTimestamp():
32 return time.strftime('%Y-%m-%d-%H%M%S', time.localtime())
33
34
35 class ChromeTracingController(object):
36 def __init__(self, adb):
37 self._adb = adb
38 self._trace_start_re = \
39 re.compile(r'Logging performance trace to file: (.*)')
40 self._trace_finish_re = \
41 re.compile(r'Profiler finished[.] Results are in (.*)[.]')
42
43 def __str__(self):
44 return 'chrome trace'
45
46 def StartTracing(self):
47 pass
48
49 def StopTracing(self):
50 pass
51
52 def PullTrace(self):
53 # Wait a bit for the browser to finish writing the trace file.
54 time.sleep(3)
55
56 trace_file = '/data/data/com.google.android.apps.chrome/cache/chrome-trace'
57 host_file = os.path.join(os.path.curdir, os.path.basename(trace_file))
58 self._adb.PullFileFromDevice(trace_file, host_file)
59 return host_file
60
61
62 _SYSTRACE_OPTIONS = [
63 # Compress the trace before sending it over USB.
64 '-z',
65 # Use a large trace buffer to increase the polling interval.
66 '-b', '16384'
67 ]
68
69 # Interval in seconds for sampling systrace data.
70 _SYSTRACE_INTERVAL = 15
71
72
73 class SystraceController(object):
74 def __init__(self, adb, categories):
75 self._adb = adb
76 self._categories = categories
77 self._done = threading.Event()
78 self._thread = None
79 self._trace_data = None
80
81 def __str__(self):
82 return 'systrace'
83
84 @staticmethod
85 def GetCategories(adb):
86 return adb.RunShellCommand('atrace --list_categories')
87
88 def StartTracing(self):
89 self._thread = threading.Thread(target=self._CollectData)
90 self._thread.start()
91
92 def StopTracing(self):
93 self._done.set()
94
95 def PullTrace(self):
96 self._thread.join()
97 self._thread = None
98 if self._trace_data:
99 output_name = 'systrace-%s' % _GetTraceTimestamp()
100 with open(output_name, 'w') as out:
101 out.write(self._trace_data)
102 return output_name
103
104 def _RunATraceCommand(self, command):
105 # We use a separate interface to adb because the one from AndroidCommands
106 # isn't re-entrant.
107 device = ['-s', self._adb.GetDevice()] if self._adb.GetDevice() else []
108 cmd = ['adb'] + device + ['shell', 'atrace', '--%s' % command] + \
109 _SYSTRACE_OPTIONS + self._categories
110 return cmd_helper.GetCmdOutput(cmd)
111
112 def _CollectData(self):
113 trace_data = []
114 self._RunATraceCommand('async_start')
115 try:
116 while not self._done.is_set():
117 self._done.wait(_SYSTRACE_INTERVAL)
118 if self._done.is_set():
119 trace_data.append(
120 self._DecodeTraceData(self._RunATraceCommand('async_dump')))
121 finally:
122 trace_data.append(
123 self._DecodeTraceData(self._RunATraceCommand('async_stop')))
124 self._trace_data = ''.join([zlib.decompress(d) for d in trace_data])
125
126 @staticmethod
127 def _DecodeTraceData(trace_data):
128 try:
129 trace_start = trace_data.index('TRACE:')
130 except ValueError:
131 raise RuntimeError('Systrace start marker not found')
132 trace_data = trace_data[trace_start + 6:]
133
134 # Collapse CRLFs that are added by adb shell.
135 if trace_data.startswith('\r\n'):
136 trace_data = trace_data.replace('\r\n', '\n')
137
138 # Skip the initial newline.
139 return trace_data[1:]
140
141 def _CompressFile(host_file, output):
142 with gzip.open(output, 'wb') as out:
143 with open(host_file, 'rb') as input_file:
144 out.write(input_file.read())
145 os.unlink(host_file)
146
147
148 def _ArchiveFiles(host_files, output):
149 print "_ArchiveFiles"
150 with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as z:
151 for host_file in host_files:
152 z.write(host_file)
153 os.unlink(host_file)
154
155
156 def _PackageTracesAsHtml(trace_files, html_file):
157 with open(html_file, 'w') as f:
158 trace2html.WriteHTMLForTracesToFile(trace_files, f)
159 for trace_file in trace_files:
160 os.unlink(trace_file)
161
162
163 def _PrintMessage(heading, eol='\n'):
164 sys.stdout.write('%s%s' % (heading, eol))
165 sys.stdout.flush()
166
167
168 def _WaitForEnter(timeout):
169 select.select([sys.stdin], [], [], timeout)
170
171
172 def _StartTracing(controllers):
173 for controller in controllers:
174 controller.StartTracing()
175 time.sleep(1)
176
177
178 def _StopTracing(controllers):
179 for controller in controllers:
180 controller.StopTracing()
181
182
183 def _PullTraces(controllers, output, write_json):
184 _PrintMessage('Downloading...', eol='')
185 trace_files = []
186 for controller in controllers:
187 trace_files.append(controller.PullTrace())
188
189 if not write_json:
190 html_file = os.path.splitext(trace_files[0])[0] + '.html'
191 _PackageTracesAsHtml(trace_files, html_file)
192 trace_files = [html_file]
193
194 if len(trace_files) > 1:
195 result = output or 'chrome-combined-trace-%s.zip' % _GetTraceTimestamp()
196 _ArchiveFiles(trace_files, result)
197 elif output:
198 result = output
199 shutil.move(trace_files[0], result)
200 else:
201 result = trace_files[0]
202
203 _PrintMessage('done')
204 _PrintMessage('Trace written to file://%s' % os.path.abspath(result))
205 return result
206
207
208 def _CaptureAndPullTrace(adb, controllers, output, write_json):
209 trace_type = ' + '.join(map(str, controllers))
210 try:
211 adb.RunShellCommand(
212 'echo "chrome --trace-startup '
213 '--trace-startup-file='
214 '/data/data/com.google.android.apps.chrome/cache/chrome-trace"'
215 '> /data/local/chrome-command-line')
216
217 adb.RunShellCommand('echo 1 > /proc/sys/vm/drop_caches')
218 _StartTracing(controllers)
219 adb.RunShellCommand(
220 'am start -a android.intent.action.VIEW -n '
221 'com.google.android.apps.chrome/.Main -d google.com')
222 _PrintMessage('Capturing %s. Press Enter to stop...' % trace_type, eol='')
223 raw_input()
224 finally:
225 _StopTracing(controllers)
226 _PrintMessage('done')
227
228 return _PullTraces(controllers, output, write_json)
229
230
231 def _ComputeSystraceCategories(options):
232 if not options.systrace_categories:
233 return []
234 return options.systrace_categories.split(',')
235
236
237 def main():
238 parser = optparse.OptionParser(description='Record about://tracing profiles '
239 'from Android browsers. See http://dev.'
240 'chromium.org/developers/how-tos/trace-event-'
241 'profiling-tool for detailed instructions for '
242 'profiling.')
243
244
245 categories = optparse.OptionGroup(parser, 'Trace categories')
246 categories.add_option('-s', '--systrace', help='Capture a systrace with the '
247 'chosen comma-delimited systrace categories. You can '
248 'also capture a combined Chrome + systrace by enabling '
249 'both types of categories. Use "list" to see the '
250 'available categories. Systrace is disabled by '
251 'default.', metavar='SYS_CATEGORIES',
252 dest='systrace_categories', default='')
253 categories.add_option('--trace-flow', help='Enable extra trace categories '
254 'for IPC message flows.', action='store_true')
255 parser.add_option_group(categories)
256
257 output_options = optparse.OptionGroup(parser, 'Output options')
258 output_options.add_option('-o', '--output', help='Save trace output to file.')
259 output_options.add_option('--json', help='Save trace as raw JSON instead of '
260 'HTML.', action='store_true')
261 output_options.add_option('--view', help='Open resulting trace file in a '
262 'browser.', action='store_true')
263 parser.add_option_group(output_options)
264
265 parser.add_option('-v', '--verbose', help='Verbose logging.',
266 action='store_true')
267 options, _args = parser.parse_args()
268
269 if options.verbose:
270 logging.getLogger().setLevel(logging.DEBUG)
271
272 adb = android_commands.AndroidCommands()
273 if options.systrace_categories in ['list', 'help']:
274 _PrintMessage('\n'.join(SystraceController.GetCategories(adb)))
275 return 0
276
277 systrace_categories = _ComputeSystraceCategories(options)
278
279 controllers = []
280 controllers.append(SystraceController(adb, systrace_categories))
281 controllers.append(ChromeTracingController(adb))
282
283 if options.output:
284 options.output = os.path.expanduser(options.output)
285 result = _CaptureAndPullTrace(adb, controllers, options.output, options.json)
286 if options.view:
287 if sys.platform == 'darwin':
288 os.system('/usr/bin/open %s' % os.path.abspath(result))
289 else:
290 webbrowser.open(result)
291
292
293 if __name__ == '__main__':
294 sys.exit(main())
OLDNEW
« 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