[Lldb-commits] [lldb] r238405 - [NativeProcessLinux] Support inferiors which change their process group

Pavel Labath labath at google.com
Thu May 28 01:59:21 PDT 2015


Author: labath
Date: Thu May 28 03:59:21 2015
New Revision: 238405

URL: http://llvm.org/viewvc/llvm-project?rev=238405&view=rev
Log:
[NativeProcessLinux] Support inferiors which change their process group

Summary:
Previously, we wait()ed for events from the inferiors process group. This is resulted in a
failure if the inferior changed its process group in the middle of execution. To avoid this, I
pass -1 to the wait() call. The flag __WNOTHREAD makes sure we don't actually wait for events
from any process, but only the processes(threads) which are our children (or traced by us). Since
this happens on the monitor thread, which is dedicated to monitoring a single inferior, we will
be getting events only from this inferior.

Test Plan: All tests pass on linux. I have added a test to check the new functionality.

Reviewers: chaoren, ovyalov

Subscribers: lldb-commits

Differential Revision: http://reviews.llvm.org/D10061

Added:
    lldb/trunk/test/functionalities/process_group/
    lldb/trunk/test/functionalities/process_group/Makefile
    lldb/trunk/test/functionalities/process_group/TestChangeProcessGroup.py
    lldb/trunk/test/functionalities/process_group/main.c
Modified:
    lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp

