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

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

Issue 11148018: Upstream the android python scripts changes that enable the new Forwarder2 to be used in our test f… (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 8 years, 2 months 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
« no previous file with comments | « build/android/pylib/base_test_runner.py ('k') | build/android/pylib/ports.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 pexpect 7 import pexpect
8 import re 8 import re
9 import sys 9 import sys
10 import time
10 11
11 import android_commands 12 import android_commands
13 import cmd_helper
12 import constants 14 import constants
15 import ports
13 16
14 class Forwarder(object): 17 class Forwarder(object):
15 """Class to manage port forwards from the device to the host.""" 18 """Class to manage port forwards from the device to the host."""
16 19
17 _FORWARDER_PATH = constants.TEST_EXECUTABLE_DIR + '/forwarder' 20 _DEVICE_FORWARDER_PATH = constants.TEST_EXECUTABLE_DIR + '/device_forwarder'
21
22 # Unix Abstract socket path:
23 _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder'
18 _TIMEOUT_SECS = 30 24 _TIMEOUT_SECS = 30
19 25
20 def __init__(self, adb, port_pairs, tool, host_name, build_type): 26 def __init__(self, adb, port_pairs, tool, host_name, build_type):
21 """Forwards TCP ports on the device back to the host. 27 """Forwards TCP ports on the device back to the host.
22 28
23 Works like adb forward, but in reverse. 29 Works like adb forward, but in reverse.
24 30
25 Args: 31 Args:
26 adb: Instance of AndroidCommands for talking to the device. 32 adb: Instance of AndroidCommands for talking to the device.
27 port_pairs: A list of tuples (device_port, host_port) to forward. Note 33 port_pairs: A list of tuples (device_port, host_port) to forward. Note
28 that you can specify 0 as a device_port, in which case a 34 that you can specify 0 as a device_port, in which case a
29 port will by dynamically assigned on the device. You can 35 port will by dynamically assigned on the device. You can
30 get the number of the assigned port using the 36 get the number of the assigned port using the
31 DevicePortForHostPort method. 37 DevicePortForHostPort method.
32 tool: Tool class to use to get wrapper, if necessary, for executing the 38 tool: Tool class to use to get wrapper, if necessary, for executing the
33 forwarder (see valgrind_tools.py). 39 forwarder (see valgrind_tools.py).
34 host_name: Address to forward to, must be addressable from the 40 host_name: Address to forward to, must be addressable from the
35 host machine. Usually use loopback '127.0.0.1'. 41 host machine. Usually use loopback '127.0.0.1'.
36 build_type: 'Release' or 'Debug'. 42 build_type: 'Release' or 'Debug'.
37 43
38 Raises: 44 Raises:
39 Exception on failure to forward the port. 45 Exception on failure to forward the port.
40 """ 46 """
41 self._adb = adb 47 self._adb = adb
42 self._host_to_device_port_map = dict() 48 self._host_to_device_port_map = dict()
43 self._process = None 49 self._host_process = None
50 self._device_process = None
51 self._adb_forward_process = None
52
53 self._host_adb_control_port = ports.AllocateTestServerPort()
54 if not self._host_adb_control_port:
55 raise Exception('Failed to allocate a TCP port in the host machine.')
44 adb.PushIfNeeded( 56 adb.PushIfNeeded(
45 os.path.join(constants.CHROME_DIR, 'out', build_type, 'forwarder'), 57 os.path.join(constants.CHROME_DIR, 'out', build_type,
46 Forwarder._FORWARDER_PATH) 58 'device_forwarder'),
59 Forwarder._DEVICE_FORWARDER_PATH)
60 self._host_forwarder_path = os.path.join(constants.CHROME_DIR,
61 'out',
62 build_type,
63 'host_forwarder')
47 forward_string = ['%d:%d:%s' % 64 forward_string = ['%d:%d:%s' %
48 (device, host, host_name) for device, host in port_pairs] 65 (device, host, host_name) for device, host in port_pairs]
66 logging.info('Forwarding ports: %s', forward_string)
67 timeout_sec = 5
68 host_pattern = 'host_forwarder.*' + ' '.join(forward_string)
69 # TODO(felipeg): Rather than using a blocking kill() here, the device
70 # forwarder could try to bind the Unix Domain Socket until it succeeds or
71 # while it fails because the socket is already bound (with appropriate
72 # timeout handling obviously).
73 self._KillHostForwarderBlocking(host_pattern, timeout_sec)
74 self._KillDeviceForwarderBlocking(timeout_sec)
75 self._adb_forward_process = pexpect.spawn(
76 'adb', ['-s',
77 adb._adb.GetSerialNumber(),
78 'forward',
79 'tcp:%s' % self._host_adb_control_port,
80 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT])
81 self._device_process = pexpect.spawn(
82 'adb', ['-s',
83 adb._adb.GetSerialNumber(),
84 'shell',
85 '%s %s -D --adb_sock=%s' % (
86 tool.GetUtilWrapper(),
87 Forwarder._DEVICE_FORWARDER_PATH,
88 Forwarder._DEVICE_ADB_CONTROL_PORT)])
49 89
50 # Kill off any existing forwarders on conflicting non-dynamically allocated 90 device_success_re = re.compile('Starting Device Forwarder.')
51 # ports. 91 device_failure_re = re.compile('.*:ERROR:(.*)')
52 for device_port, _ in port_pairs: 92 index = self._device_process.expect([device_success_re,
53 if device_port != 0: 93 device_failure_re,
54 self._KillForwardersUsingDevicePort(device_port) 94 pexpect.EOF,
95 pexpect.TIMEOUT],
96 Forwarder._TIMEOUT_SECS)
97 if index == 1:
98 # Failure
99 error_msg = str(self._device_process.match.group(1))
100 logging.error(self._device_process.before)
101 self._CloseProcess()
102 raise Exception('Failed to start Device Forwarder with Error: %s' %
103 error_msg)
104 elif index == 2:
105 logging.error(self._device_process.before)
106 self._CloseProcess()
107 raise Exception('Unexpected EOF while trying to start Device Forwarder.')
108 elif index == 3:
109 logging.error(self._device_process.before)
110 self._CloseProcess()
111 raise Exception('Timeout while trying start Device Forwarder')
55 112
56 logging.info('Forwarding ports: %s' % (forward_string)) 113 self._host_process = pexpect.spawn(self._host_forwarder_path,
57 process = pexpect.spawn( 114 ['--adb_port=%s' % (
58 'adb', ['-s', adb._adb.GetSerialNumber(), 115 self._host_adb_control_port)] +
59 'shell', '%s %s -D %s' % ( 116 forward_string)
60 tool.GetUtilWrapper(), Forwarder._FORWARDER_PATH,
61 ' '.join(forward_string))])
62 117
63 # Read the output of the command to determine which device ports where 118 # Read the output of the command to determine which device ports where
64 # forwarded to which host ports (necessary if 119 # forwarded to which host ports (necessary if
65 success_re = re.compile('Forwarding device port (\d+) to host (\d+):') 120 host_success_re = re.compile('Forwarding device port (\d+) to host (\d+):')
66 failure_re = re.compile('Couldn\'t start forwarder server for port spec: ' 121 host_failure_re = re.compile('Couldn\'t start forwarder server for port '
67 '(\d+):(\d+)') 122 'spec: (\d+):(\d+)')
68 for pair in port_pairs: 123 for pair in port_pairs:
69 index = process.expect([success_re, failure_re, pexpect.EOF, 124 index = self._host_process.expect([host_success_re,
70 pexpect.TIMEOUT], 125 host_failure_re,
71 Forwarder._TIMEOUT_SECS) 126 pexpect.EOF,
127 pexpect.TIMEOUT],
128 Forwarder._TIMEOUT_SECS)
72 if index == 0: 129 if index == 0:
73 # Success 130 # Success
74 device_port = int(process.match.group(1)) 131 device_port = int(self._host_process.match.group(1))
75 host_port = int(process.match.group(2)) 132 host_port = int(self._host_process.match.group(2))
76 self._host_to_device_port_map[host_port] = device_port 133 self._host_to_device_port_map[host_port] = device_port
77 logging.info("Forwarding device port: %d to host port: %d." % 134 logging.info("Forwarding device port: %d to host port: %d." %
78 (device_port, host_port)) 135 (device_port, host_port))
79 elif index == 1: 136 elif index == 1:
80 # Failure 137 # Failure
81 device_port = int(process.match.group(1)) 138 device_port = int(self._host_process.match.group(1))
82 host_port = int(process.match.group(2)) 139 host_port = int(self._host_process.match.group(2))
83 process.close() 140 self._CloseProcess()
84 raise Exception('Failed to forward port %d to %d' % (device_port, 141 raise Exception('Failed to forward port %d to %d' % (device_port,
85 host_port)) 142 host_port))
86 elif index == 2: 143 elif index == 2:
87 logging.error(process.before) 144 logging.error(self._host_process.before)
88 process.close() 145 self._CloseProcess()
89 raise Exception('Unexpected EOF while trying to forward ports %s' % 146 raise Exception('Unexpected EOF while trying to forward ports %s' %
90 port_pairs) 147 port_pairs)
91 elif index == 3: 148 elif index == 3:
92 logging.error(process.before) 149 logging.error(self._host_process.before)
93 process.close() 150 self._CloseProcess()
94 raise Exception('Timeout while trying to forward ports %s' % port_pairs) 151 raise Exception('Timeout while trying to forward ports %s' % port_pairs)
95 152
96 self._process = process 153 def _KillHostForwarderBlocking(self, host_pattern, timeout_sec):
154 """Kills any existing host forwarders using the provided pattern.
97 155
98 def _KillForwardersUsingDevicePort(self, device_port): 156 Note that this waits until the process terminates.
99 """Check if the device port is in use and if it is try to terminate the 157 """
100 forwarder process (if any) that may already be forwarding it""" 158 cmd_helper.RunCmd(['pkill', '-f', host_pattern])
101 processes = self._adb.ProcessesUsingDevicePort(device_port) 159 elapsed = 0
102 for pid, name in processes: 160 wait_period = 0.1
103 if name == 'forwarder': 161 while not cmd_helper.RunCmd(['pgrep', '-f', host_pattern]) and (
104 logging.warning( 162 elapsed < timeout_sec):
105 'Killing forwarder process with pid %d using device_port %d' % ( 163 time.sleep(wait_period)
106 pid, device_port)) 164 elapsed += wait_period
107 self._adb.RunShellCommand('kill %d' % pid) 165 if elapsed >= timeout_sec:
108 else: 166 raise Exception('Timed out while killing ' + host_pattern)
109 logging.error( 167
110 'Not killing process with pid %d (%s) using device_port %d' % ( 168 def _KillDeviceForwarderBlocking(self, timeout_sec):
111 pid, name, device_port)) 169 """Kills any existing device forwarders.
170
171 Note that this waits until the process terminates.
172 """
173 processes_killed = self._adb.KillAllBlocking(
174 'device_forwarder', timeout_sec)
175 if not processes_killed:
176 pids = self._adb.ExtractPid('device_forwarder')
177 if pids:
178 raise Exception('Timed out while killing device_forwarder')
179
180 def _CloseProcess(self):
181 if self._host_process:
182 self._host_process.close()
183 if self._device_process:
184 self._device_process.close()
185 if self._adb_forward_process:
186 self._adb_forward_process.close()
187 self._host_process = None
188 self._device_process = None
189 self._adb_forward_process = None
112 190
113 def DevicePortForHostPort(self, host_port): 191 def DevicePortForHostPort(self, host_port):
114 """Get the device port that corresponds to a given host port.""" 192 """Get the device port that corresponds to a given host port."""
115 return self._host_to_device_port_map.get(host_port) 193 return self._host_to_device_port_map.get(host_port)
116 194
117 def Close(self): 195 def Close(self):
118 """Terminate the forwarder process.""" 196 """Terminate the forwarder process."""
119 if self._process: 197 self._CloseProcess()
120 self._process.close()
121 self._process = None
OLDNEW
« no previous file with comments | « build/android/pylib/base_test_runner.py ('k') | build/android/pylib/ports.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698