summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Malea <daniel.malea@intel.com>2013-06-05 21:07:02 +0000
committerDaniel Malea <daniel.malea@intel.com>2013-06-05 21:07:02 +0000
commit692074622c8671941eee2c6003ba850ed6f387fb (patch)
tree306699f034bb334c5cca1f007d21e9e90d753632
parent23bcd0ad81c5d9630787f928318b14ecde88238e (diff)
downloadbcm5719-llvm-692074622c8671941eee2c6003ba850ed6f387fb.tar.gz
bcm5719-llvm-692074622c8671941eee2c6003ba850ed6f387fb.zip
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! llvm-svn: 183340
-rw-r--r--lldb/test/functionalities/thread/create_after_attach/Makefile4
-rw-r--r--lldb/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py124
-rw-r--r--lldb/test/functionalities/thread/create_after_attach/main.c60
-rw-r--r--lldb/test/lldbtest.py31
4 files changed, 219 insertions, 0 deletions
diff --git a/lldb/test/functionalities/thread/create_after_attach/Makefile b/lldb/test/functionalities/thread/create_after_attach/Makefile
new file mode 100644
index 00000000000..3b5a8a30fa4
--- /dev/null
+++ b/lldb/test/functionalities/thread/create_after_attach/Makefile
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+
+C_SOURCES := main.c
+include $(LEVEL)/Makefile.rules
diff --git a/lldb/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py b/lldb/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py
new file mode 100644
index 00000000000..082922c0b6c
--- /dev/null
+++ b/lldb/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py
@@ -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()
diff --git a/lldb/test/functionalities/thread/create_after_attach/main.c b/lldb/test/functionalities/thread/create_after_attach/main.c
new file mode 100644
index 00000000000..68340260bc9
--- /dev/null
+++ b/lldb/test/functionalities/thread/create_after_attach/main.c
@@ -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");
+}
diff --git a/lldb/test/lldbtest.py b/lldb/test/lldbtest.py
index 353f4553fc9..00502d7d830 100644
--- a/lldb/test/lldbtest.py
+++ b/lldb/test/lldbtest.py
@@ -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.
OpenPOWER on IntegriCloud