Title: [111736] trunk/Tools
Revision
111736
Author
[email protected]
Date
2012-03-22 11:44:10 -0700 (Thu, 22 Mar 2012)

Log Message

Re-land fix for r81603 yet again (!) with latest build fix.
https://bugs.webkit.org/show_bug.cgi?id=81603

Unreviewed, build fix.

The latest problem was a bad sprintf() in
BasePort._get_crash_log(). I've reworked that routine and added
unit tests for coverage.

* Scripts/webkitpy/layout_tests/controllers/single_test_runner.py:
(SingleTestRunner._handle_error):
* Scripts/webkitpy/layout_tests/controllers/test_result_writer.py:
(write_test_result):
(TestResultWriter.write_crash_log):
* Scripts/webkitpy/layout_tests/port/base.py:
(Port.diff_text):
(Port._get_crash_log):
* Scripts/webkitpy/layout_tests/port/chromium.py:
(ChromiumDriver.run_test):
* Scripts/webkitpy/layout_tests/port/chromium_unittest.py:
(ChromiumDriverTest.test_crash_log):
* Scripts/webkitpy/layout_tests/port/driver.py:
(DriverOutput.__init__):
* Scripts/webkitpy/layout_tests/port/mac.py:
(MacPort.is_lion):
(MacPort._get_crash_log):
* Scripts/webkitpy/layout_tests/port/mac_unittest.py:
(test_default_child_processes):
(test_get_crash_log):
* Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py:
(MockDRTPortTest.test_get_crash_log):
* Scripts/webkitpy/layout_tests/port/port_testcase.py:
(PortTestCase.test_get_crash_log):
* Scripts/webkitpy/layout_tests/port/test.py:
(TestDriver.run_test):
* Scripts/webkitpy/layout_tests/port/webkit.py:
(WebKitDriver.__init__):
(WebKitDriver._start):
(WebKitDriver.has_crashed):
(WebKitDriver._check_for_driver_crash):
(WebKitDriver.run_test):

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (111735 => 111736)


--- trunk/Tools/ChangeLog	2012-03-22 18:32:49 UTC (rev 111735)
+++ trunk/Tools/ChangeLog	2012-03-22 18:44:10 UTC (rev 111736)
@@ -1,3 +1,47 @@
+2012-03-22  Dirk Pranke  <[email protected]>
+
+        Re-land fix for r81603 yet again (!) with latest build fix.
+        https://bugs.webkit.org/show_bug.cgi?id=81603
+
+        Unreviewed, build fix.
+
+        The latest problem was a bad sprintf() in
+        BasePort._get_crash_log(). I've reworked that routine and added
+        unit tests for coverage.
+
+        * Scripts/webkitpy/layout_tests/controllers/single_test_runner.py:
+        (SingleTestRunner._handle_error):
+        * Scripts/webkitpy/layout_tests/controllers/test_result_writer.py:
+        (write_test_result):
+        (TestResultWriter.write_crash_log):
+        * Scripts/webkitpy/layout_tests/port/base.py:
+        (Port.diff_text):
+        (Port._get_crash_log):
+        * Scripts/webkitpy/layout_tests/port/chromium.py:
+        (ChromiumDriver.run_test):
+        * Scripts/webkitpy/layout_tests/port/chromium_unittest.py:
+        (ChromiumDriverTest.test_crash_log):
+        * Scripts/webkitpy/layout_tests/port/driver.py:
+        (DriverOutput.__init__):
+        * Scripts/webkitpy/layout_tests/port/mac.py:
+        (MacPort.is_lion):
+        (MacPort._get_crash_log):
+        * Scripts/webkitpy/layout_tests/port/mac_unittest.py:
+        (test_default_child_processes):
+        (test_get_crash_log):
+        * Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py:
+        (MockDRTPortTest.test_get_crash_log):
+        * Scripts/webkitpy/layout_tests/port/port_testcase.py:
+        (PortTestCase.test_get_crash_log):
+        * Scripts/webkitpy/layout_tests/port/test.py:
+        (TestDriver.run_test):
+        * Scripts/webkitpy/layout_tests/port/webkit.py:
+        (WebKitDriver.__init__):
+        (WebKitDriver._start):
+        (WebKitDriver.has_crashed):
+        (WebKitDriver._check_for_driver_crash):
+        (WebKitDriver.run_test):
+
 2012-03-22  Alexander Færøy  <[email protected]>
 
         [Qt] Enable support for handling database quota's in the MiniBrowser

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py (111735 => 111736)


