Index: Tools/Scripts/webkitpy/layout_tests/port/driver.py |
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/driver.py b/Tools/Scripts/webkitpy/layout_tests/port/driver.py |
index e7a439d04baa9cbc547e81bcd3f8a5360751853a..fd44a53e7f034f5a436922c8cea7e97bc8760213 100644 |
--- a/Tools/Scripts/webkitpy/layout_tests/port/driver.py |
+++ b/Tools/Scripts/webkitpy/layout_tests/port/driver.py |
@@ -60,7 +60,7 @@ class DriverOutput(object): |
def __init__(self, text, image, image_hash, audio, crash=False, |
test_time=0, measurements=None, timeout=False, error='', crashed_process_name='??', |
- crashed_pid=None, crash_log=None, pid=None): |
+ crashed_pid=None, crash_log=None, leak=False, leak_log=None, pid=None): |
# FIXME: Args could be renamed to better clarify what they do. |
self.text = text |
self.image = image # May be empty-string if the test crashes. |
@@ -71,6 +71,8 @@ class DriverOutput(object): |
self.crashed_process_name = crashed_process_name |
self.crashed_pid = crashed_pid |
self.crash_log = crash_log |
+ self.leak = leak |
+ self.leak_log = leak_log |
self.test_time = test_time |
self.measurements = measurements |
self.timeout = timeout |
@@ -102,17 +104,22 @@ class Driver(object): |
self._no_timeout = no_timeout |
self._driver_tempdir = None |
- # WebKitTestRunner can report back subprocess crashes by printing |
+ # content_shell can report back subprocess crashes by printing |
# "#CRASHED - PROCESSNAME". Since those can happen at any time |
# and ServerProcess won't be aware of them (since the actual tool |
# didn't crash, just a subprocess) we record the crashed subprocess name here. |
self._crashed_process_name = None |
self._crashed_pid = None |
- # WebKitTestRunner can report back subprocesses that became unresponsive |
+ # content_shell can report back subprocesses that became unresponsive |
# This could mean they crashed. |
self._subprocess_was_unresponsive = False |
+ # content_shell can report back subprocess DOM-object leaks by printing |
+ # "#LEAK". This leak detection is enabled only when the flag |
+ # --enable-leak-detection is passed to content_shell. |
+ self._leaked = False |
+ |
# stderr reading is scoped on a per-test (not per-block) basis, so we store the accumulated |
# stderr output, as well as if we've seen #EOF on this driver instance. |
# FIXME: We should probably remove _read_first_block and _read_optional_image_block and |
@@ -158,8 +165,9 @@ class Driver(object): |
crashed = self.has_crashed() |
timed_out = self._server_process.timed_out |
pid = self._server_process.pid() |
+ leaked = self._leaked |
- if stop_when_done or crashed or timed_out: |
+ if stop_when_done or crashed or timed_out or leaked: |
# We call stop() even if we crashed or timed out in order to get any remaining stdout/stderr output. |
# In the timeout case, we kill the hung process as well. |
out, err = self._server_process.stop(self._port.driver_stop_timeout() if stop_when_done else 0.0) |
@@ -170,6 +178,7 @@ class Driver(object): |
self._server_process = None |
crash_log = None |
+ leak_log = None |
if crashed: |
self.error_from_test, crash_log = self._get_crash_log(text, self.error_from_test, newer_than=start_time) |
@@ -184,16 +193,23 @@ class Driver(object): |
# Print stdout and stderr to the placeholder crash log; we want as much context as possible. |
if self.error_from_test: |
crash_log += '\nstdout:\n%s\nstderr:\n%s\n' % (text, self.error_from_test) |
+ elif leaked: |
+ self.error_from_test, leak_log = self._get_leak_log(text, self.error_from_test, newer_than=start_time) |
return DriverOutput(text, image, actual_image_hash, audio, |
crash=crashed, test_time=time.time() - test_begin_time, measurements=self._measurements, |
timeout=timed_out, error=self.error_from_test, |
crashed_process_name=self._crashed_process_name, |
- crashed_pid=self._crashed_pid, crash_log=crash_log, pid=pid) |
+ crashed_pid=self._crashed_pid, crash_log=crash_log, |
+ leak=leaked, leak_log=leak_log, |
+ pid=pid) |
def _get_crash_log(self, stdout, stderr, newer_than): |
return self._port._get_crash_log(self._crashed_process_name, self._crashed_pid, stdout, stderr, newer_than) |
+ def _get_leak_log(self, stdout, stderr, newer_than): |
+ return self._port._get_leak_log(self._crashed_process_name, self._crashed_pid, stdout, stderr, newer_than) |
+ |
# FIXME: Seems this could just be inlined into callers. |
@classmethod |
def _command_wrapper(cls, wrapper_option): |
@@ -268,6 +284,7 @@ class Driver(object): |
environment = self._setup_environ_for_driver(environment) |
self._crashed_process_name = None |
self._crashed_pid = None |
+ self._leaked = False |
cmd_line = self.cmd_line(pixel_tests, per_test_args) |
self._server_process = self._port._server_process_constructor(self._port, server_name, cmd_line, environment, logging=self._port.get_option("driver_logging")) |
self._server_process.start() |
@@ -320,6 +337,8 @@ class Driver(object): |
cmd.append('--no-timeout') |
cmd.extend(self._port.get_option('additional_drt_flag', [])) |
cmd.extend(self._port.additional_drt_flag()) |
+ if self._port.get_option('enable_leak_detection'): |
+ cmd.append('--enable-leak-detection') |
cmd.extend(per_test_args) |
cmd.append('-') |
return cmd |
@@ -348,6 +367,11 @@ class Driver(object): |
return True |
return self.has_crashed() |
+ def _check_for_leak(self, error_line): |
+ if error_line.startswith("#LEAK - "): |
+ self._leaked = True |
+ return self._leaked |
+ |
def _command_from_driver_input(self, driver_input): |
# FIXME: performance tests pass in full URLs instead of test names. |
if driver_input.test_name.startswith('http://') or driver_input.test_name.startswith('https://') or driver_input.test_name == ('about:blank'): |
@@ -461,6 +485,7 @@ class Driver(object): |
if err_line: |
if self._check_for_driver_crash(err_line): |
break |
+ self._check_for_leak(err_line) |
self.error_from_test += err_line |
block.decode_content() |