[Lldb-commits] [lldb] r183340 - Add test cases for attaching to a process after fork

Daniel Malea daniel.malea at intel.com
Wed Jun 5 14:07:03 PDT 2013


Author: dmalea
Date: Wed Jun  5 16:07:02 2013
New Revision: 183340

URL: http://llvm.org/viewvc/llvm-project?rev=183340&view=rev
Log:
Add test cases for attaching to a process after fork
- one test case is due to llvm.org/pr16229
- other test case uses a Linux workaround for above by using os.fork() instead of subprocess module

Patch by Andy Kaylor!


Added:
    lldb/trunk/test/functionalities/thread/create_after_attach/
    lldb/trunk/test/functionalities/thread/create_after_attach/Makefile
    lldb/trunk/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py
    lldb/trunk/test/functionalities/thread/create_after_attach/main.c
Modified:
    lldb/trunk/test/lldbtest.py

Added: lldb/trunk/test/functionalities/thread/create_after_attach/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/thread/create_after_attach/Makefile?rev=183340&view=auto
==============================================================================
--- lldb/trunk/test/functionalities/thread/create_after_attach/Makefile (added)
+++ lldb/trunk/test/functionalities/thread/create_after_attach/Makefile Wed Jun  5 16:07:02 2013
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+
+C_SOURCES := main.c
+include $(LEVEL)/Makefile.rules

Added: lldb/trunk/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py?rev=183340&view=auto
==============================================================================
--- lldb/trunk/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py (added)
+++ lldb/trunk/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py Wed Jun  5 16:07:02 2013
@@ -0,0 +1,124 @@
+"""
+Test thread creation after process attach.
+"""
+
+import os, time
+import unittest2
+import lldb
+from lldbtest import *
+import lldbutil
+
+class CreateAfterAttachTestCase(TestBase):
+
+    mydir = os.path.join("functionalities", "thread", "create_after_attach")
+
+    @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
+    @dsym_test
+    def test_create_after_attach_with_dsym(self):
+        """Test thread creation after process attach."""
+        self.buildDsym(dictionary=self.getBuildFlags(use_cpp11=False))
+        self.create_after_attach(use_fork=False)
+
+    @skipIfLinux # Hangs, see llvm.org/pr16229
+    @dwarf_test
+    def test_create_after_attach_with_dwarf_and_popen(self):
+        """Test thread creation after process attach."""
+        self.buildDwarf(dictionary=self.getBuildFlags(use_cpp11=False))
+        self.create_after_attach(use_fork=False)
+
+    @dwarf_test
+    def test_create_after_attach_with_dwarf_and_fork(self):
+        """Test thread creation after process attach."""
+        self.buildDwarf(dictionary=self.getBuildFlags(use_cpp11=False))
+        self.create_after_attach(use_fork=True)
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+        # Find the line numbers for our breakpoints.
+        self.break_1 = line_number('main.c', '// Set first breakpoint here')
+        self.break_2 = line_number('main.c', '// Set second breakpoint here')
+        self.break_3 = line_number('main.c', '// Set third breakpoint here')
+
+    def create_after_attach(self, use_fork):
+        """Test thread creation after process attach."""
+
+        exe = os.path.join(os.getcwd(), "a.out")
+
+        # Spawn a new process
+        if use_fork:
+            pid = self.forkSubprocess(exe)
+        else:
+            popen = self.spawnSubprocess(exe)
+            pid = popen.pid
+        self.addTearDownHook(self.cleanupSubprocesses)
+
+        # Attach to the spawned process
+        self.runCmd("process attach -p " + str(pid))
+
+        target = self.dbg.GetSelectedTarget()
+
+        process = target.GetProcess()
+        self.assertTrue(process, PROCESS_IS_VALID)
+
+        # This should create a breakpoint in the main thread.
+        lldbutil.run_break_set_by_file_and_line (self, "main.c", self.break_1, num_expected_locations=1)
+
+        # This should create a breakpoint in the second child thread.
+        lldbutil.run_break_set_by_file_and_line (self, "main.c", self.break_2, num_expected_locations=1)
+
+        # This should create a breakpoint in the first child thread.
+        lldbutil.run_break_set_by_file_and_line (self, "main.c", self.break_3, num_expected_locations=1)
+
+        # Run to the first breakpoint
+        self.runCmd("continue")
+
+        # The stop reason of the thread should be breakpoint.
+        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+            substrs = ['stopped',
+                       '* thread #1',
+                       'stop reason = breakpoint',
+                       'thread #2'])
+
+        # Change a variable to escape the loop
+        self.runCmd("expression main_thread_continue = 1")
+
+        # Run to the second breakpoint
+        self.runCmd("continue")
+        self.runCmd("thread select 3")
+
+        # The stop reason of the thread should be breakpoint.
+        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+            substrs = ['stopped',
+                       'thread #1',
+                       'thread #2',
+                       '* thread #3',
+                       'stop reason = breakpoint'])
+
+        # Change a variable to escape the loop
+        self.runCmd("expression child_thread_continue = 1")
+
+        # Run to the third breakpoint
+        self.runCmd("continue")
+        self.runCmd("thread select 2")
+
+        # The stop reason of the thread should be breakpoint.
+        # Thread 3 may or may not have already exited.
+        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+            substrs = ['stopped',
+                       'thread #1',
+                       '* thread #2',
+                       'stop reason = breakpoint'])
+
+        # Run to completion
+        self.runCmd("continue")
+
+        # At this point, the inferior process should have exited.
+        self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED)
+
+
+if __name__ == '__main__':
+    import atexit
+    lldb.SBDebugger.Initialize()
+    atexit.register(lambda: lldb.SBDebugger.Terminate())
+    unittest2.main()