--- trunk/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py	2012-03-22 18:32:49 UTC (rev 111735)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py	2012-03-22 18:44:10 UTC (rev 111736)
@@ -198,7 +198,9 @@
             testname = self._test_name
 
         if driver_output.crash:
-            failures.append(test_failures.FailureCrash(bool(reference_filename)))
+            failures.append(test_failures.FailureCrash(bool(reference_filename),
+                                                       driver_output.crashed_process_name,
+                                                       driver_output.crashed_pid))
             if driver_output.error:
                 _log.debug("%s %s crashed, stack trace:" % (self._worker_name, testname))
             else:

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/controllers/test_result_writer.py (111735 => 111736)


--- trunk/Tools/Scripts/webkitpy/layout_tests/controllers/test_result_writer.py	2012-03-22 18:32:49 UTC (rev 111735)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/controllers/test_result_writer.py	2012-03-22 18:44:10 UTC (rev 111736)
@@ -29,7 +29,6 @@
 
 import logging
 
-from webkitpy.common.system.crashlogs import CrashLogs
 from webkitpy.layout_tests.models import test_failures
 
 
@@ -63,7 +62,7 @@
             writer.write_audio_files(driver_output.audio, expected_driver_output.audio)
         elif isinstance(failure, test_failures.FailureCrash):
             crashed_driver_output = expected_driver_output if failure.is_reftest else driver_output
-            writer.write_crash_report(crashed_driver_output.crashed_process_name, crashed_driver_output.error)
+            writer.write_crash_log(crashed_driver_output.crash_log)
         elif isinstance(failure, test_failures.FailureReftestMismatch):
             writer.write_image_files(driver_output.image, expected_driver_output.image)
             # FIXME: This work should be done earlier in the pipeline (e.g., when we compare images for non-ref tests).
@@ -157,15 +156,12 @@
         fs.maybe_make_directory(fs.dirname(filename))
         fs.write_binary_file(filename, error)
 
-    def write_crash_report(self, crashed_process_name, error):
+    def write_crash_log(self, crash_log):
         fs = self._filesystem
         filename = self.output_filename(self.FILENAME_SUFFIX_CRASH_LOG + ".txt")
         fs.maybe_make_directory(fs.dirname(filename))
-        crash_logs = CrashLogs(fs)
-        log = crash_logs.find_newest_log(crashed_process_name)
-        # CrashLogs doesn't support every platform, so we fall back to
-        # including the stderr output, which is admittedly somewhat redundant.
-        fs.write_text_file(filename, log if log else error)
+        if crash_log is not None:
+            fs.write_text_file(filename, crash_log)
 
     def write_text_files(self, actual_text, expected_text):
         self.write_output_files(".txt", actual_text, expected_text)

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/base.py (111735 => 111736)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/base.py	2012-03-22 18:32:49 UTC (rev 111735)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/base.py	2012-03-22 18:44:10 UTC (rev 111736)
@@ -290,9 +290,6 @@
                                     actual_filename)
         return ''.join(diff)
 
-    def is_crash_reporter(self, process_name):
-        return False
-
     def check_for_leaks(self, process_name, process_pid):
         # Subclasses should check for leaks in the running process
         # and print any necessary warnings if leaks are found.
@@ -1054,6 +1051,15 @@
         """Returns the port's driver implementation."""
         raise NotImplementedError('Port._driver_class')
 