Modified: lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp?rev=238405&r1=238404&r2=238405&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp Thu May 28 03:59:21 2015
@@ -786,7 +786,7 @@ NativeProcessLinux::Monitor::HandleWait(
     while (true)
     {
         int status = -1;
-        ::pid_t wait_pid = waitpid(m_child_pid, &status, __WALL | WNOHANG);
+        ::pid_t wait_pid = waitpid(-1, &status, __WALL | __WNOTHREAD | WNOHANG);
 
         if (wait_pid == 0)
             break; // We are done.
@@ -797,8 +797,8 @@ NativeProcessLinux::Monitor::HandleWait(
                 continue;
 
             if (log)
-              log->Printf("NativeProcessLinux::Monitor::%s waitpid (pid = %" PRIi32 ", &status, __WALL | WNOHANG) failed: %s",
-                      __FUNCTION__, m_child_pid, strerror(errno));
+              log->Printf("NativeProcessLinux::Monitor::%s waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG) failed: %s",
+                      __FUNCTION__, strerror(errno));
             break;
         }
 
@@ -821,7 +821,7 @@ NativeProcessLinux::Monitor::HandleWait(
         {
             signal = WTERMSIG(status);
             status_cstr = "SIGNALED";
-            if (wait_pid == abs(m_child_pid)) {
+            if (wait_pid == m_child_pid) {
                 exited = true;
                 exit_status = -1;
             }
@@ -830,9 +830,9 @@ NativeProcessLinux::Monitor::HandleWait(
             status_cstr = "(\?\?\?)";
 
         if (log)
-            log->Printf("NativeProcessLinux::Monitor::%s: waitpid (pid = %" PRIi32 ", &status, __WALL | WNOHANG)"
+            log->Printf("NativeProcessLinux::Monitor::%s: waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG)"
                 "=> pid = %" PRIi32 ", status = 0x%8.8x (%s), signal = %i, exit_state = %i",
-                __FUNCTION__, m_child_pid, wait_pid, status, status_cstr, signal, exit_status);
+                __FUNCTION__, wait_pid, status, status_cstr, signal, exit_status);
 
         m_native_process->MonitorCallback (wait_pid, exited, signal, exit_status);
     }
@@ -893,7 +893,7 @@ NativeProcessLinux::Monitor::MainLoop()
 {
     ::pid_t child_pid = (*m_initial_operation_up)(m_operation_error);
     m_initial_operation_up.reset();
-    m_child_pid = -getpgid(child_pid),
+    m_child_pid = child_pid;
     sem_post(&m_operation_sem);
 
     while (true)

Added: lldb/trunk/test/functionalities/process_group/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/process_group/Makefile?rev=238405&view=auto
==============================================================================
--- lldb/trunk/test/functionalities/process_group/Makefile (added)
+++ lldb/trunk/test/functionalities/process_group/Makefile Thu May 28 03:59:21 2015
@@ -0,0 +1,5 @@
+LEVEL = ../../make
+
+C_SOURCES := main.c
+
+include $(LEVEL)/Makefile.rules

Added: lldb/trunk/test/functionalities/process_group/TestChangeProcessGroup.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/process_group/TestChangeProcessGroup.py?rev=238405&view=auto
==============================================================================
--- lldb/trunk/test/functionalities/process_group/TestChangeProcessGroup.py (added)
+++ lldb/trunk/test/functionalities/process_group/TestChangeProcessGroup.py Thu May 28 03:59:21 2015
@@ -0,0 +1,109 @@
+"""Test that we handle inferiors which change their process group"""
+
+import os
+import unittest2
+import lldb
+from lldbtest import *
+import lldbutil
+
+
+class ChangeProcessGroupTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+        # Find the line number to break for main.c.
+        self.line = line_number('main.c', '// Set breakpoint here')
+
+    @skipIfWindows # setpgid call does not exist on Windows
+    @skipUnlessDarwin
+    @dsym_test
+    def test_setpgid_with_dsym(self):
+        self.buildDsym()
+        self.setpgid()
+
+    @skipIfWindows # setpgid call does not exist on Windows
+    @dwarf_test
+    def test_setpgid_with_dwarf(self):
+        self.buildDwarf()
+        self.setpgid()
+
+    def run_platform_command(self, cmd):
+        platform = self.dbg.GetSelectedPlatform()
+        shell_command = lldb.SBPlatformShellCommand(cmd)
+        err = platform.Run(shell_command)
+        return (err, shell_command.GetStatus(), shell_command.GetOutput())
+
+    def setpgid(self):
+        exe = os.path.join(os.getcwd(), 'a.out')
+
+        # Use a file as a synchronization point between test and inferior.
+        pid_file_path = os.path.join(self.get_process_working_directory(),
+                                     "pid_file_%d" % (int(time.time())))
+        self.addTearDownHook(lambda: self.run_platform_command("rm %s" % (pid_file_path)))
+
+        popen = self.spawnSubprocess(exe, [pid_file_path])
+        self.addTearDownHook(self.cleanupSubprocesses)
+
+        max_attempts = 5
+        for i in range(max_attempts):
+            err, retcode, msg = self.run_platform_command("ls %s" % pid_file_path)
+            if err.Success() and retcode == 0:
+                break
+            else:
+                print msg
+            if i < max_attempts:
+                # Exponential backoff!
+                time.sleep(pow(2, i) * 0.25)
+        else:
+            self.fail("Child PID file %s not found even after %d attempts." % (pid_file_path, max_attempts))
+
+        err, retcode, pid = self.run_platform_command("cat %s" % (pid_file_path))
+
+        self.assertTrue(err.Success() and retcode == 0,
+                "Failed to read file %s: %s, retcode: %d" % (pid_file_path, err.GetCString(), retcode))
+
+
+        # Create a target by the debugger.
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+
+        listener = lldb.SBListener("my.attach.listener")
+        error = lldb.SBError()
+        process = target.AttachToProcessWithID(listener, int(pid), error)
+        self.assertTrue(error.Success() and process, PROCESS_IS_VALID)
+
+        # set a breakpoint just before the setpgid() call
+        lldbutil.run_break_set_by_file_and_line(self, 'main.c', self.line)
+
+        thread = process.GetSelectedThread()
+        # this gives a chance for the thread to exit the sleep syscall and sidesteps
+        # <https://llvm.org/bugs/show_bug.cgi?id=23659> on linux
+        thread.StepInstruction(False)
+
+        # release the child from its loop
+        self.expect("expr release_child_flag = 1", substrs = ["= 1"])
+        process.Continue()
+
+        # make sure the child's process group id is different from its pid
+        self.expect("print (int)getpgid(0)", substrs = [pid], matching=False)
+
+        # step over the setpgid() call
+        thread.StepOver()
+        self.assertEqual(thread.GetStopReason(), lldb.eStopReasonPlanComplete)
+
+        # verify that the process group has been set correctly
+        # this also checks that we are still in full control of the child
+        self.expect("print (pid_t)getpgid(0)", substrs = [pid])
+
+        # run to completion
+        process.Continue()
+        self.assertEqual(process.GetState(), lldb.eStateExited)
+
+if __name__ == '__main__':
+    import atexit
+    lldb.SBDebugger.Initialize()
+    atexit.register(lambda: lldb.SBDebugger.Terminate())
+    unittest2.main()

Added: lldb/trunk/test/functionalities/process_group/main.c
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/process_group/main.c?rev=238405&view=auto
==============================================================================
--- lldb/trunk/test/functionalities/process_group/main.c (added)
+++ lldb/trunk/test/functionalities/process_group/main.c Thu May 28 03:59:21 2015
@@ -0,0 +1,86 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#if defined(__linux__)
+#include <sys/prctl.h>
+#endif
+
+volatile int release_child_flag = 0;
+
+int main(int argc, char const *argv[])
+{
+#if defined(__linux__)
+    // Immediately enable any ptracer so that we can allow the stub attach
+    // operation to succeed.  Some Linux kernels are locked down so that
+    // only an ancestor process can be a ptracer of a process.  This disables that
+    // restriction.  Without it, attach-related stub tests will fail.
+#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY)
+    // For now we execute on best effort basis.  If this fails for
+    // some reason, so be it.
+    const int prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
+    (void) prctl_result;
+#endif
+#endif
+
+    pid_t child = fork();
+    if (child == -1)
+    {
+        perror("fork");
+        return 1;
+    }
+
+    if (child > 0)
+    { // parent
+        if (argc < 2)
+        {
+            fprintf(stderr, "Need pid filename.\n");
+            return 2;
+        }
+
+        // Let the test suite know the child's pid.
+        FILE *pid_file = fopen(argv[1], "w");
+        if (pid_file == NULL)
+        {
+            perror("fopen");
+            return 3;
+        }
+
+        fprintf(pid_file, "%d\n", child);
+        if (fclose(pid_file) == EOF)
+        {
+            perror("fclose");
+            return 4;
+        }
+
+        // And wait for the child to finish it's work.
+        int status = 0;
+        pid_t wpid = wait(&status);
+        if (wpid == -1)
+        {
+            perror("wait");
+            return 5;
+        }
+        if (wpid != child)
+        {
+            fprintf(stderr, "wait() waited for wrong child\n");
+            return 6;
+        }
+        if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+        {
+            fprintf(stderr, "child did not exit correctly\n");
+            return 7;
+        }
+    }
+    else
+    { // child
+        while (! release_child_flag) // Wait for debugger to attach
+            sleep(1);
+
+        printf("Child's previous process group is: %d\n", getpgid(0));
+        setpgid(0, 0); // Set breakpoint here
+        printf("Child's process group set to: %d\n", getpgid(0));
+    }
+
+    return 0;
+}





More information about the lldb-commits mailing list