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

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

Issue 15151003: Use a single Python Forwarder instance for each base test runner instance. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address Craig + Frank comments Created 7 years, 7 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 | Annotate | Revision Log
« no previous file with comments | « build/android/pylib/chrome_test_server_spawner.py ('k') | no next file » | 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 re 7 import re
8 import sys 8 import sys
9 import threading
9 import time 10 import time
10 11
11 import android_commands 12 import android_commands
12 import cmd_helper 13 import cmd_helper
13 import constants 14 import constants
14 import ports 15 import ports
15 16
16 from pylib import pexpect 17 from pylib import pexpect
17 18
18 19
19 def _MakeBinaryPath(build_type, binary_name): 20 def _MakeBinaryPath(build_type, binary_name):
20 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)
21 22
22 23
23 class Forwarder(object): 24 class Forwarder(object):
24 """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."""
25 26
26 # Unix Abstract socket path: 27 # Unix Abstract socket path:
27 _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder' 28 _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder'
28 _TIMEOUT_SECS = 30 29 _TIMEOUT_SECS = 30
29 30
30 _DEVICE_FORWARDER_FOLDER = (constants.TEST_EXECUTABLE_DIR + 31 _DEVICE_FORWARDER_FOLDER = (constants.TEST_EXECUTABLE_DIR +
31 '/forwarder/') 32 '/forwarder/')
32 _DEVICE_FORWARDER_PATH = (constants.TEST_EXECUTABLE_DIR + 33 _DEVICE_FORWARDER_PATH = (constants.TEST_EXECUTABLE_DIR +
33 '/forwarder/device_forwarder') 34 '/forwarder/device_forwarder')
34 _LD_LIBRARY_PATH = 'LD_LIBRARY_PATH=%s' % _DEVICE_FORWARDER_FOLDER 35 _LD_LIBRARY_PATH = 'LD_LIBRARY_PATH=%s' % _DEVICE_FORWARDER_FOLDER
35 36
36 def __init__(self, adb, build_type): 37 def __init__(self, adb, build_type):
37 """Forwards TCP ports on the device back to the host. 38 """Forwards TCP ports on the device back to the host.
38 39
39 Works like adb forward, but in reverse. 40 Works like adb forward, but in reverse.
40 41
41 Args: 42 Args:
42 adb: Instance of AndroidCommands for talking to the device. 43 adb: Instance of AndroidCommands for talking to the device.
43 build_type: 'Release' or 'Debug'. 44 build_type: 'Release' or 'Debug'.
44 """ 45 """
45 assert build_type in ('Release', 'Debug') 46 assert build_type in ('Release', 'Debug')
46 self._adb = adb 47 self._adb = adb
47 self._host_to_device_port_map = dict() 48 self._host_to_device_port_map = dict()
48 self._device_process = None 49 self._device_initialized = False
50 self._host_adb_control_port = 0
51 self._lock = threading.Lock()
49 self._host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder') 52 self._host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder')
50 self._device_forwarder_path_on_host = os.path.join( 53 self._device_forwarder_path_on_host = os.path.join(
51 cmd_helper.OutDirectory.get(), build_type, 'forwarder_dist') 54 cmd_helper.OutDirectory.get(), build_type, 'forwarder_dist')
52 55
53 def Run(self, port_pairs, tool, host_name): 56 def Run(self, port_pairs, tool, host_name):
54 """Runs the forwarder. 57 """Runs the forwarder.
55 58
56 Args: 59 Args:
57 port_pairs: A list of tuples (device_port, host_port) to forward. Note 60 port_pairs: A list of tuples (device_port, host_port) to forward. Note
58 that you can specify 0 as a device_port, in which case a 61 that you can specify 0 as a device_port, in which case a
59 port will by dynamically assigned on the device. You can 62 port will by dynamically assigned on the device. You can
60 get the number of the assigned port using the 63 get the number of the assigned port using the
61 DevicePortForHostPort method. 64 DevicePortForHostPort method.
62 tool: Tool class to use to get wrapper, if necessary, for executing the 65 tool: Tool class to use to get wrapper, if necessary, for executing the
63 forwarder (see valgrind_tools.py). 66 forwarder (see valgrind_tools.py).
64 host_name: Address to forward to, must be addressable from the 67 host_name: Address to forward to, must be addressable from the
65 host machine. Usually use loopback '127.0.0.1'. 68 host machine. Usually use loopback '127.0.0.1'.
66 69
67 Raises: 70 Raises:
68 Exception on failure to forward the port. 71 Exception on failure to forward the port.
69 """ 72 """
73 with self._lock:
74 self._InitDeviceLocked(tool)
75 self._InitHostLocked()
76 redirection_commands = [
77 '%d:%d:%d:%s' % (self._host_adb_control_port, device, host,
78 host_name) 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)
82
83 for redirection_command in redirection_commands:
84 try:
85 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
86 [self._host_forwarder_path, redirection_command])
87 except OSError as e:
88 if e.errno == 2:
89 raise Exception('Unable to start host forwarder. Make sure you have'
90 ' built host_forwarder.')
91 else: raise
92 if exit_code != 0:
93 raise Exception('%s exited with %d:\n%s' % (
94 self._host_forwarder_path, exit_code, '\n'.join(output)))
95 tokens = output.split(':')
96 if len(tokens) != 2:
97 raise Exception('Unexpected host forwarder output "%s", ' +
98 'expected "device_port:host_port"' % output)
99 device_port = int(tokens[0])
100 host_port = int(tokens[1])
101 self._host_to_device_port_map[host_port] = device_port
102 logging.info('Forwarding device port: %d to host port: %d.',
103 device_port, host_port)
104
105 def _InitHostLocked(self):
106 """Initializes the host forwarder process (only once)."""
107 if self._host_adb_control_port:
108 return
70 self._host_adb_control_port = ports.AllocateTestServerPort() 109 self._host_adb_control_port = ports.AllocateTestServerPort()
71 if not self._host_adb_control_port: 110 if not self._host_adb_control_port:
72 raise Exception('Failed to allocate a TCP port in the host machine.') 111 raise Exception('Failed to allocate a TCP port in the host machine.')
73 self._adb.PushIfNeeded(
74 self._device_forwarder_path_on_host, Forwarder._DEVICE_FORWARDER_FOLDER)
75 redirection_commands = [
76 '%d:%d:%d:%s' % (self._host_adb_control_port, device, host,
77 host_name) 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)
81 if cmd_helper.RunCmd( 112 if cmd_helper.RunCmd(
82 ['adb', '-s', self._adb._adb.GetSerialNumber(), 'forward', 113 ['adb', '-s', self._adb._adb.GetSerialNumber(), 'forward',
83 'tcp:%s' % self._host_adb_control_port, 114 'tcp:%s' % self._host_adb_control_port,
84 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) != 0: 115 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) != 0:
85 raise Exception('Error while running adb forward.') 116 raise Exception('Error while running adb forward.')
86 117
118 def _InitDeviceLocked(self, tool):
119 """Initializes the device forwarder process (only once)."""
120 if self._device_initialized:
121 return
122 self._adb.PushIfNeeded(
123 self._device_forwarder_path_on_host,
124 Forwarder._DEVICE_FORWARDER_FOLDER)
87 (exit_code, output) = self._adb.GetShellCommandStatusAndOutput( 125 (exit_code, output) = self._adb.GetShellCommandStatusAndOutput(
88 '%s %s %s %s' % (Forwarder._LD_LIBRARY_PATH, tool.GetUtilWrapper(), 126 '%s %s %s %s' % (Forwarder._LD_LIBRARY_PATH, tool.GetUtilWrapper(),
89 Forwarder._DEVICE_FORWARDER_PATH, 127 Forwarder._DEVICE_FORWARDER_PATH,
90 Forwarder._DEVICE_ADB_CONTROL_PORT)) 128 Forwarder._DEVICE_ADB_CONTROL_PORT))
91 if exit_code != 0: 129 if exit_code != 0:
92 raise Exception( 130 raise Exception(
93 'Failed to start device forwarder:\n%s' % '\n'.join(output)) 131 'Failed to start device forwarder:\n%s' % '\n'.join(output))
132 self._device_initialized = True
94 133
95 for redirection_command in redirection_commands: 134 def UnmapDevicePort(self, device_port):
96 try: 135 """Unmaps a previously forwarded device port.
97 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( 136
98 [self._host_forwarder_path, redirection_command]) 137 Args:
99 except OSError as e: 138 device_port: A previously forwarded port (through Run()).
100 if e.errno == 2: 139 """
101 raise Exception('Unable to start host forwarder. Make sure you have ' 140 with self._lock:
102 'built host_forwarder.') 141 # Please note the minus sign below.
103 else: raise 142 redirection_command = '%d:-%d' % (
143 self._host_adb_control_port, device_port)
144 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
145 [self._host_forwarder_path, redirection_command])
104 if exit_code != 0: 146 if exit_code != 0:
105 raise Exception('%s exited with %d:\n%s' % ( 147 raise Exception('%s exited with %d:\n%s' % (
106 self._host_forwarder_path, exit_code, '\n'.join(output))) 148 self._host_forwarder_path, exit_code, '\n'.join(output)))
107 tokens = output.split(':')
108 if len(tokens) != 2:
109 raise Exception('Unexpected host forwarder output "%s", ' +
110 'expected "device_port:host_port"' % output)
111 device_port = int(tokens[0])
112 host_port = int(tokens[1])
113 self._host_to_device_port_map[host_port] = device_port
114 logging.info('Forwarding device port: %d to host port: %d.', device_port,
115 host_port)
116
117 def UnmapDevicePort(self, device_port):
118 # Please note the minus sign below.
119 redirection_command = '%d:-%d' % (self._host_adb_control_port, device_port)
120 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
121 [self._host_forwarder_path, redirection_command])
122 if exit_code != 0:
123 raise Exception('%s exited with %d:\n%s' % (
124 self._host_forwarder_path, exit_code, '\n'.join(output)))
125 149
126 @staticmethod 150 @staticmethod
127 def KillHost(build_type): 151 def KillHost(build_type):
152 """Kills the forwarder process running on the host.
153
154 Args:
155 build_type: 'Release' or 'Debug'
156 """
128 logging.info('Killing host_forwarder.') 157 logging.info('Killing host_forwarder.')
129 host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder') 158 host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder')
130 assert os.path.exists(host_forwarder_path), 'Please build forwarder2' 159 assert os.path.exists(host_forwarder_path), 'Please build forwarder2'
131 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( 160 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
132 [host_forwarder_path, 'kill-server']) 161 [host_forwarder_path, 'kill-server'])
133 if exit_code != 0: 162 if exit_code != 0:
134 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( 163 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
135 ['pkill', 'host_forwarder']) 164 ['pkill', 'host_forwarder'])
136 if exit_code != 0: 165 if exit_code != 0:
137 raise Exception('%s exited with %d:\n%s' % ( 166 raise Exception('%s exited with %d:\n%s' % (
138 host_forwarder_path, exit_code, '\n'.join(output))) 167 host_forwarder_path, exit_code, '\n'.join(output)))
139 168
140 @staticmethod 169 @staticmethod
141 def KillDevice(adb, tool): 170 def KillDevice(adb, tool):
171 """Kills the forwarder process running on the device.
172
173 Args:
174 adb: Instance of AndroidCommands for talking to the device.
175 tool: Wrapper tool (e.g. valgrind) that can be used to execute the device
176 forwarder (see valgrind_tools.py).
177 """
142 logging.info('Killing device_forwarder.') 178 logging.info('Killing device_forwarder.')
143 if not adb.FileExistsOnDevice(Forwarder._DEVICE_FORWARDER_PATH): 179 if not adb.FileExistsOnDevice(Forwarder._DEVICE_FORWARDER_PATH):
144 return 180 return
145 (exit_code, output) = adb.GetShellCommandStatusAndOutput( 181 (exit_code, output) = adb.GetShellCommandStatusAndOutput(
146 '%s %s kill-server' % (tool.GetUtilWrapper(), 182 '%s %s kill-server' % (tool.GetUtilWrapper(),
147 Forwarder._DEVICE_FORWARDER_PATH)) 183 Forwarder._DEVICE_FORWARDER_PATH))
148 # TODO(pliard): Remove the following call to KillAllBlocking() when we are 184 # TODO(pliard): Remove the following call to KillAllBlocking() when we are
149 # sure that the old version of device_forwarder (not supporting 185 # sure that the old version of device_forwarder (not supporting
150 # 'kill-server') is not running on the bots anymore. 186 # 'kill-server') is not running on the bots anymore.
151 timeout_sec = 5 187 timeout_sec = 5
152 processes_killed = adb.KillAllBlocking('device_forwarder', timeout_sec) 188 processes_killed = adb.KillAllBlocking('device_forwarder', timeout_sec)
153 if not processes_killed: 189 if not processes_killed:
154 pids = adb.ExtractPid('device_forwarder') 190 pids = adb.ExtractPid('device_forwarder')
155 if pids: 191 if pids:
156 raise Exception('Timed out while killing device_forwarder') 192 raise Exception('Timed out while killing device_forwarder')
157 193
158 def DevicePortForHostPort(self, host_port): 194 def DevicePortForHostPort(self, host_port):
159 """Get the device port that corresponds to a given host port.""" 195 """Returns the device port that corresponds to a given host port."""
160 return self._host_to_device_port_map.get(host_port) 196 with self._lock:
197 return self._host_to_device_port_map.get(host_port)
161 198
199 # Deprecated.
162 def Close(self): 200 def Close(self):
163 """Terminate the forwarder process.""" 201 """Terminates the forwarder process."""
164 if self._device_process: 202 # TODO(pliard): Remove references in client code.
165 self._device_process.close() 203 pass
166 self._device_process = None
OLDNEW
« no previous file with comments | « build/android/pylib/chrome_test_server_spawner.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698