+    def _get_crash_log(self, name, pid, stdout, stderr):
+        name_str = name or '<unknown process name>'
+        pid_str = str(pid or '<unknown>')
+        stdout_lines = (stdout or '<empty>').decode('utf8').splitlines()
+        stderr_lines = (stderr or '<empty>').decode('utf8').splitlines()
+        return 'crash log for %s (pid %s):\n%s\n%s\n' % (name_str, pid_str,
+            '\n'.join(('STDOUT: ' + l) for l in stdout_lines),
+            '\n'.join(('STDERR: ' + l) for l in stderr_lines))
+
     def virtual_test_suites(self):
         return []
 

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium.py (111735 => 111736)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium.py	2012-03-22 18:32:49 UTC (rev 111735)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium.py	2012-03-22 18:44:10 UTC (rev 111736)
@@ -592,16 +592,23 @@
                 text = None
 
         error = ''.join(error)
-        crashed_process_name = None
         # Currently the stacktrace is in the text output, not error, so append the two together so
         # that we can see stack in the output. See http://webkit.org/b/66806
         # FIXME: We really should properly handle the stderr output separately.
+        crash_log = ''
+        crashed_process_name = None
+        crashed_pid = None
         if crash:
-            error = error + str(text)
             crashed_process_name = self._port.driver_name()
+            if self._proc:
+                crashed_pid = self._proc.pid
+            crash_log = self._port._get_crash_log(crashed_process_name, crashed_pid, text, error)
+            if text:
+                error = error + text
 
         return DriverOutput(text, output_image, actual_checksum, audio=audio_bytes,
-            crash=crash, crashed_process_name=crashed_process_name, test_time=run_time, timeout=timeout, error=error)
+            crash=crash, crashed_process_name=crashed_process_name, crashed_pid=crashed_pid, crash_log=crash_log,
+            test_time=run_time, timeout=timeout, error=error)
 
     def start(self, pixel_tests, per_test_args):
         if not self._proc:

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py (111735 => 111736)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py	2012-03-22 18:32:49 UTC (rev 111735)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py	2012-03-22 18:44:10 UTC (rev 111736)
@@ -92,17 +92,23 @@
         self.driver._proc.stdout.readline = mock_readline
         self._assert_write_command_and_read_line(expected_crash=True)
 
-    def test_crashed_process_name(self):
+    def test_crash_log(self):
         self.driver._proc = Mock()
 
         # Simulate a crash by having stdout close unexpectedly.
         def mock_readline():
             raise IOError
         self.driver._proc.stdout.readline = mock_readline
+        self.driver._proc.pid = 1234
 
         self.driver.test_to_uri = lambda test: 'mocktesturi'
+        self.driver._port.driver_name = lambda: 'mockdriver'
+        self.driver._port._get_crash_log = lambda name, pid, out, err: 'mockcrashlog'
         driver_output = self.driver.run_test(DriverInput(test_name='some/test.html', timeout=1, image_hash=None, is_reftest=False))
-        self.assertEqual(self.driver._port.driver_name(), driver_output.crashed_process_name)
+        self.assertTrue(driver_output.crash)
+        self.assertEqual(driver_output.crashed_process_name, 'mockdriver')
+        self.assertEqual(driver_output.crashed_pid, 1234)
+        self.assertEqual(driver_output.crash_log, 'mockcrashlog')
 
     def test_stop(self):
         self.pid = None

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/driver.py (111735 => 111736)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/driver.py	2012-03-22 18:32:49 UTC (rev 111735)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/driver.py	2012-03-22 18:44:10 UTC (rev 111736)
@@ -64,7 +64,8 @@
     strip_patterns.append((re.compile('scrollHeight [0-9]+'), 'scrollHeight'))
 
     def __init__(self, text, image, image_hash, audio, crash=False,
-            test_time=0, timeout=False, error='', crashed_process_name=None):
+            test_time=0, timeout=False, error='', crashed_process_name='??',
+            crashed_pid=None, crash_log=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.
@@ -73,6 +74,8 @@
         self.audio = audio  # Binary format is port-dependent.
         self.crash = crash
         self.crashed_process_name = crashed_process_name
+        self.crashed_pid = crashed_pid
+        self.crash_log = crash_log
         self.test_time = test_time
         self.timeout = timeout
         self.error = error  # stderr output

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/mac.py (111735 => 111736)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/mac.py	2012-03-22 18:32:49 UTC (rev 111735)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/mac.py	2012-03-22 18:44:10 UTC (rev 111736)
@@ -29,7 +29,9 @@
 import logging
 import re
 import subprocess
