diff options
Diffstat (limited to 'packages/Python/lldbsuite/test/test_runner')
| -rw-r--r-- | packages/Python/lldbsuite/test/test_runner/process_control.py | 62 | ||||
| -rwxr-xr-x | packages/Python/lldbsuite/test/test_runner/test/test_process_control.py | 13 | 
2 files changed, 57 insertions, 18 deletions
diff --git a/packages/Python/lldbsuite/test/test_runner/process_control.py b/packages/Python/lldbsuite/test/test_runner/process_control.py index a7e639e4b8b6..720f5112a4cd 100644 --- a/packages/Python/lldbsuite/test/test_runner/process_control.py +++ b/packages/Python/lldbsuite/test/test_runner/process_control.py @@ -23,6 +23,7 @@ import threading  class CommunicatorThread(threading.Thread):      """Provides a thread class that communicates with a subprocess.""" +      def __init__(self, process, event, output_file):          super(CommunicatorThread, self).__init__()          # Don't let this thread prevent shutdown. @@ -100,6 +101,7 @@ class ProcessHelper(object):      @see ProcessHelper.process_helper()      """ +      def __init__(self):          super(ProcessHelper, self).__init__() @@ -281,6 +283,7 @@ class UnixProcessHelper(ProcessHelper):      This implementation supports anything that looks Posix-y      (e.g. Darwin, Linux, *BSD, etc.)      """ +      def __init__(self):          super(UnixProcessHelper, self).__init__() @@ -302,7 +305,7 @@ class UnixProcessHelper(ProcessHelper):              stdin=subprocess.PIPE,              stdout=subprocess.PIPE,              stderr=subprocess.PIPE, -            universal_newlines=True, # Elicits automatic byte -> string decoding in Py3 +            universal_newlines=True,  # Elicits automatic byte -> string decoding in Py3              close_fds=True,              preexec_fn=preexec_func) @@ -357,18 +360,28 @@ class UnixProcessHelper(ProcessHelper):          # Choose kill mechanism based on whether we're targeting          # a process group or just a process. -        if popen_process.using_process_groups: -            # if log_file: -            #    log_file.write( -            #        "sending signum {} to process group {} now\n".format( -            #            signum, popen_process.pid)) -            os.killpg(popen_process.pid, signum) -        else: -            # if log_file: -            #    log_file.write( -            #        "sending signum {} to process {} now\n".format( -            #            signum, popen_process.pid)) -            os.kill(popen_process.pid, signum) +        try: +            if popen_process.using_process_groups: +                # if log_file: +                #    log_file.write( +                #        "sending signum {} to process group {} now\n".format( +                #            signum, popen_process.pid)) +                os.killpg(popen_process.pid, signum) +            else: +                # if log_file: +                #    log_file.write( +                #        "sending signum {} to process {} now\n".format( +                #            signum, popen_process.pid)) +                os.kill(popen_process.pid, signum) +        except OSError as error: +            import errno +            if error.errno == errno.ESRCH: +                # This is okay - failed to find the process.  It may be that +                # that the timeout pre-kill hook eliminated the process.  We'll +                # ignore. +                pass +            else: +                raise      def soft_terminate(self, popen_process, log_file=None, want_core=True):          # Choose signal based on desire for core file. @@ -412,8 +425,10 @@ class UnixProcessHelper(ProcessHelper):          signal_name = signal_names_by_number.get(signo, "")          return (signo, signal_name) +  class WindowsProcessHelper(ProcessHelper):      """Provides a Windows implementation of the ProcessHelper class.""" +      def __init__(self):          super(WindowsProcessHelper, self).__init__() @@ -429,7 +444,7 @@ class WindowsProcessHelper(ProcessHelper):              stdin=subprocess.PIPE,              stdout=subprocess.PIPE,              stderr=subprocess.PIPE, -            universal_newlines=True, # Elicits automatic byte -> string decoding in Py3 +            universal_newlines=True,  # Elicits automatic byte -> string decoding in Py3              creationflags=creation_flags)      def was_hard_terminate(self, returncode): @@ -447,6 +462,7 @@ class ProcessDriver(object):      way.  The on_process_exited method is informed if the exit was natural      or if it was due to a timeout.      """ +      def __init__(self, soft_terminate_timeout=10.0):          super(ProcessDriver, self).__init__()          self.process_helper = ProcessHelper.process_helper() @@ -477,6 +493,19 @@ class ProcessDriver(object):      def on_process_exited(self, command, output, was_timeout, exit_status):          pass +    def on_timeout_pre_kill(self): +        """Called after the timeout interval elapses but before killing it. + +        This method is added to enable derived classes the ability to do +        something to the process prior to it being killed.  For example, +        this would be a good spot to run a program that samples the process +        to see what it was doing (or not doing). + +        Do not attempt to reap the process (i.e. use wait()) in this method. +        That will interfere with the kill mechanism and return code processing. +        """ +        pass +      def write(self, content):          # pylint: disable=no-self-use          # Intended - we want derived classes to be able to override @@ -634,6 +663,11 @@ class ProcessDriver(object):              # Reap the child process here.              self.returncode = self.process.wait()          else: + +            # Allow derived classes to do some work after we detected +            # a timeout but before we touch the timed-out process. +            self.on_timeout_pre_kill() +              # Prepare to stop the process              process_terminated = completed_normally              terminate_attempt_count = 0 diff --git a/packages/Python/lldbsuite/test/test_runner/test/test_process_control.py b/packages/Python/lldbsuite/test/test_runner/test/test_process_control.py index 817c83c4fb55..88ad961be2b9 100755 --- a/packages/Python/lldbsuite/test/test_runner/test/test_process_control.py +++ b/packages/Python/lldbsuite/test/test_runner/test/test_process_control.py @@ -27,6 +27,7 @@ from test_runner import process_control  class TestInferiorDriver(process_control.ProcessDriver): +      def __init__(self, soft_terminate_timeout=None):          super(TestInferiorDriver, self).__init__(              soft_terminate_timeout=soft_terminate_timeout) @@ -58,6 +59,7 @@ class TestInferiorDriver(process_control.ProcessDriver):  class ProcessControlTests(unittest.TestCase): +      @classmethod      def _suppress_soft_terminate(cls, command):          # Do the right thing for your platform here. @@ -79,7 +81,8 @@ class ProcessControlTests(unittest.TestCase):          # Base command.          script_name = "{}/inferior.py".format(os.path.dirname(__file__))          if not os.path.exists(script_name): -            raise Exception("test inferior python script not found: {}".format(script_name)) +            raise Exception( +                "test inferior python script not found: {}".format(script_name))          command = ([sys.executable, script_name])          if ignore_soft_terminate: @@ -97,6 +100,7 @@ class ProcessControlTests(unittest.TestCase):  class ProcessControlNoTimeoutTests(ProcessControlTests):      """Tests the process_control module.""" +      def test_run_completes(self):          """Test that running completes and gets expected stdout/stderr."""          driver = TestInferiorDriver() @@ -115,6 +119,7 @@ class ProcessControlNoTimeoutTests(ProcessControlTests):  class ProcessControlTimeoutTests(ProcessControlTests): +      def test_run_completes(self):          """Test that running completes and gets expected return code."""          driver = TestInferiorDriver() @@ -124,7 +129,7 @@ class ProcessControlTimeoutTests(ProcessControlTests):              "{}s".format(timeout_seconds),              False)          self.assertTrue( -            driver.completed_event.wait(2*timeout_seconds), +            driver.completed_event.wait(2 * timeout_seconds),              "process failed to complete")          self.assertEqual(driver.returncode, 0) @@ -141,13 +146,13 @@ class ProcessControlTimeoutTests(ProcessControlTests):              # Sleep twice as long as the timeout interval.  This              # should force a timeout.              self.inferior_command( -                options="--sleep {}".format(timeout_seconds*2)), +                options="--sleep {}".format(timeout_seconds * 2)),              "{}s".format(timeout_seconds),              with_core)          # We should complete, albeit with a timeout.          self.assertTrue( -            driver.completed_event.wait(2*timeout_seconds), +            driver.completed_event.wait(2 * timeout_seconds),              "process failed to complete")          # Ensure we received a timeout.  | 
