Diff
Modified: trunk/Tools/ChangeLog (204658 => 204659)
--- trunk/Tools/ChangeLog 2016-08-19 22:13:00 UTC (rev 204658)
+++ trunk/Tools/ChangeLog 2016-08-19 22:13:15 UTC (rev 204659)
@@ -1,3 +1,63 @@
+2016-08-18 Simon Fraser <[email protected]>
+
+ REGRESSION (r204477): Running LayoutTests on ios-simulator became ~15 minutes slower
+ https://bugs.webkit.org/show_bug.cgi?id=160985
+
+ Reviewed by Alexey Proskuryakov.
+
+ r204477 removed @memoized on a couple of ios.py functions, causing them to instantiate
+ a Simulator() on every call, which causes 'xcrun simctl list' to run. The functions
+ must not be @memoized, because their return value depends on the value of simulator_device_type().
+
+ Fix by adding some global state in simulator.py that tracks the created devices
+ in a worker number -> Device dictionary. Explicitly create devices in _create_simulators(),
+ and delete them in clean_up_test_run().
+
+ Also explicitly called 'xcrun simctl shutdown' to shut down devices, since it seems
+ that killing the Simulator apps isn't enough.
+
+ Simulator tracks the devices in a global dictionary, since state needs to persist
+ across different instances of IOSSimulatorPort.
+
+ Annoyingly, the "Command line:" dumping tried to access a device before we'd done
+ any setup. Rather than implicitly creating a device here (which the old code did),
+ override the more clearly named driver_cmd_line_for_logging() in IOSSimulatorPort
+ and set flag to say that device_id_for_worker_number() doesn't need to return a real
+ device id.
+
+ * Scripts/webkitpy/layout_tests/views/printing.py:
+ (print_options):
+ (Printer.print_config):
+ * Scripts/webkitpy/port/base.py:
+ (Port.driver_cmd_line_for_logging):
+ (Port.driver_cmd_line): Deleted.
+ * Scripts/webkitpy/port/driver.py:
+ (IOSSimulatorDriver.cmd_line):
+ * Scripts/webkitpy/port/ios.py:
+ (IOSSimulatorPort.__init__):
+ (IOSSimulatorPort.driver_cmd_line_for_logging):
+ (IOSSimulatorPort._create_simulators):
+ (IOSSimulatorPort.setup_test_run):
+ (IOSSimulatorPort.clean_up_test_run):
+ (IOSSimulatorPort._create_device):
+ (IOSSimulatorPort):
+ (IOSSimulatorPort._remove_device):
+ (IOSSimulatorPort._testing_device):
+ (IOSSimulatorPort.device_id_for_worker_number):
+ (IOSSimulatorPort._set_device_class): Deleted.
+ (IOSSimulatorPort.testing_device): Deleted.
+ * Scripts/webkitpy/port/port_testcase.py:
+ (PortTestCase.test_driver_cmd_line):
+ * Scripts/webkitpy/xcode/simulator.py:
+ (Device.shutdown):
+ (Device.delete):
+ (Device.reset):
+ (Simulator.create_device):
+ (Simulator.remove_device):
+ (Simulator.device_number):
+ (Simulator.device_state_description):
+ (Simulator.wait_until_device_is_in_state):
+
2016-08-19 Alexey Proskuryakov <[email protected]>
Adopt SimServiceContext in LayoutTestRelay
Modified: trunk/Tools/Scripts/webkitpy/layout_tests/views/printing.py (204658 => 204659)
--- trunk/Tools/Scripts/webkitpy/layout_tests/views/printing.py 2016-08-19 22:13:00 UTC (rev 204658)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/views/printing.py 2016-08-19 22:13:15 UTC (rev 204659)
@@ -94,7 +94,7 @@
self._print_default("Regular timeout: %s, slow test timeout: %s" %
(self._options.time_out_ms, self._options.slow_time_out_ms))
- self._print_default('Command line: ' + ' '.join(self._port.driver_cmd_line()))
+ self._print_default('Command line: ' + ' '.join(self._port.driver_cmd_line_for_logging()))
self._print_default('')
def print_found(self, num_all_test_files, num_to_run, repeat_each, iterations):
Modified: trunk/Tools/Scripts/webkitpy/port/base.py (204658 => 204659)
--- trunk/Tools/Scripts/webkitpy/port/base.py 2016-08-19 22:13:00 UTC (rev 204658)
+++ trunk/Tools/Scripts/webkitpy/port/base.py 2016-08-19 22:13:15 UTC (rev 204659)
@@ -658,7 +658,7 @@
return test_name + '/'
return test_name
- def driver_cmd_line(self):
+ def driver_cmd_line_for_logging(self):
"""Prints the DRT command line that will be used."""
driver = self.create_driver(0)
return driver.cmd_line(self.get_option('pixel_tests'), [])
Modified: trunk/Tools/Scripts/webkitpy/port/driver.py (204658 => 204659)
--- trunk/Tools/Scripts/webkitpy/port/driver.py 2016-08-19 22:13:00 UTC (rev 204658)
+++ trunk/Tools/Scripts/webkitpy/port/driver.py 2016-08-19 22:13:15 UTC (rev 204659)
@@ -600,7 +600,7 @@
product_dir = self._port._build_path()
relay_args = [
'-developerDir', self._port.developer_dir,
- '-udid', self._port.testing_device(self._worker_number).udid,
+ '-udid', self._port.device_id_for_worker_number(self._worker_number),
'-productDir', product_dir,
'-app', dump_tool,
]
Modified: trunk/Tools/Scripts/webkitpy/port/ios.py (204658 => 204659)
--- trunk/Tools/Scripts/webkitpy/port/ios.py 2016-08-19 22:13:00 UTC (rev 204658)
+++ trunk/Tools/Scripts/webkitpy/port/ios.py 2016-08-19 22:13:15 UTC (rev 204659)
@@ -98,6 +98,7 @@
super(IOSSimulatorPort, self).__init__(host, port_name, **kwargs)
optional_device_class = self.get_option('device_class')
+ self._printing_cmd_line = False
self._device_class = optional_device_class if optional_device_class else self.DEFAULT_DEVICE_CLASS
_log.debug('IOSSimulatorPort _device_class is %s', self._device_class)
@@ -108,6 +109,13 @@
return 'WebKitTestRunnerApp.app'
return 'DumpRenderTree.app'
+ def driver_cmd_line_for_logging(self):
+ # Avoid spinning up devices just for logging the commandline.
+ self._printing_cmd_line = True
+ result = super(IOSSimulatorPort, self).driver_cmd_line_for_logging()
+ self._printing_cmd_line = False
+ return result
+
@property
@memoized
def simulator_runtime(self):
@@ -224,7 +232,6 @@
return list(reversed([self._filesystem.join(self._webkit_baseline_path(p), 'TestExpectations') for p in self.baseline_search_path()]))
def _set_device_class(self, device_class):
- # Ideally we'd ensure that no simulators are running when this is called.
self._device_class = device_class if device_class else self.DEFAULT_DEVICE_CLASS
def _create_simulators(self):
@@ -236,9 +243,13 @@
self._createSimulatorApps()
for i in xrange(self.child_processes()):
- Simulator.wait_until_device_is_in_state(self.testing_device(i).udid, Simulator.DeviceState.SHUTDOWN)
- Simulator.reset_device(self.testing_device(i).udid)
+ self._create_device(i)
+ for i in xrange(self.child_processes()):
+ device_udid = self._testing_device(i).udid
+ Simulator.wait_until_device_is_in_state(device_udid, Simulator.DeviceState.SHUTDOWN)
+ Simulator.reset_device(device_udid)
+
def setup_test_run(self, device_class=None):
mac_os_version = self.host.platform.os_version
@@ -250,7 +261,7 @@
self._create_simulators()
for i in xrange(self.child_processes()):
- device_udid = self.testing_device(i).udid
+ device_udid = self._testing_device(i).udid
_log.debug('testing device %s has udid %s', i, device_udid)
# FIXME: <rdar://problem/20916140> Switch to using CoreSimulator.framework for launching and quitting iOS Simulator
@@ -263,10 +274,10 @@
_log.info('Waiting for all iOS Simulators to finish booting.')
for i in xrange(self.child_processes()):
- Simulator.wait_until_device_is_booted(self.testing_device(i).udid)
+ Simulator.wait_until_device_is_booted(self._testing_device(i).udid)
def _quit_ios_simulator(self):
- _log.debug("_quit_ios_simulator")
+ _log.debug("_quit_ios_simulator killing all Simulator processes")
# FIXME: We should kill only the Simulators we started.
subprocess.call(["killall", "-9", "-m", "Simulator"])
@@ -284,7 +295,9 @@
for i in xrange(self.child_processes()):
simulator_path = self.get_simulator_path(i)
- device_udid = self.testing_device(i).udid
+ device_udid = self._testing_device(i).udid
+ self._remove_device(i)
+
if not os.path.exists(simulator_path):
continue
try:
@@ -301,7 +314,6 @@
_log.debug('rmtree %s', saved_state_path)
self._filesystem.rmtree(saved_state_path)
- Simulator().delete_device(device_udid)
except:
_log.warning('Unable to remove Simulator' + str(i))
@@ -369,13 +381,21 @@
return stderr, None
return stderr, crash_log
- def testing_device(self, number):
- # FIXME: rather than calling lookup_or_create_device every time, we should just store a mapping of
- # number to device_udid.
- device_type = self.simulator_device_type()
- _log.debug(' testing_device %s using device_type %s', number, device_type)
- return Simulator().lookup_or_create_device(device_type.name + ' WebKit Tester' + str(number), device_type, self.simulator_runtime)
+ def _create_device(self, number):
+ return Simulator.create_device(number, self.simulator_device_type(), self.simulator_runtime)
+ def _remove_device(self, number):
+ Simulator.remove_device(number)
+
+ def _testing_device(self, number):
+ return Simulator.device_number(number)
+
+ # This is only exposed so that IOSSimulatorDriver can use it.
+ def device_id_for_worker_number(self, number):
+ if self._printing_cmd_line:
+ return '<dummy id>'
+ return self._testing_device(number).udid
+
def get_simulator_path(self, suffix=""):
return os.path.join(self.SIMULATOR_DIRECTORY, "Simulator" + str(suffix) + ".app")
Modified: trunk/Tools/Scripts/webkitpy/port/port_testcase.py (204658 => 204659)
--- trunk/Tools/Scripts/webkitpy/port/port_testcase.py 2016-08-19 22:13:00 UTC (rev 204658)
+++ trunk/Tools/Scripts/webkitpy/port/port_testcase.py 2016-08-19 22:13:15 UTC (rev 204659)
@@ -100,11 +100,11 @@
def test_driver_cmd_line(self):
port = self.make_port()
- self.assertTrue(len(port.driver_cmd_line()))
+ self.assertTrue(len(port.driver_cmd_line_for_logging()))
options = MockOptions(additional_drt_flag=['--foo=bar', '--foo=baz'])
port = self.make_port(options=options)
- cmd_line = port.driver_cmd_line()
+ cmd_line = port.driver_cmd_line_for_logging()
self.assertTrue('--foo=bar' in cmd_line)
self.assertTrue('--foo=baz' in cmd_line)
Modified: trunk/Tools/Scripts/webkitpy/xcode/simulator.py (204658 => 204659)
--- trunk/Tools/Scripts/webkitpy/xcode/simulator.py 2016-08-19 22:13:00 UTC (rev 204658)
+++ trunk/Tools/Scripts/webkitpy/xcode/simulator.py 2016-08-19 22:13:15 UTC (rev 204659)
@@ -218,6 +218,23 @@
return Simulator().find_device_by_udid(device_udid)
@classmethod
+ def shutdown(cls, udid):
+ """
+ Shut down the given CoreSimulator device.
+ :param udid: The udid of the device.
+ :type udid: str
+ """
+ device_state = Simulator.device_state(udid)
+ if device_state == Simulator.DeviceState.BOOTING or device_state == Simulator.DeviceState.BOOTED:
+ try:
+ _log.debug('xcrun simctl shutdown %s', udid)
+ subprocess.check_call(['xcrun', 'simctl', 'shutdown', udid])
+ except subprocess.CalledProcessError:
+ raise RuntimeError('"xcrun simctl shutdown" failed')
+
+ Simulator.wait_until_device_is_in_state(udid, Simulator.DeviceState.SHUTDOWN)
+
+ @classmethod
def delete(cls, udid):
"""
Delete the given CoreSimulator device.
@@ -224,8 +241,7 @@
:param udid: The udid of the device.
:type udid: str
"""
- _log.debug('deleting device %s', udid)
- Simulator.wait_until_device_is_in_state(udid, Simulator.DeviceState.SHUTDOWN)
+ Device.shutdown(udid)
try:
_log.debug('xcrun simctl delete %s', udid)
subprocess.check_call(['xcrun', 'simctl', 'delete', udid])
@@ -239,8 +255,7 @@
:param udid: The udid of the device.
:type udid: str
"""
- _log.debug('resetting device %s', udid)
- Simulator.wait_until_device_is_in_state(udid, Simulator.DeviceState.SHUTDOWN)
+ Device.shutdown(udid)
try:
_log.debug('xcrun simctl erase %s', udid)
subprocess.check_call(['xcrun', 'simctl', 'erase', udid])
@@ -281,6 +296,8 @@
devices_re = re.compile(
'\s*(?P<name>[^(]+ )\((?P<udid>[^)]+)\) \((?P<state>[^)]+)\)( \((?P<availability>[^)]+)\))?')
+ _managed_devices = {}
+
def __init__(self, host=None):
self._host = host or Host()
self.runtimes = []
@@ -296,7 +313,41 @@
BOOTED = 3
SHUTTING_DOWN = 4
+ NAME_FOR_STATE = [
+ 'CREATING',
+ 'SHUTDOWN',
+ 'BOOTING',
+ 'BOOTED',
+ 'SHUTTING_DOWN'
+ ]
+
@staticmethod
+ def create_device(number, device_type, runtime):
+ device = Simulator().lookup_or_create_device(device_type.name + ' WebKit Tester' + str(number), device_type, runtime)
+ _log.debug('created device {} {}'.format(number, device))
+ assert(len(Simulator._managed_devices) == number)
+ Simulator._managed_devices[number] = device
+
+ @staticmethod
+ def remove_device(number):
+ if not Simulator._managed_devices[number]:
+ return
+ device_udid = Simulator._managed_devices[number].udid
+ _log.debug('removing device {} {}'.format(number, device_udid))
+ del Simulator._managed_devices[number]
+ Simulator.delete_device(device_udid)
+
+ @staticmethod
+ def device_number(number):
+ return Simulator._managed_devices[number]
+
+ @staticmethod
+ def device_state_description(state):
+ if (state == Simulator.DeviceState.DOES_NOT_EXIST):
+ return 'DOES_NOT_EXIST'
+ return Simulator.NAME_FOR_STATE[state]
+
+ @staticmethod
def wait_until_device_is_booted(udid, timeout_seconds=60 * 5):
Simulator.wait_until_device_is_in_state(udid, Simulator.DeviceState.BOOTED, timeout_seconds)
with timeout(seconds=timeout_seconds):
@@ -315,17 +366,17 @@
@staticmethod
def wait_until_device_is_in_state(udid, wait_until_state, timeout_seconds=60 * 5):
- _log.debug('waiting for device %s to enter state %s with timeout %s', udid, wait_until_state, timeout_seconds)
+ _log.debug('waiting for device %s to enter state %s with timeout %s', udid, Simulator.device_state_description(wait_until_state), timeout_seconds)
with timeout(seconds=timeout_seconds):
device_state = Simulator.device_state(udid)
while (device_state != wait_until_state):
device_state = Simulator.device_state(udid)
- _log.debug(' device state %s', device_state)
+ _log.debug(' device state %s', Simulator.device_state_description(device_state))
time.sleep(0.5)
end_state = Simulator.device_state(udid)
if (end_state != wait_until_state):
- raise RuntimeError('Timed out waiting for simulator device to enter state {0}; current state is {1}'.format(wait_until_state, end_state))
+ raise RuntimeError('Timed out waiting for simulator device to enter state {0}; current state is {1}'.format(Simulator.device_state_description(wait_until_state), Simulator.device_state_description(end_state)))
@staticmethod
def device_state(udid):