Reland r212020: Move Python setup/tear down logic into Forwarder ...
Forwarder used to be a pain to setup/tear down across all the various harnesses. This CL should hopefully solve these issues by hiding these implementation details. The host daemon is now killed once the first time that the Forwarder class is used and the daemon running on the devices is also killed the first time a port is forwarded for a specific device. TBR=bulach@chromium.org BUG=242846 Review URL: https://codereview.chromium.org/19550004 git-svn-id: http://src.chromium.org/svn/trunk/src/build@212582 4ff67af0-8c30-449e-8e8b-ad334ec8d88c
This commit is contained in:
Родитель
a4a384800a
Коммит
5f1683c854
|
@ -17,7 +17,6 @@ import time
|
|||
|
||||
from pylib import android_commands, forwarder
|
||||
from pylib.utils import run_tests_helper
|
||||
from pylib.valgrind_tools import CreateTool
|
||||
|
||||
|
||||
def main(argv):
|
||||
|
@ -51,17 +50,14 @@ def main(argv):
|
|||
sys.exit(1)
|
||||
|
||||
adb = android_commands.AndroidCommands(options.device)
|
||||
tool = CreateTool(None, adb)
|
||||
forwarder_instance = forwarder.Forwarder(adb, options.build_type)
|
||||
try:
|
||||
forwarder_instance.Run(port_pairs, tool)
|
||||
forwarder.Forwarder.Map(port_pairs, adb, options.build_type)
|
||||
while True:
|
||||
time.sleep(60)
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(0)
|
||||
finally:
|
||||
forwarder_instance.Close()
|
||||
|
||||
forwarder.Forwarder.UnmapAllDevicePorts(adb)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
|
|
|
@ -165,7 +165,6 @@ def _PrintAllStepsOutput(steps):
|
|||
|
||||
def _KillPendingServers():
|
||||
for retry in range(5):
|
||||
forwarder.Forwarder.KillHost()
|
||||
for server in ['lighttpd', 'web-page-replay']:
|
||||
pids = cmd_helper.GetCmdOutput(['pgrep', '-f', server])
|
||||
pids = [pid.strip() for pid in pids.split('\n') if pid.strip()]
|
||||
|
@ -201,6 +200,8 @@ def main(argv):
|
|||
# previous runs.
|
||||
_KillPendingServers()
|
||||
|
||||
forwarder.Forwarder.UseMultiprocessing()
|
||||
|
||||
# Reset the test port allocation. It's important to do it before starting
|
||||
# to dispatch any step.
|
||||
if not ports.ResetTestServerPortAllocation():
|
||||
|
|
|
@ -44,14 +44,12 @@ class BaseTestRunner(object):
|
|||
self.adb = android_commands.AndroidCommands(device=device)
|
||||
self.tool = CreateTool(tool, self.adb)
|
||||
self._http_server = None
|
||||
self._forwarder = None
|
||||
self._forwarder_device_port = 8000
|
||||
self.forwarder_base_url = ('http://localhost:%d' %
|
||||
self._forwarder_device_port)
|
||||
self.flags = FlagChanger(self.adb)
|
||||
self.flags.AddFlags(['--disable-fre'])
|
||||
self._spawning_server = None
|
||||
self._spawner_forwarder = None
|
||||
# We will allocate port for test server spawner when calling method
|
||||
# LaunchChromeTestServerSpawner and allocate port for test server when
|
||||
# starting it in TestServerThread.
|
||||
|
@ -90,7 +88,6 @@ class BaseTestRunner(object):
|
|||
|
||||
def SetUp(self):
|
||||
"""Run once before all tests are run."""
|
||||
Forwarder.KillDevice(self.adb, self.tool)
|
||||
self.InstallTestPackage()
|
||||
push_size_before = self.adb.GetPushSizeInfo()
|
||||
if self._push_deps:
|
||||
|
@ -128,49 +125,47 @@ class BaseTestRunner(object):
|
|||
self._http_server.port)
|
||||
else:
|
||||
logging.critical('Failed to start http server')
|
||||
self.StartForwarderForHttpServer()
|
||||
self._ForwardPortsForHttpServer()
|
||||
return (self._forwarder_device_port, self._http_server.port)
|
||||
|
||||
def _ForwardPort(self, port_pairs):
|
||||
"""Creates a forwarder instance if needed and forward a port."""
|
||||
if not self._forwarder:
|
||||
self._forwarder = Forwarder(self.adb, self.build_type)
|
||||
self._forwarder.Run(port_pairs, self.tool)
|
||||
def _ForwardPorts(self, port_pairs):
|
||||
"""Forwards a port."""
|
||||
Forwarder.Map(port_pairs, self.adb, self.build_type, self.tool)
|
||||
|
||||
def _UnmapPorts(self, port_pairs):
|
||||
"""Unmap previously forwarded ports."""
|
||||
for (device_port, _) in port_pairs:
|
||||
Forwarder.UnmapDevicePort(device_port, self.adb)
|
||||
|
||||
# Deprecated: Use ForwardPorts instead.
|
||||
def StartForwarder(self, port_pairs):
|
||||
"""Starts TCP traffic forwarding for the given |port_pairs|.
|
||||
|
||||
Args:
|
||||
host_port_pairs: A list of (device_port, local_port) tuples to forward.
|
||||
"""
|
||||
self._ForwardPort(port_pairs)
|
||||
self._ForwardPorts(port_pairs)
|
||||
|
||||
def StartForwarderForHttpServer(self):
|
||||
def _ForwardPortsForHttpServer(self):
|
||||
"""Starts a forwarder for the HTTP server.
|
||||
|
||||
The forwarder forwards HTTP requests and responses between host and device.
|
||||
"""
|
||||
self._ForwardPort([(self._forwarder_device_port, self._http_server.port)])
|
||||
self._ForwardPorts([(self._forwarder_device_port, self._http_server.port)])
|
||||
|
||||
def RestartHttpServerForwarderIfNecessary(self):
|
||||
def _RestartHttpServerForwarderIfNecessary(self):
|
||||
"""Restarts the forwarder if it's not open."""
|
||||
# Checks to see if the http server port is being used. If not forwards the
|
||||
# request.
|
||||
# TODO(dtrainor): This is not always reliable because sometimes the port
|
||||
# will be left open even after the forwarder has been killed.
|
||||
if not ports.IsDevicePortUsed(self.adb,
|
||||
self._forwarder_device_port):
|
||||
self.StartForwarderForHttpServer()
|
||||
if not ports.IsDevicePortUsed(self.adb, self._forwarder_device_port):
|
||||
self._ForwardPortsForHttpServer()
|
||||
|
||||
def ShutdownHelperToolsForTestSuite(self):
|
||||
"""Shuts down the server and the forwarder."""
|
||||
# Forwarders should be killed before the actual servers they're forwarding
|
||||
# to as they are clients potentially with open connections and to allow for
|
||||
# proper hand-shake/shutdown.
|
||||
Forwarder.KillDevice(self.adb, self.tool)
|
||||
if self._forwarder:
|
||||
self._forwarder.Close()
|
||||
if self._http_server:
|
||||
self._UnmapPorts([(self._forwarder_device_port, self._http_server.port)])
|
||||
self._http_server.ShutdownHttpServer()
|
||||
if self._spawning_server:
|
||||
self._spawning_server.Stop()
|
||||
|
@ -195,12 +190,11 @@ class BaseTestRunner(object):
|
|||
# Try 3 times to launch test spawner server.
|
||||
for i in xrange(0, 3):
|
||||
self.test_server_spawner_port = ports.AllocateTestServerPort()
|
||||
self._ForwardPort(
|
||||
self._ForwardPorts(
|
||||
[(self.test_server_spawner_port, self.test_server_spawner_port)])
|
||||
self._spawning_server = SpawningServer(self.test_server_spawner_port,
|
||||
self.adb,
|
||||
self.tool,
|
||||
self._forwarder,
|
||||
self.build_type)
|
||||
self._spawning_server.Start()
|
||||
server_ready, error_msg = ports.IsHttpServerConnectable(
|
||||
|
|
|
@ -9,7 +9,6 @@ import threading
|
|||
|
||||
from pylib import android_commands
|
||||
from pylib import constants
|
||||
from pylib import forwarder
|
||||
from pylib.utils import reraiser_thread
|
||||
from pylib.utils import watchdog_timer
|
||||
|
||||
|
@ -293,7 +292,6 @@ def ShardAndRunTests(runner_factory, devices, tests, build_type='Debug',
|
|||
return (base_test_result.TestRunResults(), constants.ERROR_EXIT_CODE)
|
||||
|
||||
logging.info('Will run %d tests: %s', len(tests), str(tests))
|
||||
forwarder.Forwarder.KillHost(build_type)
|
||||
runners = _CreateRunners(runner_factory, devices, setup_timeout)
|
||||
try:
|
||||
return _RunAllTests(runners, tests, num_retries, test_timeout)
|
||||
|
@ -302,5 +300,3 @@ def ShardAndRunTests(runner_factory, devices, tests, build_type='Debug',
|
|||
_TearDownRunners(runners, setup_timeout)
|
||||
except android_commands.errors.DeviceUnresponsiveError as e:
|
||||
logging.warning('Device unresponsive during TearDown: [%s]', e)
|
||||
finally:
|
||||
forwarder.Forwarder.KillHost(build_type)
|
||||
|
|
|
@ -23,6 +23,7 @@ import urlparse
|
|||
import constants
|
||||
import ports
|
||||
|
||||
from pylib.forwarder import Forwarder
|
||||
|
||||
# Path that are needed to import necessary modules when launching a testserver.
|
||||
os.environ['PYTHONPATH'] = os.environ.get('PYTHONPATH', '') + (':%s:%s:%s:%s:%s'
|
||||
|
@ -99,7 +100,7 @@ def _GetServerTypeCommandLine(server_type):
|
|||
class TestServerThread(threading.Thread):
|
||||
"""A thread to run the test server in a separate process."""
|
||||
|
||||
def __init__(self, ready_event, arguments, adb, tool, forwarder, build_type):
|
||||
def __init__(self, ready_event, arguments, adb, tool, build_type):
|
||||
"""Initialize TestServerThread with the following argument.
|
||||
|
||||
Args:
|
||||
|
@ -107,7 +108,6 @@ class TestServerThread(threading.Thread):
|
|||
arguments: dictionary of arguments to run the test server.
|
||||
adb: instance of AndroidCommands.
|
||||
tool: instance of runtime error detection tool.
|
||||
forwarder: instance of Forwarder.
|
||||
build_type: 'Release' or 'Debug'.
|
||||
"""
|
||||
threading.Thread.__init__(self)
|
||||
|
@ -122,7 +122,6 @@ class TestServerThread(threading.Thread):
|
|||
self.is_ready = False
|
||||
self.host_port = self.arguments['port']
|
||||
assert isinstance(self.host_port, int)
|
||||
self._test_server_forwarder = forwarder
|
||||
# The forwarder device port now is dynamically allocated.
|
||||
self.forwarder_device_port = 0
|
||||
# Anonymous pipe in order to get port info from test server.
|
||||
|
@ -240,11 +239,10 @@ class TestServerThread(threading.Thread):
|
|||
else:
|
||||
self.is_ready = _CheckPortStatus(self.host_port, True)
|
||||
if self.is_ready:
|
||||
self._test_server_forwarder.Run([(0, self.host_port)], self.tool)
|
||||
Forwarder.Map([(0, self.host_port)], self.adb, self.build_type, self.tool)
|
||||
# Check whether the forwarder is ready on the device.
|
||||
self.is_ready = False
|
||||
device_port = self._test_server_forwarder.DevicePortForHostPort(
|
||||
self.host_port)
|
||||
device_port = Forwarder.DevicePortForHostPort(self.host_port)
|
||||
if device_port and _CheckDevicePortStatus(self.adb, device_port):
|
||||
self.is_ready = True
|
||||
self.forwarder_device_port = device_port
|
||||
|
@ -254,7 +252,7 @@ class TestServerThread(threading.Thread):
|
|||
_WaitUntil(lambda: self.stop_flag, max_attempts=sys.maxint)
|
||||
if self.process.poll() is None:
|
||||
self.process.kill()
|
||||
self._test_server_forwarder.UnmapDevicePort(self.forwarder_device_port)
|
||||
Forwarder.UnmapDevicePort(self.forwarder_device_port, self.adb)
|
||||
self.process = None
|
||||
self.is_ready = False
|
||||
if self.pipe_out:
|
||||
|
@ -324,7 +322,6 @@ class SpawningServerRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||
json.loads(test_server_argument_json),
|
||||
self.server.adb,
|
||||
self.server.tool,
|
||||
self.server.forwarder,
|
||||
self.server.build_type)
|
||||
self.server.test_server_instance.setDaemon(True)
|
||||
self.server.test_server_instance.start()
|
||||
|
@ -392,14 +389,12 @@ class SpawningServerRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||
class SpawningServer(object):
|
||||
"""The class used to start/stop a http server."""
|
||||
|
||||
def __init__(self, test_server_spawner_port, adb, tool, forwarder,
|
||||
build_type):
|
||||
def __init__(self, test_server_spawner_port, adb, tool, build_type):
|
||||
logging.info('Creating new spawner on port: %d.', test_server_spawner_port)
|
||||
self.server = BaseHTTPServer.HTTPServer(('', test_server_spawner_port),
|
||||
SpawningServerRequestHandler)
|
||||
self.server.adb = adb
|
||||
self.server.tool = tool
|
||||
self.server.forwarder = forwarder
|
||||
self.server.test_server_instance = None
|
||||
self.server.build_type = build_type
|
||||
|
||||
|
|
|
@ -2,24 +2,49 @@
|
|||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import fcntl
|
||||
import logging
|
||||
import os
|
||||
import psutil
|
||||
import re
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
import android_commands
|
||||
import cmd_helper
|
||||
import constants
|
||||
|
||||
from pylib import pexpect
|
||||
from pylib import valgrind_tools
|
||||
|
||||
|
||||
def _MakeBinaryPath(build_type, binary_name):
|
||||
return os.path.join(cmd_helper.OutDirectory.get(), build_type, binary_name)
|
||||
|
||||
|
||||
def _GetProcessStartTime(pid):
|
||||
return psutil.Process(pid).create_time
|
||||
|
||||
|
||||
class _FileLock(object):
|
||||
"""With statement-aware implementation of a file lock.
|
||||
|
||||
File locks are needed for cross-process synchronization when the
|
||||
multiprocessing Python module is used.
|
||||
"""
|
||||
def __init__(self, path):
|
||||
self._path = path
|
||||
|
||||
def __enter__(self):
|
||||
self._fd = os.open(self._path, os.O_RDONLY | os.O_CREAT)
|
||||
if self._fd < 0:
|
||||
raise Exception('Could not open file %s for reading' % self._path)
|
||||
fcntl.flock(self._fd, fcntl.LOCK_EX)
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
fcntl.flock(self._fd, fcntl.LOCK_UN)
|
||||
os.close(self._fd)
|
||||
|
||||
|
||||
class Forwarder(object):
|
||||
"""Thread-safe class to manage port forwards from the device to the host."""
|
||||
|
||||
|
@ -28,28 +53,18 @@ class Forwarder(object):
|
|||
_DEVICE_FORWARDER_PATH = (constants.TEST_EXECUTABLE_DIR +
|
||||
'/forwarder/device_forwarder')
|
||||
_LD_LIBRARY_PATH = 'LD_LIBRARY_PATH=%s' % _DEVICE_FORWARDER_FOLDER
|
||||
_LOCK_PATH = '/tmp/chrome.forwarder.lock'
|
||||
_MULTIPROCESSING_ENV_VAR = 'CHROME_FORWARDER_USE_MULTIPROCESSING'
|
||||
|
||||
def __init__(self, adb, build_type):
|
||||
"""Forwards TCP ports on the device back to the host.
|
||||
_instance = None
|
||||
|
||||
Works like adb forward, but in reverse.
|
||||
@staticmethod
|
||||
def UseMultiprocessing():
|
||||
"""Tells the forwarder that multiprocessing is used."""
|
||||
os.environ[Forwarder._MULTIPROCESSING_ENV_VAR] = '1'
|
||||
|
||||
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._device_to_host_port_map = dict()
|
||||
self._host_to_device_port_map = dict()
|
||||
self._device_initialized = False
|
||||
self._host_adb_control_port = 0
|
||||
self._lock = threading.Lock()
|
||||
self._host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder')
|
||||
self._device_forwarder_path_on_host = os.path.join(
|
||||
cmd_helper.OutDirectory.get(), build_type, 'forwarder_dist')
|
||||
|
||||
def Run(self, port_pairs, tool):
|
||||
@staticmethod
|
||||
def Map(port_pairs, adb, build_type='Debug', tool=None):
|
||||
"""Runs the forwarder.
|
||||
|
||||
Args:
|
||||
|
@ -58,24 +73,29 @@ class Forwarder(object):
|
|||
port will by dynamically assigned on the device. You can
|
||||
get the number of the assigned port using the
|
||||
DevicePortForHostPort method.
|
||||
adb: An AndroidCommands instance.
|
||||
tool: Tool class to use to get wrapper, if necessary, for executing the
|
||||
forwarder (see valgrind_tools.py).
|
||||
|
||||
Raises:
|
||||
Exception on failure to forward the port.
|
||||
"""
|
||||
with self._lock:
|
||||
self._InitDeviceLocked(tool)
|
||||
host_name = '127.0.0.1'
|
||||
if not tool:
|
||||
tool = valgrind_tools.CreateTool(None, adb)
|
||||
with _FileLock(Forwarder._LOCK_PATH):
|
||||
instance = Forwarder._GetInstanceLocked(build_type, tool)
|
||||
instance._InitDeviceLocked(adb, tool)
|
||||
|
||||
device_serial = adb.Adb().GetSerialNumber()
|
||||
redirection_commands = [
|
||||
['--serial-id=' + self._adb.Adb().GetSerialNumber(), '--map',
|
||||
str(device), str(host)] for device, host in port_pairs]
|
||||
['--serial-id=' + device_serial, '--map', str(device),
|
||||
str(host)] for device, host in port_pairs]
|
||||
logging.info('Forwarding using commands: %s', redirection_commands)
|
||||
|
||||
for redirection_command in redirection_commands:
|
||||
try:
|
||||
(exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
|
||||
[self._host_forwarder_path] + redirection_command)
|
||||
[instance._host_forwarder_path] + redirection_command)
|
||||
except OSError as e:
|
||||
if e.errno == 2:
|
||||
raise Exception('Unable to start host forwarder. Make sure you have'
|
||||
|
@ -83,83 +103,203 @@ class Forwarder(object):
|
|||
else: raise
|
||||
if exit_code != 0:
|
||||
raise Exception('%s exited with %d:\n%s' % (
|
||||
self._host_forwarder_path, exit_code, '\n'.join(output)))
|
||||
instance._host_forwarder_path, exit_code, '\n'.join(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._device_to_host_port_map[device_port] = host_port
|
||||
self._host_to_device_port_map[host_port] = device_port
|
||||
serial_with_port = (device_serial, device_port)
|
||||
instance._device_to_host_port_map[serial_with_port] = host_port
|
||||
instance._host_to_device_port_map[host_port] = serial_with_port
|
||||
logging.info('Forwarding device port: %d to host port: %d.',
|
||||
device_port, host_port)
|
||||
|
||||
def _InitDeviceLocked(self, tool):
|
||||
"""Initializes the device forwarder process (only once)."""
|
||||
if self._device_initialized:
|
||||
@staticmethod
|
||||
def UnmapDevicePort(device_port, adb):
|
||||
"""Unmaps a previously forwarded device port.
|
||||
|
||||
Args:
|
||||
adb: An AndroidCommands instance.
|
||||
device_port: A previously forwarded port (through Map()).
|
||||
"""
|
||||
with _FileLock(Forwarder._LOCK_PATH):
|
||||
Forwarder._UnmapDevicePortLocked(device_port, adb)
|
||||
|
||||
@staticmethod
|
||||
def UnmapAllDevicePorts(adb):
|
||||
"""Unmaps all the previously forwarded ports for the provided device.
|
||||
|
||||
Args:
|
||||
adb: An AndroidCommands instance.
|
||||
port_pairs: A list of tuples (device_port, host_port) to unmap.
|
||||
"""
|
||||
with _FileLock(Forwarder._LOCK_PATH):
|
||||
port_map = Forwarder._GetInstanceLocked(
|
||||
None, None)._device_to_host_port_map
|
||||
adb_serial = adb.Adb().GetSerialNumber()
|
||||
for ((device_serial, device_port), _) in port_map:
|
||||
if adb_serial == device_serial:
|
||||
Forwarder._UnmapDevicePortLocked(device_port, adb)
|
||||
|
||||
@staticmethod
|
||||
def DevicePortForHostPort(host_port):
|
||||
"""Returns the device port that corresponds to a given host port."""
|
||||
with _FileLock(Forwarder._LOCK_PATH):
|
||||
(device_serial, device_port) = Forwarder._GetInstanceLocked(
|
||||
None, None)._host_to_device_port_map.get(host_port)
|
||||
return device_port
|
||||
|
||||
@staticmethod
|
||||
def _GetInstanceLocked(build_type, tool):
|
||||
"""Returns the singleton instance.
|
||||
|
||||
Note that the global lock must be acquired before calling this method.
|
||||
|
||||
Args:
|
||||
build_type: 'Release' or 'Debug'
|
||||
tool: Tool class to use to get wrapper, if necessary, for executing the
|
||||
forwarder (see valgrind_tools.py).
|
||||
"""
|
||||
if not Forwarder._instance:
|
||||
Forwarder._instance = Forwarder(build_type, tool)
|
||||
return Forwarder._instance
|
||||
|
||||
def __init__(self, build_type, tool):
|
||||
"""Constructs a new instance of Forwarder.
|
||||
|
||||
Note that Forwarder is a singleton therefore this constructor should be
|
||||
called only once.
|
||||
|
||||
Args:
|
||||
build_type: 'Release' or 'Debug'
|
||||
tool: Tool class to use to get wrapper, if necessary, for executing the
|
||||
forwarder (see valgrind_tools.py).
|
||||
"""
|
||||
assert not Forwarder._instance
|
||||
self._build_type = build_type
|
||||
self._tool = tool
|
||||
self._initialized_devices = set()
|
||||
self._device_to_host_port_map = dict()
|
||||
self._host_to_device_port_map = dict()
|
||||
self._host_forwarder_path = _MakeBinaryPath(
|
||||
self._build_type, 'host_forwarder')
|
||||
if not os.path.exists(self._host_forwarder_path):
|
||||
self._build_type = 'Release' if self._build_type == 'Debug' else 'Debug'
|
||||
self._host_forwarder_path = _MakeBinaryPath(
|
||||
self._build_type, 'host_forwarder')
|
||||
assert os.path.exists(
|
||||
self._host_forwarder_path), 'Please build forwarder2'
|
||||
self._device_forwarder_path_on_host = os.path.join(
|
||||
cmd_helper.OutDirectory.get(), self._build_type, 'forwarder_dist')
|
||||
self._InitHostLocked()
|
||||
|
||||
@staticmethod
|
||||
def _UnmapDevicePortLocked(device_port, adb):
|
||||
"""Internal method used by UnmapDevicePort().
|
||||
|
||||
Note that the global lock must be acquired before calling this method.
|
||||
"""
|
||||
instance = Forwarder._GetInstanceLocked(None, None)
|
||||
serial = adb.Adb().GetSerialNumber()
|
||||
serial_with_port = (serial, device_port)
|
||||
if not serial_with_port in instance._device_to_host_port_map:
|
||||
logging.error('Trying to unmap non-forwarded port %d' % device_port)
|
||||
return
|
||||
self._adb.PushIfNeeded(
|
||||
redirection_command = ['--serial-id=' + serial, '--unmap', str(device_port)]
|
||||
(exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
|
||||
[instance._host_forwarder_path] + redirection_command)
|
||||
if exit_code != 0:
|
||||
logging.error('%s exited with %d:\n%s' % (
|
||||
instance._host_forwarder_path, exit_code, '\n'.join(output)))
|
||||
host_port = instance._device_to_host_port_map[serial_with_port]
|
||||
del instance._device_to_host_port_map[serial_with_port]
|
||||
del instance._host_to_device_port_map[host_port]
|
||||
|
||||
@staticmethod
|
||||
def _GetPidForLock():
|
||||
"""Returns the PID used for host_forwarder initialization.
|
||||
|
||||
In case multi-process sharding is used, the PID of the "sharder" is used.
|
||||
The "sharder" is the initial process that forks that is the parent process.
|
||||
By default, multi-processing is not used. In that case the PID of the
|
||||
current process is returned.
|
||||
"""
|
||||
use_multiprocessing = Forwarder._MULTIPROCESSING_ENV_VAR in os.environ
|
||||
return os.getppid() if use_multiprocessing else os.getpid()
|
||||
|
||||
def _InitHostLocked(self):
|
||||
"""Initializes the host forwarder daemon.
|
||||
|
||||
Note that the global lock must be acquired before calling this method. This
|
||||
method kills any existing host_forwarder process that could be stale.
|
||||
"""
|
||||
# See if the host_forwarder daemon was already initialized by a concurrent
|
||||
# process or thread (in case multi-process sharding is not used).
|
||||
pid_for_lock = Forwarder._GetPidForLock()
|
||||
fd = os.open(Forwarder._LOCK_PATH, os.O_RDWR | os.O_CREAT)
|
||||
with os.fdopen(fd, 'r+') as pid_file:
|
||||
pid_with_start_time = pid_file.readline()
|
||||
if pid_with_start_time:
|
||||
(pid, process_start_time) = pid_with_start_time.split(':')
|
||||
if pid == str(pid_for_lock):
|
||||
if process_start_time == str(_GetProcessStartTime(pid_for_lock)):
|
||||
return
|
||||
self._KillHostLocked()
|
||||
pid_file.seek(0)
|
||||
pid_file.write(
|
||||
'%s:%s' % (pid_for_lock, str(_GetProcessStartTime(pid_for_lock))))
|
||||
|
||||
def _InitDeviceLocked(self, adb, tool):
|
||||
"""Initializes the device_forwarder daemon for a specific device (once).
|
||||
|
||||
Note that the global lock must be acquired before calling this method. This
|
||||
method kills any existing device_forwarder daemon on the device that could
|
||||
be stale, pushes the latest version of the daemon (to the device) and starts
|
||||
it.
|
||||
|
||||
Args:
|
||||
adb: An AndroidCommands instance.
|
||||
tool: Tool class to use to get wrapper, if necessary, for executing the
|
||||
forwarder (see valgrind_tools.py).
|
||||
"""
|
||||
device_serial = adb.Adb().GetSerialNumber()
|
||||
if device_serial in self._initialized_devices:
|
||||
return
|
||||
Forwarder._KillDeviceLocked(adb, tool)
|
||||
adb.PushIfNeeded(
|
||||
self._device_forwarder_path_on_host,
|
||||
Forwarder._DEVICE_FORWARDER_FOLDER)
|
||||
(exit_code, output) = self._adb.GetShellCommandStatusAndOutput(
|
||||
(exit_code, output) = adb.GetShellCommandStatusAndOutput(
|
||||
'%s %s %s' % (Forwarder._LD_LIBRARY_PATH, tool.GetUtilWrapper(),
|
||||
Forwarder._DEVICE_FORWARDER_PATH))
|
||||
if exit_code != 0:
|
||||
raise Exception(
|
||||
'Failed to start device forwarder:\n%s' % '\n'.join(output))
|
||||
self._device_initialized = True
|
||||
self._initialized_devices.add(device_serial)
|
||||
|
||||
def UnmapDevicePort(self, device_port):
|
||||
"""Unmaps a previously forwarded device port.
|
||||
|
||||
Args:
|
||||
device_port: A previously forwarded port (through Run()).
|
||||
"""
|
||||
with self._lock:
|
||||
self._UnmapDevicePortInternalLocked(device_port)
|
||||
|
||||
def _UnmapDevicePortInternalLocked(self, device_port):
|
||||
if not device_port in self._device_to_host_port_map:
|
||||
return
|
||||
redirection_command = [
|
||||
'--serial-id=' + self._adb.Adb().GetSerialNumber(), '--unmap',
|
||||
str(device_port)]
|
||||
(exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
|
||||
[self._host_forwarder_path] + redirection_command)
|
||||
if exit_code != 0:
|
||||
logging.error('%s exited with %d:\n%s' % (
|
||||
self._host_forwarder_path, exit_code, '\n'.join(output)))
|
||||
host_port = self._device_to_host_port_map[device_port]
|
||||
del self._device_to_host_port_map[device_port]
|
||||
del self._host_to_device_port_map[host_port]
|
||||
|
||||
@staticmethod
|
||||
def KillHost(build_type='Debug'):
|
||||
def _KillHostLocked(self):
|
||||
"""Kills the forwarder process running on the host.
|
||||
|
||||
Args:
|
||||
build_type: 'Release' or 'Debug' (default='Debug')
|
||||
Note that the global lock must be acquired before calling this method.
|
||||
"""
|
||||
logging.info('Killing host_forwarder.')
|
||||
host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder')
|
||||
if not os.path.exists(host_forwarder_path):
|
||||
host_forwarder_path = _MakeBinaryPath(
|
||||
'Release' if build_type == 'Debug' else 'Debug', 'host_forwarder')
|
||||
assert os.path.exists(host_forwarder_path), 'Please build forwarder2'
|
||||
(exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
|
||||
[host_forwarder_path, '--kill-server'])
|
||||
[self._host_forwarder_path, '--kill-server'])
|
||||
if exit_code != 0:
|
||||
(exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
|
||||
['pkill', 'host_forwarder'])
|
||||
['pkill', '-9', 'host_forwarder'])
|
||||
if exit_code != 0:
|
||||
raise Exception('%s exited with %d:\n%s' % (
|
||||
host_forwarder_path, exit_code, '\n'.join(output)))
|
||||
self._host_forwarder_path, exit_code, '\n'.join(output)))
|
||||
|
||||
@staticmethod
|
||||
def KillDevice(adb, tool):
|
||||
def _KillDeviceLocked(adb, tool):
|
||||
"""Kills the forwarder process running on the device.
|
||||
|
||||
Note that the global lock must be acquired before calling this method.
|
||||
|
||||
Args:
|
||||
adb: Instance of AndroidCommands for talking to the device.
|
||||
tool: Wrapper tool (e.g. valgrind) that can be used to execute the device
|
||||
|
@ -180,14 +320,3 @@ class Forwarder(object):
|
|||
pids = adb.ExtractPid('device_forwarder')
|
||||
if pids:
|
||||
raise Exception('Timed out while killing device_forwarder')
|
||||
|
||||
def DevicePortForHostPort(self, host_port):
|
||||
"""Returns the device port that corresponds to a given host port."""
|
||||
with self._lock:
|
||||
return self._host_to_device_port_map.get(host_port)
|
||||
|
||||
def Close(self):
|
||||
"""Releases the previously forwarded ports."""
|
||||
with self._lock:
|
||||
for device_port in self._device_to_host_port_map.copy():
|
||||
self._UnmapDevicePortInternalLocked(device_port)
|
||||
|
|
|
@ -122,7 +122,7 @@ class PythonTestSharder(object):
|
|||
final_results = base_test_result.TestRunResults()
|
||||
tests_to_run = self.tests
|
||||
|
||||
Forwarder.KillHost()
|
||||
Forwarder.UseMultiprocessing()
|
||||
|
||||
for retry in xrange(self.retries):
|
||||
logging.warning('Try %d of %d', retry + 1, self.retries)
|
||||
|
|
|
@ -153,11 +153,13 @@ class TestRunner(base_test_runner.BaseTestRunner):
|
|||
http_server_ports = self.LaunchTestHttpServer(
|
||||
os.path.join(constants.DIR_SOURCE_ROOT), self._lighttp_port)
|
||||
if self.ports_to_forward:
|
||||
self.StartForwarder([(port, port) for port in self.ports_to_forward])
|
||||
self.ForwardPorts([(port, port) for port in self.ports_to_forward])
|
||||
self.flags.AddFlags(['--enable-test-intents'])
|
||||
|
||||
def TearDown(self):
|
||||
"""Cleans up the test harness and saves outstanding data from test run."""
|
||||
if self.ports_to_forward:
|
||||
self._UnmapPortPairs(self.ports_to_forward)
|
||||
super(TestRunner, self).TearDown()
|
||||
|
||||
def TestSetup(self, test):
|
||||
|
@ -171,7 +173,7 @@ class TestRunner(base_test_runner.BaseTestRunner):
|
|||
self.tool.SetupEnvironment()
|
||||
|
||||
# Make sure the forwarder is still running.
|
||||
self.RestartHttpServerForwarderIfNecessary()
|
||||
self._RestartHttpServerForwarderIfNecessary()
|
||||
|
||||
def _IsPerfTest(self, test):
|
||||
"""Determines whether a test is a performance test.
|
||||
|
|
Загрузка…
Ссылка в новой задаче