[Erp5-report] r46015 arnaud.fontaine - /erp5/trunk/utils/erp5.utils.benchmark/src/erp5/util...

nobody at svn.erp5.org nobody at svn.erp5.org
Tue Aug 30 10:11:40 CEST 2011


Author: arnaud.fontaine
Date: Tue Aug 30 10:11:39 2011
New Revision: 46015

URL: http://svn.erp5.org?rev=46015&view=rev
Log:
Fix KeyboardInterrupt on control process where the results were not
properly flushed before exiting and terminate children properly.

Also, terminate children after a given number of errors (based upon a
patch from jm).

Modified:
    erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/performance_tester.py
    erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/process.py
    erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/result.py

Modified: erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/performance_tester.py
URL: http://svn.erp5.org/erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/performance_tester.py?rev=46015&r1=46014&r2=46015&view=diff
==============================================================================
--- erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/performance_tester.py [utf8] (original)
+++ erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/performance_tester.py [utf8] Tue Aug 30 10:11:39 2011
@@ -33,11 +33,15 @@ import os
 import sys
 import multiprocessing
 import xmlrpclib
+import signal
+import errno
 
 from erp5.utils.benchmark.argument import ArgumentType
 from erp5.utils.benchmark.process import BenchmarkProcess
 from erp5.utils.benchmark.result import ERP5BenchmarkResult, CSVBenchmarkResult
 
+MAXIMUM_KEYBOARD_INTERRUPT = 5
+
 class PerformanceTester(object):
   def __init__(self, namespace=None):
     if not namespace:
@@ -46,6 +50,8 @@ class PerformanceTester(object):
     else:
       self._argument_namespace = namespace
 
+    self._process_terminated_counter = 0
+
   @staticmethod
   def _add_parser_arguments(parser):
     # Optional arguments
@@ -189,6 +195,9 @@ class PerformanceTester(object):
     ERP5BenchmarkResult.closeResultDocument(self._argument_namespace.erp5_publish_url,
                                             error_message_set)
 
+  def _child_terminated_handler(self, *args, **kwargs):
+    self._process_terminated_counter += 1
+
   def _run_constant(self, nb_users):
     process_list = []
     exit_msg_queue = multiprocessing.Queue(nb_users)
@@ -202,33 +211,41 @@ class PerformanceTester(object):
 
       process_list.append(process)
 
+    signal.signal(signal.SIGCHLD, self._child_terminated_handler)
+
     for process in process_list:
       process.start()
 
     error_message_set = set()
-    i = 0
-    while i != len(process_list):
+    interrupted_counter = 0
+    while self._process_terminated_counter != len(process_list):
       try:
-        msg = exit_msg_queue.get()
-      except KeyboardInterrupt:
-        if self._argument_namespace.repeat != -1:
-          print >>sys.stderr, "Stopping gracefully"
-          for process in process_list:
-            process.terminate()
+        error_message = exit_msg_queue.get()
 
-          i = 0
-          continue
-        else:
-          msg = None
+      except KeyboardInterrupt, e:
+        if interrupted_counter == 0:
+          print >>sys.stderr, "\nInterrupted by user, stopping gracefully " \
+              "unless interrupted %d times" % MAXIMUM_KEYBOARD_INTERRUPT
+
+        interrupted_counter += 1
 
-      if msg is not None:
-        error_message_set.add(msg)
         for process in process_list:
-          process.terminate()
+          if (not getattr(process, '_stopping', False) or
+              interrupted_counter == MAXIMUM_KEYBOARD_INTERRUPT):
+            process._stopping = True
+            process.terminate()
 
-        break
+      # An IOError may be raised when receiving a SIGCHLD which interrupts the
+      # blocking system call above and the system call should not be restarted
+      # (using siginterrupt), otherwise the  process will stall forever as its
+      # child has already exited
+      except IOError, e:
+        if e.errno == errno.EINTR:
+          continue
 
-      i += 1
+      else:
+        if error_message is not None:
+          error_message_set.add(error_message)
 
     if error_message_set:
       return (error_message_set, 1)

Modified: erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/process.py
URL: http://svn.erp5.org/erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/process.py?rev=46015&r1=46014&r2=46015&view=diff
==============================================================================
--- erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/process.py [utf8] (original)
+++ erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/process.py [utf8] Tue Aug 30 10:11:39 2011
@@ -36,6 +36,9 @@ import sys
 
 from erp5.utils.test_browser.browser import Browser
 
