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 time | 9 import time |
10 | 10 |
11 import android_commands | 11 import android_commands |
12 import cmd_helper | 12 import cmd_helper |
13 import constants | 13 import constants |
14 import ports | 14 import ports |
15 | 15 |
16 from pylib import pexpect | 16 from pylib import pexpect |
17 | 17 |
18 | 18 |
19 def _MakeBinaryPath(build_type, binary_name): | 19 def _MakeBinaryPath(build_type, binary_name): |
20 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) |
21 | 21 |
22 | 22 |
23 class Forwarder(object): | 23 class Forwarder(object): |
24 """Class to manage port forwards from the device to the host.""" | 24 """Class to manage port forwards from the device to the host.""" |
25 | 25 |
26 _DEVICE_FORWARDER_PATH = constants.TEST_EXECUTABLE_DIR + '/device_forwarder' | |
27 | |
28 # Unix Abstract socket path: | 26 # Unix Abstract socket path: |
29 _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder' | 27 _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder' |
30 _TIMEOUT_SECS = 30 | 28 _TIMEOUT_SECS = 30 |
31 | 29 |
| 30 _DEVICE_FORWARDER_PATH = constants.TEST_EXECUTABLE_DIR + '/device_forwarder' |
| 31 |
32 def __init__(self, adb, build_type): | 32 def __init__(self, adb, build_type): |
33 """Forwards TCP ports on the device back to the host. | 33 """Forwards TCP ports on the device back to the host. |
34 | 34 |
35 Works like adb forward, but in reverse. | 35 Works like adb forward, but in reverse. |
36 | 36 |
37 Args: | 37 Args: |
38 adb: Instance of AndroidCommands for talking to the device. | 38 adb: Instance of AndroidCommands for talking to the device. |
39 build_type: 'Release' or 'Debug'. | 39 build_type: 'Release' or 'Debug'. |
40 """ | 40 """ |
41 assert build_type in ('Release', 'Debug') | 41 assert build_type in ('Release', 'Debug') |
(...skipping 17 matching lines...) Expand all Loading... |
59 forwarder (see valgrind_tools.py). | 59 forwarder (see valgrind_tools.py). |
60 host_name: Address to forward to, must be addressable from the | 60 host_name: Address to forward to, must be addressable from the |
61 host machine. Usually use loopback '127.0.0.1'. | 61 host machine. Usually use loopback '127.0.0.1'. |
62 | 62 |
63 Raises: | 63 Raises: |
64 Exception on failure to forward the port. | 64 Exception on failure to forward the port. |
65 """ | 65 """ |
66 host_adb_control_port = ports.AllocateTestServerPort() | 66 host_adb_control_port = ports.AllocateTestServerPort() |
67 if not host_adb_control_port: | 67 if not host_adb_control_port: |
68 raise Exception('Failed to allocate a TCP port in the host machine.') | 68 raise Exception('Failed to allocate a TCP port in the host machine.') |
69 self._adb.PushIfNeeded(self._device_forwarder_path, | 69 self._adb.PushIfNeeded( |
70 Forwarder._DEVICE_FORWARDER_PATH) | 70 self._device_forwarder_path, Forwarder._DEVICE_FORWARDER_PATH) |
71 redirection_commands = [ | 71 redirection_commands = [ |
72 '%d:%d:%d:%s' % (host_adb_control_port, device, host, | 72 '%d:%d:%d:%s' % (host_adb_control_port, device, host, |
73 host_name) for device, host in port_pairs] | 73 host_name) for device, host in port_pairs] |
74 logging.info('Command format: <ADB port>:<Device port>' + | 74 logging.info('Command format: <ADB port>:<Device port>' + |
75 '[:<Forward to port>:<Forward to address>]') | 75 '[:<Forward to port>:<Forward to address>]') |
76 logging.info('Forwarding using commands: %s', redirection_commands) | 76 logging.info('Forwarding using commands: %s', redirection_commands) |
77 if cmd_helper.RunCmd( | 77 if cmd_helper.RunCmd( |
78 ['adb', '-s', self._adb._adb.GetSerialNumber(), 'forward', | 78 ['adb', '-s', self._adb._adb.GetSerialNumber(), 'forward', |
79 'tcp:%s' % host_adb_control_port, | 79 'tcp:%s' % host_adb_control_port, |
80 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) != 0: | 80 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) != 0: |
81 raise Exception('Error while running adb forward.') | 81 raise Exception('Error while running adb forward.') |
82 | 82 |
83 if not self._adb.ExtractPid('device_forwarder'): | 83 (exit_code, output) = self._adb.GetShellCommandStatusAndOutput( |
84 # TODO(pliard): Get rid of pexpect here and make device_forwarder a daemon | 84 '%s %s' % (Forwarder._DEVICE_FORWARDER_PATH, |
85 # with a blocking CLI process that exits with a proper exit code and not | 85 Forwarder._DEVICE_ADB_CONTROL_PORT)) |
86 # while the daemon is still setting up. This would be similar to how | 86 if exit_code != 0: |
87 # host_forwarder works. | 87 raise Exception( |
88 self._device_process = pexpect.spawn( | 88 'Failed to start device forwarder:\n%s' % '\n'.join(output)) |
89 'adb', ['-s', | |
90 self._adb._adb.GetSerialNumber(), | |
91 'shell', | |
92 '%s %s -D --adb_sock=%s' % ( | |
93 tool.GetUtilWrapper(), | |
94 Forwarder._DEVICE_FORWARDER_PATH, | |
95 Forwarder._DEVICE_ADB_CONTROL_PORT)]) | |
96 device_success_re = re.compile('Starting Device Forwarder.') | |
97 device_failure_re = re.compile('.*:ERROR:(.*)') | |
98 index = self._device_process.expect([device_success_re, | |
99 device_failure_re, | |
100 pexpect.EOF, | |
101 pexpect.TIMEOUT], | |
102 Forwarder._TIMEOUT_SECS) | |
103 if index == 1: | |
104 error_msg = str(self._device_process.match.group(1)) | |
105 logging.error(self._device_process.before) | |
106 self._device_process.close() | |
107 raise Exception('Failed to start Device Forwarder with Error: %s' % | |
108 error_msg) | |
109 elif index == 2: | |
110 logging.error(self._device_process.before) | |
111 self._device_process.close() | |
112 raise Exception( | |
113 'Unexpected EOF while trying to start Device Forwarder.') | |
114 elif index == 3: | |
115 logging.error(self._device_process.before) | |
116 self._device_process.close() | |
117 raise Exception('Timeout while trying start Device Forwarder') | |
118 | 89 |
119 for redirection_command in redirection_commands: | 90 for redirection_command in redirection_commands: |
120 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( | 91 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
121 [self._host_forwarder_path, redirection_command]) | 92 [self._host_forwarder_path, redirection_command]) |
122 if exit_code != 0: | 93 if exit_code != 0: |
123 raise Exception('%s exited with %d: %s' % (self._host_forwarder_path, | 94 raise Exception('%s exited with %d:\n%s' % ( |
124 exit_code, output)) | 95 self._host_forwarder_path, exit_code, '\n'.join(output))) |
125 tokens = output.split(':') | 96 tokens = output.split(':') |
126 if len(tokens) != 2: | 97 if len(tokens) != 2: |
127 raise Exception('Unexpected host forwarder output "%s", ' + | 98 raise Exception('Unexpected host forwarder output "%s", ' + |
128 'expected "device_port:host_port"' % output) | 99 'expected "device_port:host_port"' % output) |
129 device_port = int(tokens[0]) | 100 device_port = int(tokens[0]) |
130 host_port = int(tokens[1]) | 101 host_port = int(tokens[1]) |
131 self._host_to_device_port_map[host_port] = device_port | 102 self._host_to_device_port_map[host_port] = device_port |
132 logging.info('Forwarding device port: %d to host port: %d.', device_port, | 103 logging.info('Forwarding device port: %d to host port: %d.', device_port, |
133 host_port) | 104 host_port) |
134 | 105 |
135 @staticmethod | 106 @staticmethod |
136 def KillHost(build_type): | 107 def KillHost(build_type): |
137 host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder') | 108 host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder') |
138 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( | 109 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
139 [host_forwarder_path, 'kill-server']) | 110 [host_forwarder_path, 'kill-server']) |
140 if exit_code != 0: | 111 if exit_code != 0: |
141 raise Exception('%s exited with %d: %s' % (host_forwarder_path, | 112 raise Exception('%s exited with %d:\n%s' % (host_forwarder_path, |
142 exit_code, output)) | 113 exit_code, '\n'.join(output))) |
143 | 114 |
144 @staticmethod | 115 @staticmethod |
145 def KillDevice(adb): | 116 def KillDevice(adb): |
146 logging.info('Killing device_forwarder.') | 117 logging.info('Killing device_forwarder.') |
| 118 if not adb.FileExistsOnDevice(Forwarder._DEVICE_FORWARDER_PATH): |
| 119 return |
| 120 (exit_code, output) = adb.GetShellCommandStatusAndOutput( |
| 121 '%s kill-server' % Forwarder._DEVICE_FORWARDER_PATH) |
| 122 # TODO(pliard): Remove the following call to KillAllBlocking() when we are |
| 123 # sure that the old version of device_forwarder (not supporting |
| 124 # 'kill-server') is not running on the bots anymore. |
147 timeout_sec = 5 | 125 timeout_sec = 5 |
148 processes_killed = adb.KillAllBlocking('device_forwarder', timeout_sec) | 126 processes_killed = adb.KillAllBlocking('device_forwarder', timeout_sec) |
149 if not processes_killed: | 127 if not processes_killed: |
150 pids = adb.ExtractPid('device_forwarder') | 128 pids = adb.ExtractPid('device_forwarder') |
151 if pids: | 129 if pids: |
152 raise Exception('Timed out while killing device_forwarder') | 130 raise Exception('Timed out while killing device_forwarder') |
153 | 131 |
154 def DevicePortForHostPort(self, host_port): | 132 def DevicePortForHostPort(self, host_port): |
155 """Get the device port that corresponds to a given host port.""" | 133 """Get the device port that corresponds to a given host port.""" |
156 return self._host_to_device_port_map.get(host_port) | 134 return self._host_to_device_port_map.get(host_port) |
157 | 135 |
158 def Close(self): | 136 def Close(self): |
159 """Terminate the forwarder process.""" | 137 """Terminate the forwarder process.""" |
160 if self._device_process: | 138 if self._device_process: |
161 self._device_process.close() | 139 self._device_process.close() |
162 self._device_process = None | 140 self._device_process = None |
OLD | NEW |