Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(490)

Side by Side Diff: build/android/pylib/forwarder.py

Issue 11269036: Support HTTP test-server based net unit tests on Android. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Avoid leaking 'ap' in common.h Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 def _MakeBinaryPath(build_type, binary_name):
19 return os.path.join(constants.CHROME_DIR, 'out', build_type, binary_name)
20
21 def KillHostForwarder(build_type):
22 host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder')
23 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
24 [host_forwarder_path, 'kill-server'])
25 if exit_code != 0:
26 raise Exception('%s exited with %d: %s' % (host_forwarder_path,
27 exit_code, output))
28
29 def KillDeviceForwarder(adb):
30 logging.info('Killing device_forwarder.')
31 timeout_sec = 5
32 processes_killed = adb.KillAllBlocking('device_forwarder', timeout_sec)
33 if not processes_killed:
34 pids = adb.ExtractPid('device_forwarder')
35 if pids:
36 raise Exception('Timed out while killing device_forwarder')
37
18 class Forwarder(object): 38 class Forwarder(object):
19 """Class to manage port forwards from the device to the host.""" 39 """Class to manage port forwards from the device to the host."""
20 40
21 _DEVICE_FORWARDER_PATH = constants.TEST_EXECUTABLE_DIR + '/device_forwarder' 41 _DEVICE_FORWARDER_PATH = constants.TEST_EXECUTABLE_DIR + '/device_forwarder'
22 42
23 # Unix Abstract socket path: 43 # Unix Abstract socket path:
24 _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder' 44 _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder'
25 _TIMEOUT_SECS = 30 45 _TIMEOUT_SECS = 30
26 46
27 def __init__(self, adb, port_pairs, tool, host_name, build_type): 47 def __init__(self, adb, build_type):
28 """Forwards TCP ports on the device back to the host. 48 """Forwards TCP ports on the device back to the host.
29 49
30 Works like adb forward, but in reverse. 50 Works like adb forward, but in reverse.
31 51
32 Args: 52 Args:
33 adb: Instance of AndroidCommands for talking to the device. 53 adb: Instance of AndroidCommands for talking to the device.
54 build_type: 'Release' or 'Debug'.
55 """
56 self._adb = adb
57 self._host_to_device_port_map = dict()
58 self._device_process = None
59 self._host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder')
60 self._device_forwarder_path = _MakeBinaryPath(
61 build_type, 'device_forwarder')
62
63 def Run(self, port_pairs, tool, host_name):
64 """Runs the forwarder.
65
66 Args:
34 port_pairs: A list of tuples (device_port, host_port) to forward. Note 67 port_pairs: A list of tuples (device_port, host_port) to forward. Note
35 that you can specify 0 as a device_port, in which case a 68 that you can specify 0 as a device_port, in which case a
36 port will by dynamically assigned on the device. You can 69 port will by dynamically assigned on the device. You can
37 get the number of the assigned port using the 70 get the number of the assigned port using the
38 DevicePortForHostPort method. 71 DevicePortForHostPort method.
39 tool: Tool class to use to get wrapper, if necessary, for executing the 72 tool: Tool class to use to get wrapper, if necessary, for executing the
40 forwarder (see valgrind_tools.py). 73 forwarder (see valgrind_tools.py).
41 host_name: Address to forward to, must be addressable from the 74 host_name: Address to forward to, must be addressable from the
42 host machine. Usually use loopback '127.0.0.1'. 75 host machine. Usually use loopback '127.0.0.1'.
43 build_type: 'Release' or 'Debug'.
44 76
45 Raises: 77 Raises:
46 Exception on failure to forward the port. 78 Exception on failure to forward the port.
47 """ 79 """
48 self._adb = adb 80 host_adb_control_port = ports.AllocateTestServerPort()
49 self._host_to_device_port_map = dict() 81 if not host_adb_control_port:
50 self._host_process = None 82 raise Exception('Failed to allocate a TCP port in the host machine.')
51 self._device_process = None 83 self._adb.PushIfNeeded(self._device_forwarder_path,
52 self._adb_forward_process = None 84 Forwarder._DEVICE_FORWARDER_PATH)
85 redirection_commands = [
86 '%d:%d:%d:%s' % (host_adb_control_port, device, host,
87 host_name) for device, host in port_pairs]
88 logging.info('Command format: <ADB port>:<Device port>' +
89 '[:<Forward to port>:<Forward to address>]')
90 logging.info('Forwarding using commands: %s', redirection_commands)
91 if cmd_helper.RunCmd(
92 ['adb', '-s', self._adb._adb.GetSerialNumber(), 'forward',
93 'tcp:%s' % host_adb_control_port,
94 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) != 0:
95 raise Exception('Error while running adb forward.')
53 96
54 self._host_adb_control_port = ports.AllocateTestServerPort() 97 if not self._adb.ExtractPid('device_forwarder'):
55 if not self._host_adb_control_port: 98 # TODO(pliard): Get rid of pexpect here and make device_forwarder a daemon
56 raise Exception('Failed to allocate a TCP port in the host machine.') 99 # with a blocking CLI process that exits with a proper exit code and not
57 adb.PushIfNeeded( 100 # while the daemon is still setting up. This would be similar to how
58 os.path.join(constants.CHROME_DIR, 'out', build_type, 101 # host_forwarder works.
59 'device_forwarder'), 102 self._device_process = pexpect.spawn(
60 Forwarder._DEVICE_FORWARDER_PATH) 103 'adb', ['-s',
61 self._host_forwarder_path = os.path.join(constants.CHROME_DIR, 104 self._adb._adb.GetSerialNumber(),
62 'out', 105 'shell',
63 build_type, 106 '%s %s -D --adb_sock=%s' % (
64 'host_forwarder') 107 tool.GetUtilWrapper(),
65 forward_string = ['%d:%d:%s' % 108 Forwarder._DEVICE_FORWARDER_PATH,
66 (device, host, host_name) for device, host in port_pairs] 109 Forwarder._DEVICE_ADB_CONTROL_PORT)])
67 logging.info('Forwarding ports: %s', forward_string) 110 device_success_re = re.compile('Starting Device Forwarder.')
68 timeout_sec = 5 111 device_failure_re = re.compile('.*:ERROR:(.*)')
69 host_pattern = 'host_forwarder.*' + ' '.join(forward_string) 112 index = self._device_process.expect([device_success_re,
70 # TODO(felipeg): Rather than using a blocking kill() here, the device 113 device_failure_re,
71 # forwarder could try to bind the Unix Domain Socket until it succeeds or 114 pexpect.EOF,
72 # while it fails because the socket is already bound (with appropriate 115 pexpect.TIMEOUT],
73 # timeout handling obviously). 116 Forwarder._TIMEOUT_SECS)
74 self._KillHostForwarderBlocking(host_pattern, timeout_sec) 117 if index == 1:
75 self._KillDeviceForwarderBlocking(timeout_sec) 118 error_msg = str(self._device_process.match.group(1))
76 self._adb_forward_process = pexpect.spawn( 119 logging.error(self._device_process.before)
77 'adb', ['-s', 120 self._device_process.close()
78 adb._adb.GetSerialNumber(), 121 raise Exception('Failed to start Device Forwarder with Error: %s' %
79 'forward', 122 error_msg)
80 'tcp:%s' % self._host_adb_control_port, 123 elif index == 2:
81 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) 124 logging.error(self._device_process.before)
82 self._device_process = pexpect.spawn( 125 self._device_process.close()
83 'adb', ['-s', 126 raise Exception(
84 adb._adb.GetSerialNumber(), 127 'Unexpected EOF while trying to start Device Forwarder.')
85 'shell', 128 elif index == 3:
86 '%s %s -D --adb_sock=%s' % ( 129 logging.error(self._device_process.before)
87 tool.GetUtilWrapper(), 130 self._device_process.close()
88 Forwarder._DEVICE_FORWARDER_PATH, 131 raise Exception('Timeout while trying start Device Forwarder')
89 Forwarder._DEVICE_ADB_CONTROL_PORT)])
90 132
91 device_success_re = re.compile('Starting Device Forwarder.') 133 for redirection_command in redirection_commands:
92 device_failure_re = re.compile('.*:ERROR:(.*)') 134 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
93 index = self._device_process.expect([device_success_re, 135 [self._host_forwarder_path, redirection_command])
94 device_failure_re, 136 if exit_code != 0:
95 pexpect.EOF, 137 raise Exception('%s exited with %d: %s' % (self._host_forwarder_path,
96 pexpect.TIMEOUT], 138 exit_code, output))
97 Forwarder._TIMEOUT_SECS) 139 tokens = output.split(':')
98 if index == 1: 140 if len(tokens) != 2:
99 # Failure 141 raise Exception('Unexpected host forwarder output "%s", ' +
100 error_msg = str(self._device_process.match.group(1)) 142 'expected "device_port:host_port"' % output)
101 logging.error(self._device_process.before) 143 device_port = int(tokens[0])
102 self._CloseProcess() 144 host_port = int(tokens[1])
103 raise Exception('Failed to start Device Forwarder with Error: %s' % 145 self._host_to_device_port_map[host_port] = device_port
104 error_msg) 146 logging.info(
105 elif index == 2: 147 "Forwarding device port: %d to host port: %d." % (device_port,
106 logging.error(self._device_process.before) 148 host_port))
107 self._CloseProcess()
108 raise Exception('Unexpected EOF while trying to start Device Forwarder.')
109 elif index == 3:
110 logging.error(self._device_process.before)
111 self._CloseProcess()
112 raise Exception('Timeout while trying start Device Forwarder')
113
114 self._host_process = pexpect.spawn(self._host_forwarder_path,
115 ['--adb_port=%s' % (
116 self._host_adb_control_port)] +
117 forward_string)
118
119 # Read the output of the command to determine which device ports where
120 # forwarded to which host ports (necessary if
121 host_success_re = re.compile('Forwarding device port (\d+) to host (\d+):')
122 host_failure_re = re.compile('Couldn\'t start forwarder server for port '
123 'spec: (\d+):(\d+)')
124 for pair in port_pairs:
125 index = self._host_process.expect([host_success_re,
126 host_failure_re,
127 pexpect.EOF,
128 pexpect.TIMEOUT],
129 Forwarder._TIMEOUT_SECS)
130 if index == 0:
131 # Success
132 device_port = int(self._host_process.match.group(1))
133 host_port = int(self._host_process.match.group(2))
134 self._host_to_device_port_map[host_port] = device_port
135 logging.info("Forwarding device port: %d to host port: %d." %
136 (device_port, host_port))
137 elif index == 1:
138 # Failure
139 device_port = int(self._host_process.match.group(1))
140 host_port = int(self._host_process.match.group(2))
141 self._CloseProcess()
142 raise Exception('Failed to forward port %d to %d' % (device_port,
143 host_port))
144 elif index == 2:
145 logging.error(self._host_process.before)
146 self._CloseProcess()
147 raise Exception('Unexpected EOF while trying to forward ports %s' %
148 port_pairs)
149 elif index == 3:
150 logging.error(self._host_process.before)
151 self._CloseProcess()
152 raise Exception('Timeout while trying to forward ports %s' % port_pairs)
153
154 def _KillHostForwarderBlocking(self, host_pattern, timeout_sec):
155 """Kills any existing host forwarders using the provided pattern.
156
157 Note that this waits until the process terminates.
158 """
159 cmd_helper.RunCmd(['pkill', '-f', host_pattern])
160 elapsed = 0
161 wait_period = 0.1
162 while not cmd_helper.RunCmd(['pgrep', '-f', host_pattern]) and (
163 elapsed < timeout_sec):
164 time.sleep(wait_period)
165 elapsed += wait_period
166 if elapsed >= timeout_sec:
167 raise Exception('Timed out while killing ' + host_pattern)
168
169 def _KillDeviceForwarderBlocking(self, timeout_sec):
170 """Kills any existing device forwarders.
171
172 Note that this waits until the process terminates.
173 """
174 processes_killed = self._adb.KillAllBlocking(
175 'device_forwarder', timeout_sec)
176 if not processes_killed:
177 pids = self._adb.ExtractPid('device_forwarder')
178 if pids:
179 raise Exception('Timed out while killing device_forwarder')
180
181 def _CloseProcess(self):
182 if self._host_process:
183 self._host_process.close()
184 if self._device_process:
185 self._device_process.close()
186 if self._adb_forward_process:
187 self._adb_forward_process.close()
188 self._host_process = None
189 self._device_process = None
190 self._adb_forward_process = None
191 149
192 def DevicePortForHostPort(self, host_port): 150 def DevicePortForHostPort(self, host_port):
193 """Get the device port that corresponds to a given host port.""" 151 """Get the device port that corresponds to a given host port."""
194 return self._host_to_device_port_map.get(host_port) 152 return self._host_to_device_port_map.get(host_port)
195 153
196 def Close(self): 154 def Close(self):
197 """Terminate the forwarder process.""" 155 """Terminate the forwarder process."""
198 self._CloseProcess() 156 if self._device_process:
157 self._device_process.close()
158 self._device_process = None
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698