OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 import gzip | 7 import gzip |
8 import logging | 8 import logging |
9 import optparse | 9 import optparse |
10 import os | 10 import os |
11 import re | 11 import re |
12 import select | 12 import select |
13 import shutil | 13 import shutil |
14 import sys | 14 import sys |
15 import threading | 15 import threading |
16 import time | 16 import time |
17 import webbrowser | 17 import webbrowser |
18 import zipfile | 18 import zipfile |
19 import zlib | 19 import zlib |
20 | 20 |
21 from pylib import android_commands | 21 from pylib import android_commands |
22 from pylib import cmd_helper | 22 from pylib import cmd_helper |
23 from pylib import constants | 23 from pylib import constants |
24 from pylib import pexpect | 24 from pylib import pexpect |
| 25 from pylib.device import device_utils |
25 | 26 |
26 _TRACE_VIEWER_ROOT = os.path.join(constants.DIR_SOURCE_ROOT, | 27 _TRACE_VIEWER_ROOT = os.path.join(constants.DIR_SOURCE_ROOT, |
27 'third_party', 'trace-viewer') | 28 'third_party', 'trace-viewer') |
28 sys.path.append(_TRACE_VIEWER_ROOT) | 29 sys.path.append(_TRACE_VIEWER_ROOT) |
29 from trace_viewer.build import trace2html # pylint: disable=F0401 | 30 from trace_viewer.build import trace2html # pylint: disable=F0401 |
30 | 31 |
31 _DEFAULT_CHROME_CATEGORIES = '_DEFAULT_CHROME_CATEGORIES' | 32 _DEFAULT_CHROME_CATEGORIES = '_DEFAULT_CHROME_CATEGORIES' |
32 | 33 |
33 | 34 |
34 def _GetTraceTimestamp(): | 35 def _GetTraceTimestamp(): |
35 return time.strftime('%Y-%m-%d-%H%M%S', time.localtime()) | 36 return time.strftime('%Y-%m-%d-%H%M%S', time.localtime()) |
36 | 37 |
37 | 38 |
38 class ChromeTracingController(object): | 39 class ChromeTracingController(object): |
39 def __init__(self, adb, package_info, categories, ring_buffer): | 40 def __init__(self, device, package_info, categories, ring_buffer): |
40 self._adb = adb | 41 self._device = device |
41 self._package_info = package_info | 42 self._package_info = package_info |
42 self._categories = categories | 43 self._categories = categories |
43 self._ring_buffer = ring_buffer | 44 self._ring_buffer = ring_buffer |
44 self._trace_file = None | 45 self._trace_file = None |
45 self._trace_interval = None | 46 self._trace_interval = None |
46 self._trace_start_re = \ | 47 self._trace_start_re = \ |
47 re.compile(r'Logging performance trace to file: (.*)') | 48 re.compile(r'Logging performance trace to file: (.*)') |
48 self._trace_finish_re = \ | 49 self._trace_finish_re = \ |
49 re.compile(r'Profiler finished[.] Results are in (.*)[.]') | 50 re.compile(r'Profiler finished[.] Results are in (.*)[.]') |
50 self._adb.StartMonitoringLogcat(clear=False) | 51 self._device.old_interface.StartMonitoringLogcat(clear=False) |
51 | 52 |
52 def __str__(self): | 53 def __str__(self): |
53 return 'chrome trace' | 54 return 'chrome trace' |
54 | 55 |
55 def StartTracing(self, interval): | 56 def StartTracing(self, interval): |
56 self._trace_interval = interval | 57 self._trace_interval = interval |
57 self._adb.SyncLogCat() | 58 self._device.old_interface.SyncLogCat() |
58 self._adb.BroadcastIntent(self._package_info.package, 'GPU_PROFILER_START', | 59 self._device.old_interface.BroadcastIntent( |
59 '-e categories "%s"' % ','.join(self._categories), | 60 self._package_info.package, 'GPU_PROFILER_START', |
60 '-e continuous' if self._ring_buffer else '') | 61 '-e categories "%s"' % ','.join(self._categories), |
| 62 '-e continuous' if self._ring_buffer else '') |
61 # Chrome logs two different messages related to tracing: | 63 # Chrome logs two different messages related to tracing: |
62 # | 64 # |
63 # 1. "Logging performance trace to file [...]" | 65 # 1. "Logging performance trace to file [...]" |
64 # 2. "Profiler finished. Results are in [...]" | 66 # 2. "Profiler finished. Results are in [...]" |
65 # | 67 # |
66 # The first one is printed when tracing starts and the second one indicates | 68 # The first one is printed when tracing starts and the second one indicates |
67 # that the trace file is ready to be pulled. | 69 # that the trace file is ready to be pulled. |
68 try: | 70 try: |
69 self._trace_file = self._adb.WaitForLogMatch(self._trace_start_re, | 71 self._trace_file = self._device.old_interface.WaitForLogMatch( |
70 None, | 72 self._trace_start_re, None, timeout=5).group(1) |
71 timeout=5).group(1) | |
72 except pexpect.TIMEOUT: | 73 except pexpect.TIMEOUT: |
73 raise RuntimeError('Trace start marker not found. Is the correct version ' | 74 raise RuntimeError('Trace start marker not found. Is the correct version ' |
74 'of the browser running?') | 75 'of the browser running?') |
75 | 76 |
76 def StopTracing(self): | 77 def StopTracing(self): |
77 if not self._trace_file: | 78 if not self._trace_file: |
78 return | 79 return |
79 self._adb.BroadcastIntent(self._package_info.package, 'GPU_PROFILER_STOP') | 80 self._device.old_interface.BroadcastIntent(self._package_info.package, |
80 self._adb.WaitForLogMatch(self._trace_finish_re, None, timeout=120) | 81 'GPU_PROFILER_STOP') |
| 82 self._device.old_interface.WaitForLogMatch(self._trace_finish_re, None, |
| 83 timeout=120) |
81 | 84 |
82 def PullTrace(self): | 85 def PullTrace(self): |
83 # Wait a bit for the browser to finish writing the trace file. | 86 # Wait a bit for the browser to finish writing the trace file. |
84 time.sleep(self._trace_interval / 4 + 1) | 87 time.sleep(self._trace_interval / 4 + 1) |
85 | 88 |
86 trace_file = self._trace_file.replace('/storage/emulated/0/', '/sdcard/') | 89 trace_file = self._trace_file.replace('/storage/emulated/0/', '/sdcard/') |
87 host_file = os.path.join(os.path.curdir, os.path.basename(trace_file)) | 90 host_file = os.path.join(os.path.curdir, os.path.basename(trace_file)) |
88 self._adb.PullFileFromDevice(trace_file, host_file) | 91 self._device.old_interface.PullFileFromDevice(trace_file, host_file) |
89 return host_file | 92 return host_file |
90 | 93 |
91 | 94 |
92 _SYSTRACE_OPTIONS = [ | 95 _SYSTRACE_OPTIONS = [ |
93 # Compress the trace before sending it over USB. | 96 # Compress the trace before sending it over USB. |
94 '-z', | 97 '-z', |
95 # Use a large trace buffer to increase the polling interval. | 98 # Use a large trace buffer to increase the polling interval. |
96 '-b', '16384' | 99 '-b', '16384' |
97 ] | 100 ] |
98 | 101 |
99 # Interval in seconds for sampling systrace data. | 102 # Interval in seconds for sampling systrace data. |
100 _SYSTRACE_INTERVAL = 15 | 103 _SYSTRACE_INTERVAL = 15 |
101 | 104 |
102 | 105 |
103 class SystraceController(object): | 106 class SystraceController(object): |
104 def __init__(self, adb, categories, ring_buffer): | 107 def __init__(self, device, categories, ring_buffer): |
105 self._adb = adb | 108 self._device = device |
106 self._categories = categories | 109 self._categories = categories |
107 self._ring_buffer = ring_buffer | 110 self._ring_buffer = ring_buffer |
108 self._done = threading.Event() | 111 self._done = threading.Event() |
109 self._thread = None | 112 self._thread = None |
110 self._trace_data = None | 113 self._trace_data = None |
111 | 114 |
112 def __str__(self): | 115 def __str__(self): |
113 return 'systrace' | 116 return 'systrace' |
114 | 117 |
115 @staticmethod | 118 @staticmethod |
116 def GetCategories(adb): | 119 def GetCategories(device): |
117 return adb.RunShellCommand('atrace --list_categories') | 120 return device.old_interface.RunShellCommand('atrace --list_categories') |
118 | 121 |
119 def StartTracing(self, _): | 122 def StartTracing(self, _): |
120 self._thread = threading.Thread(target=self._CollectData) | 123 self._thread = threading.Thread(target=self._CollectData) |
121 self._thread.start() | 124 self._thread.start() |
122 | 125 |
123 def StopTracing(self): | 126 def StopTracing(self): |
124 self._done.set() | 127 self._done.set() |
125 | 128 |
126 def PullTrace(self): | 129 def PullTrace(self): |
127 self._thread.join() | 130 self._thread.join() |
128 self._thread = None | 131 self._thread = None |
129 if self._trace_data: | 132 if self._trace_data: |
130 output_name = 'systrace-%s' % _GetTraceTimestamp() | 133 output_name = 'systrace-%s' % _GetTraceTimestamp() |
131 with open(output_name, 'w') as out: | 134 with open(output_name, 'w') as out: |
132 out.write(self._trace_data) | 135 out.write(self._trace_data) |
133 return output_name | 136 return output_name |
134 | 137 |
135 def _RunATraceCommand(self, command): | 138 def _RunATraceCommand(self, command): |
| 139 # TODO(jbudorick) can this be made work with DeviceUtils? |
136 # We use a separate interface to adb because the one from AndroidCommands | 140 # We use a separate interface to adb because the one from AndroidCommands |
137 # isn't re-entrant. | 141 # isn't re-entrant. |
138 device = ['-s', self._adb.GetDevice()] if self._adb.GetDevice() else [] | 142 device_param = (['-s', self._device.old_interface.GetDevice()] |
139 cmd = ['adb'] + device + ['shell', 'atrace', '--%s' % command] + \ | 143 if self._device.old_interface.GetDevice() else []) |
| 144 cmd = ['adb'] + device_param + ['shell', 'atrace', '--%s' % command] + \ |
140 _SYSTRACE_OPTIONS + self._categories | 145 _SYSTRACE_OPTIONS + self._categories |
141 return cmd_helper.GetCmdOutput(cmd) | 146 return cmd_helper.GetCmdOutput(cmd) |
142 | 147 |
143 def _CollectData(self): | 148 def _CollectData(self): |
144 trace_data = [] | 149 trace_data = [] |
145 self._RunATraceCommand('async_start') | 150 self._RunATraceCommand('async_start') |
146 try: | 151 try: |
147 while not self._done.is_set(): | 152 while not self._done.is_set(): |
148 self._done.wait(_SYSTRACE_INTERVAL) | 153 self._done.wait(_SYSTRACE_INTERVAL) |
149 if not self._ring_buffer or self._done.is_set(): | 154 if not self._ring_buffer or self._done.is_set(): |
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
368 | 373 |
369 For basic jank busting uses, use --trace-frame-viewer | 374 For basic jank busting uses, use --trace-frame-viewer |
370 For detailed study of ubercompositor, pass --trace-ubercompositor. | 375 For detailed study of ubercompositor, pass --trace-ubercompositor. |
371 | 376 |
372 When in doubt, just try out --trace-frame-viewer. | 377 When in doubt, just try out --trace-frame-viewer. |
373 """) | 378 """) |
374 | 379 |
375 if options.verbose: | 380 if options.verbose: |
376 logging.getLogger().setLevel(logging.DEBUG) | 381 logging.getLogger().setLevel(logging.DEBUG) |
377 | 382 |
378 adb = android_commands.AndroidCommands() | 383 devices = android_commands.GetAttachedDevices() |
| 384 if len(devices) != 1: |
| 385 parser.error('Exactly 1 device much be attached.') |
| 386 device = device_utils.DeviceUtils(devices[0]) |
| 387 |
379 if options.systrace_categories in ['list', 'help']: | 388 if options.systrace_categories in ['list', 'help']: |
380 _PrintMessage('\n'.join(SystraceController.GetCategories(adb))) | 389 _PrintMessage('\n'.join(SystraceController.GetCategories(device))) |
381 return 0 | 390 return 0 |
382 | 391 |
383 if not options.time and not options.continuous: | 392 if not options.time and not options.continuous: |
384 _PrintMessage('Time interval or continuous tracing should be specified.') | 393 _PrintMessage('Time interval or continuous tracing should be specified.') |
385 return 1 | 394 return 1 |
386 | 395 |
387 chrome_categories = _ComputeChromeCategories(options) | 396 chrome_categories = _ComputeChromeCategories(options) |
388 systrace_categories = _ComputeSystraceCategories(options) | 397 systrace_categories = _ComputeSystraceCategories(options) |
389 package_info = _GetSupportedBrowsers()[options.browser] | 398 package_info = _GetSupportedBrowsers()[options.browser] |
390 | 399 |
391 if chrome_categories and 'webview' in systrace_categories: | 400 if chrome_categories and 'webview' in systrace_categories: |
392 logging.warning('Using the "webview" category in systrace together with ' | 401 logging.warning('Using the "webview" category in systrace together with ' |
393 'Chrome tracing results in duplicate trace events.') | 402 'Chrome tracing results in duplicate trace events.') |
394 | 403 |
395 controllers = [] | 404 controllers = [] |
396 if chrome_categories: | 405 if chrome_categories: |
397 controllers.append(ChromeTracingController(adb, | 406 controllers.append(ChromeTracingController(device, |
398 package_info, | 407 package_info, |
399 chrome_categories, | 408 chrome_categories, |
400 options.ring_buffer)) | 409 options.ring_buffer)) |
401 if systrace_categories: | 410 if systrace_categories: |
402 controllers.append(SystraceController(adb, | 411 controllers.append(SystraceController(device, |
403 systrace_categories, | 412 systrace_categories, |
404 options.ring_buffer)) | 413 options.ring_buffer)) |
405 | 414 |
406 if not controllers: | 415 if not controllers: |
407 _PrintMessage('No trace categories enabled.') | 416 _PrintMessage('No trace categories enabled.') |
408 return 1 | 417 return 1 |
409 | 418 |
410 if options.output: | 419 if options.output: |
411 options.output = os.path.expanduser(options.output) | 420 options.output = os.path.expanduser(options.output) |
412 result = _CaptureAndPullTrace(controllers, | 421 result = _CaptureAndPullTrace(controllers, |
413 options.time if not options.continuous else 0, | 422 options.time if not options.continuous else 0, |
414 options.output, | 423 options.output, |
415 options.compress, | 424 options.compress, |
416 options.json) | 425 options.json) |
417 if options.view: | 426 if options.view: |
418 if sys.platform == 'darwin': | 427 if sys.platform == 'darwin': |
419 os.system('/usr/bin/open %s' % os.path.abspath(result)) | 428 os.system('/usr/bin/open %s' % os.path.abspath(result)) |
420 else: | 429 else: |
421 webbrowser.open(result) | 430 webbrowser.open(result) |
422 | 431 |
423 | 432 |
424 if __name__ == '__main__': | 433 if __name__ == '__main__': |
425 sys.exit(main()) | 434 sys.exit(main()) |
OLD | NEW |