| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 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 import logging | 5 import logging |
| 6 import os | 6 import os |
| 7 import re | 7 import re |
| 8 import sys | 8 import sys |
| 9 import threading | 9 import threading |
| 10 import time | 10 import time |
| 11 | 11 |
| 12 import android_commands | 12 import android_commands |
| 13 import cmd_helper | 13 import cmd_helper |
| 14 import constants | 14 import constants |
| 15 import ports | |
| 16 | 15 |
| 17 from pylib import pexpect | 16 from pylib import pexpect |
| 18 | 17 |
| 19 | 18 |
| 20 def _MakeBinaryPath(build_type, binary_name): | 19 def _MakeBinaryPath(build_type, binary_name): |
| 21 return os.path.join(cmd_helper.OutDirectory.get(), build_type, binary_name) | 20 return os.path.join(cmd_helper.OutDirectory.get(), build_type, binary_name) |
| 22 | 21 |
| 23 | 22 |
| 24 class Forwarder(object): | 23 class Forwarder(object): |
| 25 """Thread-safe class to manage port forwards from the device to the host.""" | 24 """Thread-safe class to manage port forwards from the device to the host.""" |
| 26 | 25 |
| 27 # Unix Abstract socket path: | |
| 28 _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder' | |
| 29 _TIMEOUT_SECS = 30 | |
| 30 | |
| 31 _DEVICE_FORWARDER_FOLDER = (constants.TEST_EXECUTABLE_DIR + | 26 _DEVICE_FORWARDER_FOLDER = (constants.TEST_EXECUTABLE_DIR + |
| 32 '/forwarder/') | 27 '/forwarder/') |
| 33 _DEVICE_FORWARDER_PATH = (constants.TEST_EXECUTABLE_DIR + | 28 _DEVICE_FORWARDER_PATH = (constants.TEST_EXECUTABLE_DIR + |
| 34 '/forwarder/device_forwarder') | 29 '/forwarder/device_forwarder') |
| 35 _LD_LIBRARY_PATH = 'LD_LIBRARY_PATH=%s' % _DEVICE_FORWARDER_FOLDER | 30 _LD_LIBRARY_PATH = 'LD_LIBRARY_PATH=%s' % _DEVICE_FORWARDER_FOLDER |
| 36 | 31 |
| 37 def __init__(self, adb, build_type): | 32 def __init__(self, adb, build_type): |
| 38 """Forwards TCP ports on the device back to the host. | 33 """Forwards TCP ports on the device back to the host. |
| 39 | 34 |
| 40 Works like adb forward, but in reverse. | 35 Works like adb forward, but in reverse. |
| (...skipping 23 matching lines...) Expand all Loading... |
| 64 get the number of the assigned port using the | 59 get the number of the assigned port using the |
| 65 DevicePortForHostPort method. | 60 DevicePortForHostPort method. |
| 66 tool: Tool class to use to get wrapper, if necessary, for executing the | 61 tool: Tool class to use to get wrapper, if necessary, for executing the |
| 67 forwarder (see valgrind_tools.py). | 62 forwarder (see valgrind_tools.py). |
| 68 | 63 |
| 69 Raises: | 64 Raises: |
| 70 Exception on failure to forward the port. | 65 Exception on failure to forward the port. |
| 71 """ | 66 """ |
| 72 with self._lock: | 67 with self._lock: |
| 73 self._InitDeviceLocked(tool) | 68 self._InitDeviceLocked(tool) |
| 74 self._InitHostLocked() | |
| 75 host_name = '127.0.0.1' | 69 host_name = '127.0.0.1' |
| 76 redirection_commands = [ | 70 redirection_commands = [ |
| 77 '%d:%d:%d:%s' % (self._host_adb_control_port, device, host, | 71 ['--serial-id=' + self._adb.Adb().GetSerialNumber(), '--map', |
| 78 host_name) for device, host in port_pairs] | 72 str(device), str(host)] for device, host in port_pairs] |
| 79 logging.info('Command format: <ADB port>:<Device port>' + | |
| 80 '[:<Forward to port>:<Forward to address>]') | |
| 81 logging.info('Forwarding using commands: %s', redirection_commands) | 73 logging.info('Forwarding using commands: %s', redirection_commands) |
| 82 | 74 |
| 83 for redirection_command in redirection_commands: | 75 for redirection_command in redirection_commands: |
| 84 try: | 76 try: |
| 85 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( | 77 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
| 86 [self._host_forwarder_path, redirection_command]) | 78 [self._host_forwarder_path] + redirection_command) |
| 87 except OSError as e: | 79 except OSError as e: |
| 88 if e.errno == 2: | 80 if e.errno == 2: |
| 89 raise Exception('Unable to start host forwarder. Make sure you have' | 81 raise Exception('Unable to start host forwarder. Make sure you have' |
| 90 ' built host_forwarder.') | 82 ' built host_forwarder.') |
| 91 else: raise | 83 else: raise |
| 92 if exit_code != 0: | 84 if exit_code != 0: |
| 93 raise Exception('%s exited with %d:\n%s' % ( | 85 raise Exception('%s exited with %d:\n%s' % ( |
| 94 self._host_forwarder_path, exit_code, '\n'.join(output))) | 86 self._host_forwarder_path, exit_code, '\n'.join(output))) |
| 95 tokens = output.split(':') | 87 tokens = output.split(':') |
| 96 if len(tokens) != 2: | 88 if len(tokens) != 2: |
| 97 raise Exception('Unexpected host forwarder output "%s", ' + | 89 raise Exception(('Unexpected host forwarder output "%s", ' + |
| 98 'expected "device_port:host_port"' % output) | 90 'expected "device_port:host_port"') % output) |
| 99 device_port = int(tokens[0]) | 91 device_port = int(tokens[0]) |
| 100 host_port = int(tokens[1]) | 92 host_port = int(tokens[1]) |
| 101 self._device_to_host_port_map[device_port] = host_port | 93 self._device_to_host_port_map[device_port] = host_port |
| 102 self._host_to_device_port_map[host_port] = device_port | 94 self._host_to_device_port_map[host_port] = device_port |
| 103 logging.info('Forwarding device port: %d to host port: %d.', | 95 logging.info('Forwarding device port: %d to host port: %d.', |
| 104 device_port, host_port) | 96 device_port, host_port) |
| 105 | 97 |
| 106 def _InitHostLocked(self): | |
| 107 """Initializes the host forwarder process (only once).""" | |
| 108 if self._host_adb_control_port: | |
| 109 return | |
| 110 self._host_adb_control_port = ports.AllocateTestServerPort() | |
| 111 if not self._host_adb_control_port: | |
| 112 raise Exception('Failed to allocate a TCP port in the host machine.') | |
| 113 if cmd_helper.RunCmd( | |
| 114 ['adb', '-s', self._adb._adb.GetSerialNumber(), 'forward', | |
| 115 'tcp:%s' % self._host_adb_control_port, | |
| 116 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) != 0: | |
| 117 raise Exception('Error while running adb forward.') | |
| 118 | |
| 119 def _InitDeviceLocked(self, tool): | 98 def _InitDeviceLocked(self, tool): |
| 120 """Initializes the device forwarder process (only once).""" | 99 """Initializes the device forwarder process (only once).""" |
| 121 if self._device_initialized: | 100 if self._device_initialized: |
| 122 return | 101 return |
| 123 self._adb.PushIfNeeded( | 102 self._adb.PushIfNeeded( |
| 124 self._device_forwarder_path_on_host, | 103 self._device_forwarder_path_on_host, |
| 125 Forwarder._DEVICE_FORWARDER_FOLDER) | 104 Forwarder._DEVICE_FORWARDER_FOLDER) |
| 126 (exit_code, output) = self._adb.GetShellCommandStatusAndOutput( | 105 (exit_code, output) = self._adb.GetShellCommandStatusAndOutput( |
| 127 '%s %s %s %s' % (Forwarder._LD_LIBRARY_PATH, tool.GetUtilWrapper(), | 106 '%s %s %s' % (Forwarder._LD_LIBRARY_PATH, tool.GetUtilWrapper(), |
| 128 Forwarder._DEVICE_FORWARDER_PATH, | 107 Forwarder._DEVICE_FORWARDER_PATH)) |
| 129 Forwarder._DEVICE_ADB_CONTROL_PORT)) | |
| 130 if exit_code != 0: | 108 if exit_code != 0: |
| 131 raise Exception( | 109 raise Exception( |
| 132 'Failed to start device forwarder:\n%s' % '\n'.join(output)) | 110 'Failed to start device forwarder:\n%s' % '\n'.join(output)) |
| 133 self._device_initialized = True | 111 self._device_initialized = True |
| 134 | 112 |
| 135 def UnmapDevicePort(self, device_port): | 113 def UnmapDevicePort(self, device_port): |
| 136 """Unmaps a previously forwarded device port. | 114 """Unmaps a previously forwarded device port. |
| 137 | 115 |
| 138 Args: | 116 Args: |
| 139 device_port: A previously forwarded port (through Run()). | 117 device_port: A previously forwarded port (through Run()). |
| 140 """ | 118 """ |
| 141 with self._lock: | 119 with self._lock: |
| 142 self._UnmapDevicePortInternalLocked(device_port) | 120 self._UnmapDevicePortInternalLocked(device_port) |
| 143 | 121 |
| 144 def _UnmapDevicePortInternalLocked(self, device_port): | 122 def _UnmapDevicePortInternalLocked(self, device_port): |
| 145 if not device_port in self._device_to_host_port_map: | 123 if not device_port in self._device_to_host_port_map: |
| 146 return | 124 return |
| 147 # Please note the minus sign below. | 125 redirection_command = [ |
| 148 redirection_command = '%d:-%d' % ( | 126 '--serial-id=' + self._adb.Adb().GetSerialNumber(), '--unmap', |
| 149 self._host_adb_control_port, device_port) | 127 str(device_port)] |
| 150 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( | 128 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
| 151 [self._host_forwarder_path, redirection_command]) | 129 [self._host_forwarder_path] + redirection_command) |
| 152 if exit_code != 0: | 130 if exit_code != 0: |
| 153 raise Exception('%s exited with %d:\n%s' % ( | 131 raise Exception('%s exited with %d:\n%s' % ( |
| 154 self._host_forwarder_path, exit_code, '\n'.join(output))) | 132 self._host_forwarder_path, exit_code, '\n'.join(output))) |
| 155 host_port = self._device_to_host_port_map[device_port] | 133 host_port = self._device_to_host_port_map[device_port] |
| 156 del self._device_to_host_port_map[device_port] | 134 del self._device_to_host_port_map[device_port] |
| 157 del self._host_to_device_port_map[host_port] | 135 del self._host_to_device_port_map[host_port] |
| 158 | 136 |
| 159 @staticmethod | 137 @staticmethod |
| 160 def KillHost(build_type='Debug'): | 138 def KillHost(build_type='Debug'): |
| 161 """Kills the forwarder process running on the host. | 139 """Kills the forwarder process running on the host. |
| 162 | 140 |
| 163 Args: | 141 Args: |
| 164 build_type: 'Release' or 'Debug' (default='Debug') | 142 build_type: 'Release' or 'Debug' (default='Debug') |
| 165 """ | 143 """ |
| 166 logging.info('Killing host_forwarder.') | 144 logging.info('Killing host_forwarder.') |
| 167 host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder') | 145 host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder') |
| 168 if not os.path.exists(host_forwarder_path): | 146 if not os.path.exists(host_forwarder_path): |
| 169 host_forwarder_path = _MakeBinaryPath( | 147 host_forwarder_path = _MakeBinaryPath( |
| 170 'Release' if build_type == 'Debug' else 'Debug', 'host_forwarder') | 148 'Release' if build_type == 'Debug' else 'Debug', 'host_forwarder') |
| 171 assert os.path.exists(host_forwarder_path), 'Please build forwarder2' | 149 assert os.path.exists(host_forwarder_path), 'Please build forwarder2' |
| 172 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( | 150 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
| 173 [host_forwarder_path, 'kill-server']) | 151 [host_forwarder_path, '--kill-server']) |
| 174 if exit_code != 0: | 152 if exit_code != 0: |
| 175 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( | 153 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
| 176 ['pkill', 'host_forwarder']) | 154 ['pkill', 'host_forwarder']) |
| 177 if exit_code != 0: | 155 if exit_code != 0: |
| 178 raise Exception('%s exited with %d:\n%s' % ( | 156 raise Exception('%s exited with %d:\n%s' % ( |
| 179 host_forwarder_path, exit_code, '\n'.join(output))) | 157 host_forwarder_path, exit_code, '\n'.join(output))) |
| 180 | 158 |
| 181 @staticmethod | 159 @staticmethod |
| 182 def KillDevice(adb, tool): | 160 def KillDevice(adb, tool): |
| 183 """Kills the forwarder process running on the device. | 161 """Kills the forwarder process running on the device. |
| 184 | 162 |
| 185 Args: | 163 Args: |
| 186 adb: Instance of AndroidCommands for talking to the device. | 164 adb: Instance of AndroidCommands for talking to the device. |
| 187 tool: Wrapper tool (e.g. valgrind) that can be used to execute the device | 165 tool: Wrapper tool (e.g. valgrind) that can be used to execute the device |
| 188 forwarder (see valgrind_tools.py). | 166 forwarder (see valgrind_tools.py). |
| 189 """ | 167 """ |
| 190 logging.info('Killing device_forwarder.') | 168 logging.info('Killing device_forwarder.') |
| 191 if not adb.FileExistsOnDevice(Forwarder._DEVICE_FORWARDER_PATH): | 169 if not adb.FileExistsOnDevice(Forwarder._DEVICE_FORWARDER_PATH): |
| 192 return | 170 return |
| 193 (exit_code, output) = adb.GetShellCommandStatusAndOutput( | 171 (exit_code, output) = adb.GetShellCommandStatusAndOutput( |
| 194 '%s %s kill-server' % (tool.GetUtilWrapper(), | 172 '%s %s --kill-server' % (tool.GetUtilWrapper(), |
| 195 Forwarder._DEVICE_FORWARDER_PATH)) | 173 Forwarder._DEVICE_FORWARDER_PATH)) |
| 196 # TODO(pliard): Remove the following call to KillAllBlocking() when we are | 174 # TODO(pliard): Remove the following call to KillAllBlocking() when we are |
| 197 # sure that the old version of device_forwarder (not supporting | 175 # sure that the old version of device_forwarder (not supporting |
| 198 # 'kill-server') is not running on the bots anymore. | 176 # 'kill-server') is not running on the bots anymore. |
| 199 timeout_sec = 5 | 177 timeout_sec = 5 |
| 200 processes_killed = adb.KillAllBlocking('device_forwarder', timeout_sec) | 178 processes_killed = adb.KillAllBlocking('device_forwarder', timeout_sec) |
| 201 if not processes_killed: | 179 if not processes_killed: |
| 202 pids = adb.ExtractPid('device_forwarder') | 180 pids = adb.ExtractPid('device_forwarder') |
| 203 if pids: | 181 if pids: |
| 204 raise Exception('Timed out while killing device_forwarder') | 182 raise Exception('Timed out while killing device_forwarder') |
| 205 | 183 |
| 206 def DevicePortForHostPort(self, host_port): | 184 def DevicePortForHostPort(self, host_port): |
| 207 """Returns the device port that corresponds to a given host port.""" | 185 """Returns the device port that corresponds to a given host port.""" |
| 208 with self._lock: | 186 with self._lock: |
| 209 return self._host_to_device_port_map.get(host_port) | 187 return self._host_to_device_port_map.get(host_port) |
| 210 | 188 |
| 211 def Close(self): | 189 def Close(self): |
| 212 """Releases the previously forwarded ports.""" | 190 """Releases the previously forwarded ports.""" |
| 213 with self._lock: | 191 with self._lock: |
| 214 for device_port in self._device_to_host_port_map.copy(): | 192 for device_port in self._device_to_host_port_map.copy(): |
| 215 self._UnmapDevicePortInternalLocked(device_port) | 193 self._UnmapDevicePortInternalLocked(device_port) |
| OLD | NEW |