+import time
 
+from webkitpy.common.system.crashlogs import CrashLogs
 from webkitpy.layout_tests.port.apple import ApplePort
 from webkitpy.layout_tests.port.leakdetector import LeakDetector
 
@@ -96,10 +98,6 @@
     def is_lion(self):
         return self._version == "lion"
 
-    # Belongs on a Platform object.
-    def is_crash_reporter(self, process_name):
-        return re.search(r'ReportCrash', process_name)
-
     def default_child_processes(self):
         if self.is_snowleopard():
             _log.warn("Cannot run tests in parallel on Snow Leopard due to rdar://problem/10621525.")
@@ -160,6 +158,25 @@
     def release_http_lock(self):
         pass
 
+    def _get_crash_log(self, name, pid, stdout, stderr):
+        # Note that we do slow-spin here and wait, since it appears the time
+        # ReportCrash takes to actually write and flush the file varies when there are
+        # lots of simultaneous crashes going on.
+        # FIXME: Should most of this be moved into CrashLogs()?
+        crash_log = ''
+        crash_logs = CrashLogs(self._filesystem)
+        now = time.time()
+        deadline = now + 5 * int(self.get_option('child_processes'))
+        while not crash_log and now <= deadline:
+            crash_log = crash_logs.find_newest_log(name, pid, include_errors=True)
+            if not crash_log or not [line for line in crash_log.splitlines() if not line.startswith('ERROR')]:
+                time.sleep(0.1)
+                now = time.time()
+        if not crash_log:
+            crash_log = 'no crash log found for %s:%d' % (name, pid)
+            _log.warning(crash_log)
+        return crash_log
+
     def _path_to_helper(self):
         binary_name = 'LayoutTestHelper'
         return self._build_path(binary_name)

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py (111735 => 111736)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py	2012-03-22 18:32:49 UTC (rev 111735)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py	2012-03-22 18:44:10 UTC (rev 111736)
@@ -188,3 +188,7 @@
         expected_logs = "Cannot run tests in parallel on Snow Leopard due to rdar://problem/10621525.\n"
         child_processes = OutputCapture().assert_outputs(self, port.default_child_processes, (), expected_logs=expected_logs)
         self.assertEqual(child_processes, 1)
+
+    def test_get_crash_log(self):
+        # Mac crash logs are tested elsewhere
+        pass

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py (111735 => 111736)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py	2012-03-22 18:32:49 UTC (rev 111735)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py	2012-03-22 18:44:10 UTC (rev 111736)
@@ -90,6 +90,8 @@
     def integration_test_helper(self):
         pass
 
+    def test_get_crash_log(self):
+        pass
 
 class MockDRTTest(unittest.TestCase):
     def input_line(self, port, test_name, checksum=None):

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py (111735 => 111736)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py	2012-03-22 18:32:49 UTC (rev 111735)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py	2012-03-22 18:44:10 UTC (rev 111736)
@@ -312,6 +312,14 @@
 
             i += 1
 
+    def test_get_crash_log(self):
+        port = self.make_port()
+        self.assertEquals(port._get_crash_log(None, None, None, None),
+           ('crash log for <unknown process name> (pid <unknown>):\n'
+            'STDOUT: <empty>\n'
+            'STDERR: <empty>\n'))
+
+
 # FIXME: This class and main() should be merged into test-webkitpy.
 class EnhancedTestLoader(unittest.TestLoader):
     integration_tests = False

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/test.py (111735 => 111736)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/test.py	2012-03-22 18:32:49 UTC (rev 111735)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/test.py	2012-03-22 18:44:10 UTC (rev 111736)
@@ -35,6 +35,7 @@
 from webkitpy.layout_tests.port.base import VirtualTestSuite
 from webkitpy.layout_tests.models.test_configuration import TestConfiguration
 from webkitpy.common.system.filesystem_mock import MockFileSystem
+from webkitpy.common.system.crashlogs import CrashLogs
 
 
 # This sets basic expectations for a test. Each individual expectation
@@ -519,13 +520,22 @@
         if test.actual_audio:
             audio = base64.b64decode(test.actual_audio)
         crashed_process_name = None
