diff options
| author | Daniel Malea <daniel.malea@intel.com> | 2013-06-05 21:07:02 +0000 |
|---|---|---|
| committer | Daniel Malea <daniel.malea@intel.com> | 2013-06-05 21:07:02 +0000 |
| commit | 692074622c8671941eee2c6003ba850ed6f387fb (patch) | |
| tree | 306699f034bb334c5cca1f007d21e9e90d753632 | |
| parent | 23bcd0ad81c5d9630787f928318b14ecde88238e (diff) | |
| download | bcm5719-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
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. |

