Title: [88848] trunk/Tools
Revision
88848
Author
[email protected]
Date
2011-06-14 13:36:57 -0700 (Tue, 14 Jun 2011)

Log Message

2011-06-14  Dirk Pranke  <[email protected]>

        Reviewed by Tony Chang.

        nrwt: handle worker exceptions cleanly
        https://bugs.webkit.org/show_bug.cgi?id=62614

        This change modifiers new-run-webkit-tests to handle exceptions
        raised by worker threads better, by:
        - capturing the worker's stack and logging it in the manager
        - propagating the worker's exception in the caller correctly
        - attempting to cancel the workers and clean up even when
          we get an unexpected exception

        * Scripts/webkitpy/layout_tests/layout_package/manager.py:
        * Scripts/webkitpy/layout_tests/layout_package/worker.py:
        * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
        * Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py:

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (88847 => 88848)


--- trunk/Tools/ChangeLog	2011-06-14 20:35:15 UTC (rev 88847)
+++ trunk/Tools/ChangeLog	2011-06-14 20:36:57 UTC (rev 88848)
@@ -1,3 +1,22 @@
+2011-06-14  Dirk Pranke  <[email protected]>
+
+        Reviewed by Tony Chang.
+
+        nrwt: handle worker exceptions cleanly
+        https://bugs.webkit.org/show_bug.cgi?id=62614
+
+        This change modifiers new-run-webkit-tests to handle exceptions
+        raised by worker threads better, by:
+        - capturing the worker's stack and logging it in the manager
+        - propagating the worker's exception in the caller correctly
+        - attempting to cancel the workers and clean up even when
+          we get an unexpected exception
+
+        * Scripts/webkitpy/layout_tests/layout_package/manager.py:
+        * Scripts/webkitpy/layout_tests/layout_package/worker.py:
+        * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+        * Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py:
+
 2011-06-14  Qi Zhang  <[email protected]>
 
         Reviewed by Laszlo Gombos.

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/manager.py (88847 => 88848)


--- trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/manager.py	2011-06-14 20:35:15 UTC (rev 88847)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/manager.py	2011-06-14 20:36:57 UTC (rev 88848)
@@ -229,6 +229,11 @@
         return self.__class__, (self.reason,)
 
 
+class WorkerException(Exception):
+    """Raised when we receive an unexpected/unknown exception from a worker."""
+    pass
+
+
 class Manager:
     """A class for managing running a series of tests on a series of layout
     test files."""
@@ -687,9 +692,13 @@
             _log.info(e.reason)
             self.cancel_workers()
             interrupted = True
+        except WorkerException:
+            self.cancel_workers()
+            raise
         except:
             # Unexpected exception; don't try to clean up workers.
-            _log.info("Exception raised, exiting")
+            _log.error("Exception raised, exiting")
+            self.cancel_workers()
             raise
 
         thread_timings = [worker_state.stats for worker_state in self._worker_states.values()]
@@ -1309,9 +1318,15 @@
         worker_state = self._worker_states[source]
         worker_state.done = True
 
-    def handle_exception(self, source, exception_info):
-        exception_type, exception_value, exception_traceback = exception_info
-        raise exception_type, exception_value, exception_traceback
+    def handle_exception(self, source, exception_type, exception_value, stack):
+        if exception_type in (KeyboardInterrupt, TestRunInterruptedException):
+            raise exception_type(exception_value)
+        _log.error("%s raised %s('%s'):" % (
+                   source,
+                   exception_value.__class__.__name__,
+                   str(exception_value)))
+        self._log_worker_stack(stack)
+        raise WorkerException(str(exception_value))
 
     def handle_finished_list(self, source, list_name, num_tests, elapsed_time):
         self._group_stats[list_name] = (num_tests, elapsed_time)
@@ -1330,6 +1345,13 @@
         self._all_results.append(result)
         self._update_summary_with_result(self._current_result_summary, result)
 
