Index: build/android/pylib/forwarder.py |
diff --git a/build/android/pylib/forwarder.py b/build/android/pylib/forwarder.py |
index bc41db3ff3be7d9e6e64d48a795f5f438e3482e8..a0dc5f2fcc1d7ff59696d988968eeb85d614570b 100644 |
--- a/build/android/pylib/forwarder.py |
+++ b/build/android/pylib/forwarder.py |
@@ -15,6 +15,11 @@ import ports |
from pylib import pexpect |
+ |
+def _MakeBinaryPath(build_type, binary_name): |
+ return os.path.join(constants.CHROME_DIR, 'out', build_type, binary_name) |
+ |
+ |
class Forwarder(object): |
"""Class to manage port forwards from the device to the host.""" |
@@ -24,13 +29,27 @@ class Forwarder(object): |
_DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder' |
_TIMEOUT_SECS = 30 |
- def __init__(self, adb, port_pairs, tool, host_name, build_type): |
+ def __init__(self, adb, build_type): |
"""Forwards TCP ports on the device back to the host. |
Works like adb forward, but in reverse. |
Args: |
adb: Instance of AndroidCommands for talking to the device. |
+ build_type: 'Release' or 'Debug'. |
+ """ |
+ assert build_type in ('Release', 'Debug') |
+ self._adb = adb |
+ self._host_to_device_port_map = dict() |
+ self._device_process = None |
+ self._host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder') |
+ self._device_forwarder_path = _MakeBinaryPath( |
+ build_type, 'device_forwarder') |
+ |
+ def Run(self, port_pairs, tool, host_name): |
+ """Runs the forwarder. |
+ |
+ Args: |
port_pairs: A list of tuples (device_port, host_port) to forward. Note |
that you can specify 0 as a device_port, in which case a |
port will by dynamically assigned on the device. You can |
@@ -40,159 +59,104 @@ class Forwarder(object): |
forwarder (see valgrind_tools.py). |
host_name: Address to forward to, must be addressable from the |
host machine. Usually use loopback '127.0.0.1'. |
- build_type: 'Release' or 'Debug'. |
Raises: |
Exception on failure to forward the port. |
""" |
- self._adb = adb |
- self._host_to_device_port_map = dict() |
- self._host_process = None |
- self._device_process = None |
- self._adb_forward_process = None |
- |
- self._host_adb_control_port = ports.AllocateTestServerPort() |
- if not self._host_adb_control_port: |
+ host_adb_control_port = ports.AllocateTestServerPort() |
+ if not host_adb_control_port: |
raise Exception('Failed to allocate a TCP port in the host machine.') |
- adb.PushIfNeeded( |
- os.path.join(constants.CHROME_DIR, 'out', build_type, |
- 'device_forwarder'), |
- Forwarder._DEVICE_FORWARDER_PATH) |
- self._host_forwarder_path = os.path.join(constants.CHROME_DIR, |
- 'out', |
- build_type, |
- 'host_forwarder') |
- forward_string = ['%d:%d:%s' % |
- (device, host, host_name) for device, host in port_pairs] |
- logging.info('Forwarding ports: %s', forward_string) |
- timeout_sec = 5 |
- host_pattern = 'host_forwarder.*' + ' '.join(forward_string) |
- # TODO(felipeg): Rather than using a blocking kill() here, the device |
- # forwarder could try to bind the Unix Domain Socket until it succeeds or |
- # while it fails because the socket is already bound (with appropriate |
- # timeout handling obviously). |
- self._KillHostForwarderBlocking(host_pattern, timeout_sec) |
- self._KillDeviceForwarderBlocking(timeout_sec) |
- self._adb_forward_process = pexpect.spawn( |
- 'adb', ['-s', |
- adb._adb.GetSerialNumber(), |
- 'forward', |
- 'tcp:%s' % self._host_adb_control_port, |
- 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) |
- self._device_process = pexpect.spawn( |
- 'adb', ['-s', |
- adb._adb.GetSerialNumber(), |
- 'shell', |
- '%s %s -D --adb_sock=%s' % ( |
- tool.GetUtilWrapper(), |
- Forwarder._DEVICE_FORWARDER_PATH, |
- Forwarder._DEVICE_ADB_CONTROL_PORT)]) |
- |
- device_success_re = re.compile('Starting Device Forwarder.') |
- device_failure_re = re.compile('.*:ERROR:(.*)') |
- index = self._device_process.expect([device_success_re, |
- device_failure_re, |
- pexpect.EOF, |
- pexpect.TIMEOUT], |
- Forwarder._TIMEOUT_SECS) |
- if index == 1: |
- # Failure |
- error_msg = str(self._device_process.match.group(1)) |
- logging.error(self._device_process.before) |
- self._CloseProcess() |
- raise Exception('Failed to start Device Forwarder with Error: %s' % |
- error_msg) |
- elif index == 2: |
- logging.error(self._device_process.before) |
- self._CloseProcess() |
- raise Exception('Unexpected EOF while trying to start Device Forwarder.') |
- elif index == 3: |
- logging.error(self._device_process.before) |
- self._CloseProcess() |
- raise Exception('Timeout while trying start Device Forwarder') |
- |
- self._host_process = pexpect.spawn(self._host_forwarder_path, |
- ['--adb_port=%s' % ( |
- self._host_adb_control_port)] + |
- forward_string) |
- |
- # Read the output of the command to determine which device ports where |
- # forwarded to which host ports (necessary if |
- host_success_re = re.compile('Forwarding device port (\d+) to host (\d+):') |
- host_failure_re = re.compile('Couldn\'t start forwarder server for port ' |
- 'spec: (\d+):(\d+)') |
- for pair in port_pairs: |
- index = self._host_process.expect([host_success_re, |
- host_failure_re, |
- pexpect.EOF, |
- pexpect.TIMEOUT], |
- Forwarder._TIMEOUT_SECS) |
- if index == 0: |
- # Success |
- device_port = int(self._host_process.match.group(1)) |
- host_port = int(self._host_process.match.group(2)) |
- self._host_to_device_port_map[host_port] = device_port |
- logging.info("Forwarding device port: %d to host port: %d." % |
- (device_port, host_port)) |
- elif index == 1: |
- # Failure |
- device_port = int(self._host_process.match.group(1)) |
- host_port = int(self._host_process.match.group(2)) |
- self._CloseProcess() |
- raise Exception('Failed to forward port %d to %d' % (device_port, |
- host_port)) |
+ self._adb.PushIfNeeded(self._device_forwarder_path, |
+ Forwarder._DEVICE_FORWARDER_PATH) |
+ redirection_commands = [ |
+ '%d:%d:%d:%s' % (host_adb_control_port, device, host, |
+ host_name) for device, host in port_pairs] |
+ logging.info('Command format: <ADB port>:<Device port>' + |
+ '[:<Forward to port>:<Forward to address>]') |
+ logging.info('Forwarding using commands: %s', redirection_commands) |
+ if cmd_helper.RunCmd( |
+ ['adb', '-s', self._adb._adb.GetSerialNumber(), 'forward', |
+ 'tcp:%s' % host_adb_control_port, |
+ 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) != 0: |
+ raise Exception('Error while running adb forward.') |
+ |
+ if not self._adb.ExtractPid('device_forwarder'): |
+ # TODO(pliard): Get rid of pexpect here and make device_forwarder a daemon |
+ # with a blocking CLI process that exits with a proper exit code and not |
+ # while the daemon is still setting up. This would be similar to how |
+ # host_forwarder works. |
+ self._device_process = pexpect.spawn( |
+ 'adb', ['-s', |
+ self._adb._adb.GetSerialNumber(), |
+ 'shell', |
+ '%s %s -D --adb_sock=%s' % ( |
+ tool.GetUtilWrapper(), |
+ Forwarder._DEVICE_FORWARDER_PATH, |
+ Forwarder._DEVICE_ADB_CONTROL_PORT)]) |
+ device_success_re = re.compile('Starting Device Forwarder.') |
+ device_failure_re = re.compile('.*:ERROR:(.*)') |
+ index = self._device_process.expect([device_success_re, |
+ device_failure_re, |
+ pexpect.EOF, |
+ pexpect.TIMEOUT], |
+ Forwarder._TIMEOUT_SECS) |
+ if index == 1: |
+ error_msg = str(self._device_process.match.group(1)) |
+ logging.error(self._device_process.before) |
+ self._device_process.close() |
+ raise Exception('Failed to start Device Forwarder with Error: %s' % |
+ error_msg) |
elif index == 2: |
- logging.error(self._host_process.before) |
- self._CloseProcess() |
- raise Exception('Unexpected EOF while trying to forward ports %s' % |
- port_pairs) |
+ logging.error(self._device_process.before) |
+ self._device_process.close() |
+ raise Exception( |
+ 'Unexpected EOF while trying to start Device Forwarder.') |
elif index == 3: |
- logging.error(self._host_process.before) |
- self._CloseProcess() |
- raise Exception('Timeout while trying to forward ports %s' % port_pairs) |
- |
- def _KillHostForwarderBlocking(self, host_pattern, timeout_sec): |
- """Kills any existing host forwarders using the provided pattern. |
- |
- Note that this waits until the process terminates. |
- """ |
- cmd_helper.RunCmd(['pkill', '-f', host_pattern]) |
- elapsed = 0 |
- wait_period = 0.1 |
- while not cmd_helper.RunCmd(['pgrep', '-f', host_pattern]) and ( |
- elapsed < timeout_sec): |
- time.sleep(wait_period) |
- elapsed += wait_period |
- if elapsed >= timeout_sec: |
- raise Exception('Timed out while killing ' + host_pattern) |
- |
- def _KillDeviceForwarderBlocking(self, timeout_sec): |
- """Kills any existing device forwarders. |
- |
- Note that this waits until the process terminates. |
- """ |
- processes_killed = self._adb.KillAllBlocking( |
- 'device_forwarder', timeout_sec) |
+ logging.error(self._device_process.before) |
+ self._device_process.close() |
+ raise Exception('Timeout while trying start Device Forwarder') |
+ |
+ for redirection_command in redirection_commands: |
+ (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
+ [self._host_forwarder_path, redirection_command]) |
+ if exit_code != 0: |
+ raise Exception('%s exited with %d: %s' % (self._host_forwarder_path, |
+ exit_code, output)) |
+ tokens = output.split(':') |
+ if len(tokens) != 2: |
+ raise Exception('Unexpected host forwarder output "%s", ' + |
+ 'expected "device_port:host_port"' % output) |
+ device_port = int(tokens[0]) |
+ host_port = int(tokens[1]) |
+ self._host_to_device_port_map[host_port] = device_port |
+ logging.info('Forwarding device port: %d to host port: %d.', device_port, |
+ host_port) |
+ |
+ @staticmethod |
+ def KillHost(build_type): |
+ host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder') |
+ (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
+ [host_forwarder_path, 'kill-server']) |
+ if exit_code != 0: |
+ raise Exception('%s exited with %d: %s' % (host_forwarder_path, |
+ exit_code, output)) |
+ |
+ @staticmethod |
+ def KillDevice(adb): |
+ logging.info('Killing device_forwarder.') |
+ timeout_sec = 5 |
+ processes_killed = adb.KillAllBlocking('device_forwarder', timeout_sec) |
if not processes_killed: |
- pids = self._adb.ExtractPid('device_forwarder') |
+ pids = adb.ExtractPid('device_forwarder') |
if pids: |
raise Exception('Timed out while killing device_forwarder') |
- def _CloseProcess(self): |
- if self._host_process: |
- self._host_process.close() |
- if self._device_process: |
- self._device_process.close() |
- if self._adb_forward_process: |
- self._adb_forward_process.close() |
- self._host_process = None |
- self._device_process = None |
- self._adb_forward_process = None |
- |
def DevicePortForHostPort(self, host_port): |
"""Get the device port that corresponds to a given host port.""" |
return self._host_to_device_port_map.get(host_port) |
def Close(self): |
"""Terminate the forwarder process.""" |
- self._CloseProcess() |
+ if self._device_process: |
+ self._device_process.close() |
+ self._device_process = None |