Added: lldb/trunk/test/functionalities/thread/create_after_attach/main.c
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/thread/create_after_attach/main.c?rev=183340&view=auto
==============================================================================
--- lldb/trunk/test/functionalities/thread/create_after_attach/main.c (added)
+++ lldb/trunk/test/functionalities/thread/create_after_attach/main.c Wed Jun  5 16:07:02 2013
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <pthread.h>
+
+volatile int g_thread_2_continuing = 0;
+
+void *
+thread_1_func (void *input)
+{
+    // Waiting to be released by the debugger.
+    while (!g_thread_2_continuing) // The debugger will change this value
+    {
+        usleep(1);
+    }
+
+    // Return
+    return NULL;  // Set third breakpoint here
+}
+
+void *
+thread_2_func (void *input)
+{
+    // Waiting to be released by the debugger.
+    int child_thread_continue = 0;
+    while (!child_thread_continue) // The debugger will change this value
+    {
+        usleep(1);  // Set second breakpoint here
+    }
+
+    // Release thread 1
+    g_thread_2_continuing = 1;
+
+    // Return
+    return NULL;
+}
+
+int main(int argc, char const *argv[])
+{
+    pthread_t thread_1;
+    pthread_t thread_2;
+
+    // Create a new thread
+    pthread_create (&thread_1, NULL, thread_1_func, NULL);
+
+    // Waiting to be attached by the debugger.
+    int main_thread_continue = 0;
+    while (!main_thread_continue) // The debugger will change this value
+    {
+        usleep(1);  // Set first breakpoint here
+    }
+
+    // Create another new thread
+    pthread_create (&thread_2, NULL, thread_2_func, NULL);
+
+    // Wait for the threads to finish.
+    pthread_join(thread_1, NULL);
+    pthread_join(thread_2, NULL);
+
+    printf("Exiting now\n");
+}

Modified: lldb/trunk/test/lldbtest.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lldbtest.py?rev=183340&r1=183339&r2=183340&view=diff
==============================================================================
--- lldb/trunk/test/lldbtest.py (original)
+++ lldb/trunk/test/lldbtest.py Wed Jun  5 16:07:02 2013
@@ -34,6 +34,7 @@ $
 import os, sys, traceback
 import os.path
 import re
+import signal
 from subprocess import *
 import StringIO
 import time
@@ -820,6 +821,9 @@ class Base(unittest2.TestCase):
         # List of spawned subproces.Popen objects
         self.subprocesses = []
 
+        # List of forked process PIDs
+        self.forkedProcessPids = []
+
         # Create a string buffer to record the session info, to be dumped into a
         # test case specific file if test failure is encountered.
         self.session = StringIO.StringIO()
@@ -886,6 +890,10 @@ class Base(unittest2.TestCase):
                 p.terminate()
             del p
         del self.subprocesses[:]
+        # Ensure any forked processes are cleaned up
+        for pid in self.forkedProcessPids:
+            if os.path.exists("/proc/" + str(pid)):
+                os.kill(pid, signal.SIGTERM)
 
     def spawnSubprocess(self, executable, args=[]):
         """ Creates a subprocess.Popen object with the specified executable and arguments,
@@ -904,6 +912,29 @@ class Base(unittest2.TestCase):
         self.subprocesses.append(proc)
         return proc
 
+    def forkSubprocess(self, executable, args=[]):
+        """ Fork a subprocess with its own group ID.
+            NOTE: if using this function, ensure you also call:
+
+              self.addTearDownHook(self.cleanupSubprocesses)
+
+            otherwise the test suite will leak processes.
+        """
+        child_pid = os.fork()
+        if child_pid == 0:
+            # If more I/O support is required, this can be beefed up.
+            fd = os.open(os.devnull, os.O_RDWR)
+            os.dup2(fd, 0)
+            os.dup2(fd, 1)
+            os.dup2(fd, 2)
+            # This call causes the child to have its of group ID
+            os.setpgid(0,0)
+            os.execvp(executable, [executable] + args)
+        # Give the child time to get through the execvp() call
+        time.sleep(0.1)
+        self.forkedProcessPids.append(child_pid)
+        return child_pid
+
     def HideStdout(self):
         """Hide output to stdout from the user.
 





More information about the lldb-commits mailing list