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