+        crashed_pid = None
         if test.crash:
             crashed_process_name = self._port.driver_name()
+            crashed_pid = 1
         elif test.web_process_crash:
             crashed_process_name = 'WebProcess'
-        return DriverOutput(actual_text, test.actual_image,
-            test.actual_checksum, audio, crash=test.crash or test.web_process_crash,
-            crashed_process_name=crashed_process_name,
+            crashed_pid = 2
+
+        crash_log = ''
+        if crashed_process_name:
+            crash_logs = CrashLogs(self._port._filesystem)
+            crash_log = crash_logs.find_newest_log(crashed_process_name, None) or ''
+
+        return DriverOutput(actual_text, test.actual_image, test.actual_checksum, audio,
+            crash=test.crash or test.web_process_crash, crashed_process_name=crashed_process_name,
+            crashed_pid=crashed_pid, crash_log=crash_log,
             test_time=time.time() - start_time, timeout=test.timeout, error=test.error)
 
     def start(self, pixel_tests, per_test_args):

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/webkit.py (111735 => 111736)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/webkit.py	2012-03-22 18:32:49 UTC (rev 111735)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/webkit.py	2012-03-22 18:44:10 UTC (rev 111736)
@@ -437,7 +437,8 @@
         # "#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_subprocess_name = None
+        self._crashed_process_name = None
+        self._crashed_pid = None
 
         # 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.
@@ -483,28 +484,34 @@
         # FIXME: We're assuming that WebKitTestRunner checks this DumpRenderTree-named environment variable.
         environment['DUMPRENDERTREE_TEMP'] = str(self._driver_tempdir)
         environment['LOCAL_RESOURCE_ROOT'] = self._port.layout_tests_dir()
-        self._crashed_subprocess_name = None
+        self._crashed_process_name = None
+        self._crashed_pid = None
         self._server_process = server_process.ServerProcess(self._port, server_name, self.cmd_line(pixel_tests, per_test_args), environment)
 
     def has_crashed(self):
         if self._server_process is None:
             return False
-        if self._crashed_subprocess_name:
+        if self._crashed_process_name:
             return True
         if self._server_process.has_crashed():
-            self._crashed_subprocess_name = self._port.driver_name()
+            self._crashed_process_name = self._server_process.name()
+            self._crashed_pid = self._server_process.pid()
             return True
-        return False
 
     def _check_for_driver_crash(self, error_line):
         if error_line == "#CRASHED\n":
             # This is used on Windows to report that the process has crashed
             # See http://trac.webkit.org/changeset/65537.
-            self._crashed_subprocess_name = self._port.driver_name()
-            return True
-        if error_line == "#CRASHED - WebProcess\n":
+            self._crashed_process_name = self._server_process.name()
+            self._crashed_pid = self._server_process.pid()
+        elif error_line.startswith("#CRASHED - WebProcess"):
             # WebKitTestRunner uses this to report that the WebProcess subprocess crashed.
-            self._crashed_subprocess_name = "WebProcess"
+            pid = None
+            m = re.match('pid (\d+)', error_line)
+            if m:
+                pid = int(m.group(1))
+            self._crashed_process_name = 'WebProcess'
+            self._crashed_pid = pid
             return True
         return self.has_crashed()
 
@@ -555,10 +562,15 @@
         # FIXME: We may need to also read stderr until the process dies?
         self.error_from_test += self._server_process.pop_all_buffered_stderr()
 
+        crash_log = ''
+        if self.has_crashed():
+            crash_log = self._port._get_crash_log(self._crashed_process_name, self._crashed_pid, text, self.error_from_test)
+
         return DriverOutput(text, image, actual_image_hash, audio,
             crash=self.has_crashed(), test_time=time.time() - start_time,
             timeout=self._server_process.timed_out, error=self.error_from_test,
-            crashed_process_name=self._crashed_subprocess_name)
+            crashed_process_name=self._crashed_process_name,
+            crashed_pid=self._crashed_pid, crash_log=crash_log)
 
     def _read_header(self, block, line, header_text, header_attr, header_filter=None):
         if line.startswith(header_text) and getattr(block, header_attr) is None:
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to