+MAXIMUM_ERROR_COUNTER = 10
+RESULT_NUMBER_BEFORE_FLUSHING = 100
+
 class BenchmarkProcess(multiprocessing.Process):
   def __init__(self, exit_msg_queue, result_klass, argument_namespace,
                nb_users, user_index, *args, **kwargs):
@@ -48,6 +51,7 @@ class BenchmarkProcess(multiprocessing.P
     # Initialized when running the test
     self._browser = None
     self._current_repeat = 1
+    self._error_counter = 0
 
     super(BenchmarkProcess, self).__init__(*args, **kwargs)
 
@@ -71,6 +75,9 @@ class BenchmarkProcess(multiprocessing.P
       try:
         target(result, self._browser)
       except BaseException, e:
+        if isinstance(e, StopIteration):
+          raise
+
         msg = "%s: %s" % (target, traceback.format_exc())
 
         if (self._argument_namespace.enable_debug and isinstance(e, Exception)):
@@ -79,10 +86,11 @@ class BenchmarkProcess(multiprocessing.P
           except:
             pass
 
-        if self._current_repeat == 1:
-          self._logger.error(msg)
-          raise
+        if (self._current_repeat == 1 or
+            self._error_counter == MAXIMUM_ERROR_COUNTER):
+          raise RuntimeError(msg)
 
+        self._error_counter += 1
         self._logger.warning(msg)
 
       for stat in result.getCurrentSuiteStatList():
@@ -95,12 +103,10 @@ class BenchmarkProcess(multiprocessing.P
                              stat.standard_deviation,
                              stat.maximum))
 
-        if self._argument_namespace.max_global_average and \
-           mean > self._argument_namespace.max_global_average:
-          self._logger.info("Stopping as mean is greater than maximum "
-                            "global average")
-
-          raise StopIteration
+        if (self._argument_namespace.max_global_average and
+            mean > self._argument_namespace.max_global_average):
+          raise RuntimeError("Stopping as mean is greater than maximum "
+                             "global average")
 
       result.exitSuite()
 
@@ -113,8 +119,12 @@ class BenchmarkProcess(multiprocessing.P
 
     self._logger = result_instance.getLogger()
 
-    if self._argument_namespace.repeat != -1:
-      signal.signal(signal.SIGTERM, self.stopGracefully)
+    # Ensure the data are flushed before exiting, handled by Result class 
+    # __exit__ block
+    signal.signal(signal.SIGTERM, self.stopGracefully)
+ 
+    # Ignore KeyboardInterrupt as it is handled by the parent process
+    signal.signal(signal.SIGINT, signal.SIG_IGN)
 
     exit_status = 0
     exit_msg = None
@@ -128,16 +138,15 @@ class BenchmarkProcess(multiprocessing.P
           self.runBenchmarkSuiteList(result)
           self._current_repeat += 1
 
-          if self._current_repeat == 100:
+          if self._current_repeat == RESULT_NUMBER_BEFORE_FLUSHING:
             result.flush()
 
     except StopIteration, e:
-      exit_msg = str(e)
-      exit_status = 1
+      self._logger.error(e)
 
     except BaseException, e:
       exit_msg = e
-      exit_status = 2
+      exit_status = 1
 
     self._exit_msg_queue.put(exit_msg)
     sys.exit(exit_status)

Modified: erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/result.py
URL: http://svn.erp5.org/erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/result.py?rev=46015&r1=46014&r2=46015&view=diff
==============================================================================
--- erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/result.py [utf8] (original)
+++ erp5/trunk/utils/erp5.utils.benchmark/src/erp5/utils/benchmark/result.py [utf8] Tue Aug 30 10:11:39 2011
@@ -214,15 +214,10 @@ class CSVBenchmarkResult(BenchmarkResult
     super(CSVBenchmarkResult, self).__exit__(exc_type, exc_value, traceback)
     self._result_file.close()
 
-    if exc_type:
+    if exc_type and not issubclass(exc_type, StopIteration):
       msg = "An error occured, see: %s" % self._log_filename_path
-      from traceback import format_tb
-      self.getLogger().error("%s: %s\n%s" % (exc_type, exc_value,
-                                             ''.join(format_tb(traceback))))
-      if isinstance(exc_type, StopIteration):
-        raise StopIteration, msg
-      else:
-        raise RuntimeError, msg
+      self.getLogger().error("%s: %s" % (exc_type, exc_value))
+      raise RuntimeError(msg)
 
 from cStringIO import StringIO
 



More information about the Erp5-report mailing list