+    def _log_worker_stack(self, stack):
+        webkitpydir = self._port.path_from_webkit_base('Tools', 'Scripts', 'webkitpy') + self._port._filesystem.sep
+        for filename, line_number, function_name, text in stack:
+            if filename.startswith(webkitpydir):
+                filename = filename.replace(webkitpydir, '')
+            _log.error('  %s:%u (in %s)' % (filename, line_number, function_name))
+            _log.error('    %s' % text)
 
 def read_test_files(fs, files):
     tests = []

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/worker.py (88847 => 88848)


--- trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/worker.py	2011-06-14 20:35:15 UTC (rev 88847)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/worker.py	2011-06-14 20:36:57 UTC (rev 88848)
@@ -32,6 +32,7 @@
 import sys
 import threading
 import time
+import traceback
 
 from webkitpy.common.system import stack_utils
 from webkitpy.layout_tests.layout_package import manager_worker_broker
@@ -99,20 +100,23 @@
                                      % self._name)
         except KeyboardInterrupt:
             exception_msg = ", interrupted"
+            self.post_exception()
         except:
             exception_msg = ", exception raised"
+            self.post_exception()
         finally:
             _log.debug("%s done with message loop%s" % (self._name, exception_msg))
-            if exception_msg:
-                exception_type, exception_value, exception_traceback = sys.exc_info()
-                stack_utils.log_traceback(_log.debug, exception_traceback)
-                # FIXME: Figure out how to send a message with a traceback.
-                self._worker_connection.post_message('exception',
-                    (exception_type, exception_value, None))
             self._worker_connection.post_message('done')
             self.cleanup()
             _log.debug("%s exiting" % self._name)
 
+    def post_exception(self):
+        # Since tracebacks aren't picklable, send the extracted stack instead.
+        exception_type, exception_value, exception_traceback = sys.exc_info()
+        stack_utils.log_traceback(_log.debug, exception_traceback)
+        stack = traceback.extract_tb(exception_traceback)
+        self._worker_connection.post_message('exception', exception_type, exception_value, stack)
+
     def handle_test_list(self, src, list_name, test_list):
         if list_name == "tests_to_http_lock":
             self.start_servers_with_lock()

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py (88847 => 88848)


--- trunk/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py	2011-06-14 20:35:15 UTC (rev 88847)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py	2011-06-14 20:36:57 UTC (rev 88848)
@@ -52,6 +52,8 @@
 _log = logging.getLogger(__name__)
 
 
+WorkerException = manager.WorkerException
+
 def run(port, options, args, regular_output=sys.stderr,
         buildbot_output=sys.stdout):
     """Run the tests.
@@ -459,5 +461,11 @@
     try:
         sys.exit(main())
     except KeyboardInterrupt:
-        # this mirrors what the shell normally does
-        sys.exit(signal.SIGINT + 128)
+        # This mirrors what the shell normally does.
+        INTERRUPTED_EXIT_STATUS = signal.SIGINT + 128
+        sys.exit(INTERRUPTED_EXIT_STATUS)
+    except WorkerException, e:
+        # This is a randomly chosen exit code that can be tested against to
+        # indicate that an unexpected exception occurred.
+        EXCEPTIONAL_EXIT_STATUS = 254
+        sys.exit(EXCEPTIONAL_EXIT_STATUS)

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py (88847 => 88848)


--- trunk/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py	2011-06-14 20:35:15 UTC (rev 88847)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py	2011-06-14 20:36:57 UTC (rev 88848)
@@ -220,7 +220,7 @@
         self.assertEqual(batch_tests_run, [])
 
     def test_exception_raised(self):
-        self.assertRaises(ValueError, logging_run,
+        self.assertRaises(run_webkit_tests.WorkerException, logging_run,
             ['failures/expected/exception.html'], tests_included=True)
 
     def test_full_results_html(self):
@@ -319,7 +319,7 @@
     def test_run_force(self):
         # This raises an exception because we run
         # failures/expected/exception.html, which is normally SKIPped.
-        self.assertRaises(ValueError, logging_run, ['--force'])
+        self.assertRaises(run_webkit_tests.WorkerException, logging_run, ['--force'])
 
     def test_run_part(self):
         # Test that we actually select the right part
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to