OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 # TODO(yzshen): Once the dep manager is ready, remove this file and use the one | 5 # TODO(yzshen): Once the dep manager is ready, remove this file and use the one |
6 # from src/mojo/tools directly. | 6 # from src/mojo/tools directly. |
7 | 7 |
8 | 8 |
9 import atexit | 9 import atexit |
10 import logging | 10 import logging |
11 import os | 11 import os |
12 import signal | 12 import signal |
13 import subprocess | 13 import subprocess |
14 import sys | 14 import sys |
15 import threading | 15 import threading |
16 import time | 16 import time |
17 | 17 |
18 from .paths import Paths | 18 from .paths import Paths |
19 | 19 |
| 20 from telemetry.internal.util import binary_manager |
| 21 |
20 from devil import base_error | 22 from devil import base_error |
21 from devil.android import apk_helper | 23 from devil.android import apk_helper |
22 from devil.android import device_errors | 24 from devil.android import device_errors |
23 from devil.android import device_utils | 25 from devil.android import device_utils |
24 from pylib import constants | 26 from devil.android.sdk import adb_wrapper |
| 27 from devil.constants import exit_codes |
25 | 28 |
26 | 29 |
27 # Tags used by the mojo shell application logs. | 30 # Tags used by the mojo shell application logs. |
28 LOGCAT_TAGS = [ | 31 LOGCAT_TAGS = [ |
29 'AndroidHandler', | 32 'AndroidHandler', |
30 'MojoFileHelper', | 33 'MojoFileHelper', |
31 'MojoMain', | 34 'MojoMain', |
32 'MojoShellActivity', | 35 'MojoShellActivity', |
33 'MojoShellApplication', | 36 'MojoShellApplication', |
34 'chromium', | 37 'chromium', |
35 ] | 38 ] |
36 | 39 |
37 MAPPING_PREFIX = '--map-origin=' | 40 MAPPING_PREFIX = '--map-origin=' |
38 | 41 |
39 | 42 |
40 def _ExitIfNeeded(process): | 43 def _ExitIfNeeded(process): |
41 '''Exits |process| if it is still alive.''' | 44 '''Exits |process| if it is still alive.''' |
42 if process.poll() is None: | 45 if process.poll() is None: |
43 process.kill() | 46 process.kill() |
44 | 47 |
45 | 48 |
46 class AndroidShell(object): | 49 class AndroidShell(object): |
47 ''' | 50 ''' |
48 Used to set up and run a given mojo shell binary on an Android device. | 51 Used to set up and run a given mojo shell binary on an Android device. |
49 |config| is the mopy.config.Config for the build. | 52 |config| is the mopy.config.Config for the build. |
50 ''' | 53 ''' |
51 def __init__(self, config, chrome_root): | 54 def __init__(self, config, chrome_root): |
52 self.adb_path = constants.GetAdbPath() | 55 self.adb_path = adb_wrapper.AdbWrapper.GetAdbPath() |
53 self.config = config | 56 self.config = config |
54 self.paths = Paths(config, chrome_root) | 57 self.paths = Paths(config, chrome_root) |
55 self.device = None | 58 self.device = None |
56 self.shell_args = [] | 59 self.shell_args = [] |
57 self.target_package = apk_helper.GetPackageName(self.paths.apk_path) | 60 self.target_package = apk_helper.GetPackageName(self.paths.apk_path) |
58 self.temp_gdb_dir = None | 61 self.temp_gdb_dir = None |
59 # This is used by decive_utils.Install to check if the apk needs updating. | |
60 constants.SetOutputDirectory(self.paths.build_dir) | |
61 | 62 |
62 # TODO(msw): Use pylib's adb_wrapper and device_utils instead. | 63 # TODO(msw): Use devil's adb_wrapper and device_utils instead. |
63 def _CreateADBCommand(self, args): | 64 def _CreateADBCommand(self, args): |
64 adb_command = [self.adb_path, '-s', self.device.adb.GetDeviceSerial()] | 65 adb_command = [self.adb_path, '-s', self.device.adb.GetDeviceSerial()] |
65 adb_command.extend(args) | 66 adb_command.extend(args) |
66 logging.getLogger().debug('Command: %s', ' '.join(adb_command)) | 67 logging.getLogger().debug('Command: %s', ' '.join(adb_command)) |
67 return adb_command | 68 return adb_command |
68 | 69 |
69 def _ReadFifo(self, path, pipe, on_fifo_closed, max_attempts=5): | 70 def _ReadFifo(self, path, pipe, on_fifo_closed, max_attempts=5): |
70 ''' | 71 ''' |
71 Reads the fifo at |path| on the device and write the contents to |pipe|. | 72 Reads the fifo at |path| on the device and write the contents to |pipe|. |
72 Calls |on_fifo_closed| when the fifo is closed. This method will try to find | 73 Calls |on_fifo_closed| when the fifo is closed. This method will try to find |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
107 self.device = next((d for d in devices if d == device), None) | 108 self.device = next((d for d in devices if d == device), None) |
108 if not self.device: | 109 if not self.device: |
109 raise device_errors.DeviceUnreachableError(device) | 110 raise device_errors.DeviceUnreachableError(device) |
110 elif devices: | 111 elif devices: |
111 self.device = devices[0] | 112 self.device = devices[0] |
112 else: | 113 else: |
113 raise device_errors.NoDevicesError() | 114 raise device_errors.NoDevicesError() |
114 | 115 |
115 logging.getLogger().debug('Using device: %s', self.device) | 116 logging.getLogger().debug('Using device: %s', self.device) |
116 # Clean the logs on the device to avoid displaying prior activity. | 117 # Clean the logs on the device to avoid displaying prior activity. |
117 subprocess.check_call(self._CreateADBCommand(['logcat', '-c'])) | 118 self.device.adb.Logcat(clear=True) |
118 self.device.EnableRoot() | 119 self.device.EnableRoot() |
119 self.device.Install(self.paths.apk_path) | 120 self.device.Install(self.paths.apk_path) |
120 except base_error.BaseError as e: | 121 except base_error.BaseError as e: |
121 # Report 'device not found' as infra failures. See http://crbug.com/493900 | 122 # Report 'device not found' as infra failures. See http://crbug.com/493900 |
122 print 'Exception in AndroidShell.InitShell:\n%s' % str(e) | 123 print 'Exception in AndroidShell.InitShell:\n%s' % str(e) |
123 if e.is_infra_error or 'error: device not found' in str(e): | 124 if e.is_infra_error or 'error: device not found' in str(e): |
124 return constants.INFRA_EXIT_CODE | 125 return exit_codes.INFRA |
125 return constants.ERROR_EXIT_CODE | 126 return exit_codes.ERROR |
126 | 127 |
127 return 0 | 128 return 0 |
128 | 129 |
129 def _GetProcessId(self, process): | 130 def _GetProcessId(self, process): |
130 '''Returns the process id of the process on the remote device.''' | 131 '''Returns the process id of the process on the remote device.''' |
131 while True: | 132 while True: |
132 line = process.stdout.readline() | 133 line = process.stdout.readline() |
133 pid_command = 'launcher waiting for GDB. pid: ' | 134 pid_command = 'launcher waiting for GDB. pid: ' |
134 index = line.find(pid_command) | 135 index = line.find(pid_command) |
135 if index != -1: | 136 if index != -1: |
136 return line[index + len(pid_command):].strip() | 137 return line[index + len(pid_command):].strip() |
137 return 0 | 138 return 0 |
138 | 139 |
139 def _GetLocalGdbPath(self): | 140 def _GetLocalGdbPath(self): |
140 '''Returns the path to the android gdb.''' | 141 '''Returns the path to the android gdb.''' |
141 if self.config.target_cpu == 'arm': | 142 if self.config.target_cpu not in ('arm', 'x86', 'x64'): |
142 return os.path.join(constants.ANDROID_NDK_ROOT, 'toolchains', | |
143 'arm-linux-androideabi-4.9', 'prebuilt', | |
144 'linux-x86_64', 'bin', 'arm-linux-androideabi-gdb') | |
145 elif self.config.target_cpu == 'x86': | |
146 return os.path.join(constants.ANDROID_NDK_ROOT, 'toolchains', | |
147 'x86-4.9', 'prebuilt', 'linux-x86_64', 'bin', | |
148 'i686-linux-android-gdb') | |
149 elif self.config.target_cpu == 'x64': | |
150 return os.path.join(constants.ANDROID_NDK_ROOT, 'toolchains', | |
151 'x86_64-4.9', 'prebuilt', 'linux-x86_64', 'bin', | |
152 'x86_64-linux-android-gdb') | |
153 else: | |
154 raise Exception('Unknown target_cpu: %s' % self.config.target_cpu) | 143 raise Exception('Unknown target_cpu: %s' % self.config.target_cpu) |
| 144 return binary_manager.FetchPath('gdb', self.config.target_cpu, 'android') |
155 | 145 |
156 def _WaitForProcessIdAndStartGdb(self, process): | 146 def _WaitForProcessIdAndStartGdb(self, process): |
157 ''' | 147 ''' |
158 Waits until we see the process id from the remote device, starts up | 148 Waits until we see the process id from the remote device, starts up |
159 gdbserver on the remote device, and gdb on the local device. | 149 gdbserver on the remote device, and gdb on the local device. |
160 ''' | 150 ''' |
161 # Wait until we see 'PID' | 151 # Wait until we see 'PID' |
162 pid = self._GetProcessId(process) | 152 pid = self._GetProcessId(process) |
163 assert pid != 0 | 153 assert pid != 0 |
164 # No longer need the logcat process. | 154 # No longer need the logcat process. |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 'gdb is in the directory %s\\n' | 269 'gdb is in the directory %s\\n' |
280 'The following functions have been defined:\\n' | 270 'The following functions have been defined:\\n' |
281 'reload-symbols: forces reloading symbols. If after a crash you\\n' | 271 'reload-symbols: forces reloading symbols. If after a crash you\\n' |
282 'still do not see symbols you likely need to create a link in\\n' | 272 'still do not see symbols you likely need to create a link in\\n' |
283 'the directory you are in.\\n' | 273 'the directory you are in.\\n' |
284 'info-symbols: shows status of current shared libraries.\\n' | 274 'info-symbols: shows status of current shared libraries.\\n' |
285 'NOTE: you may need to type reload-symbols again after a ' | 275 'NOTE: you may need to type reload-symbols again after a ' |
286 'crash.\\n\\n' % (tmp_dir, build_dir, tmp_dir)) | 276 'crash.\\n\\n' % (tmp_dir, build_dir, tmp_dir)) |
287 with open(gdb_init_path, 'w') as f: | 277 with open(gdb_init_path, 'w') as f: |
288 f.write(gdbinit) | 278 f.write(gdbinit) |
OLD | NEW |