diff options
author | dim <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
commit | 78b9749c0a4ea980a8b934645da6ae98fcc665e8 (patch) | |
tree | dd2a1ddf0476664c2b823409c36cbccd52662ca7 /packages/Python/lldbsuite/test/functionalities/thread/concurrent_events | |
parent | 60cb593f9d55fa5ca7a5372b731f2330345b4b9a (diff) | |
download | FreeBSD-src-78b9749c0a4ea980a8b934645da6ae98fcc665e8.zip FreeBSD-src-78b9749c0a4ea980a8b934645da6ae98fcc665e8.tar.gz |
Vendor import of lldb trunk r256945:
https://llvm.org/svn/llvm-project/lldb/trunk@256945
Diffstat (limited to 'packages/Python/lldbsuite/test/functionalities/thread/concurrent_events')
3 files changed, 698 insertions, 0 deletions
diff --git a/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/Makefile new file mode 100644 index 0000000..469c080 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +ENABLE_THREADS := YES + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py new file mode 100644 index 0000000..9eb25b6 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py @@ -0,0 +1,491 @@ +""" +A stress-test of sorts for LLDB's handling of threads in the inferior. + +This test sets a breakpoint in the main thread where test parameters (numbers of +threads) can be adjusted, runs the inferior to that point, and modifies the +locals that control the event thread counts. This test also sets a breakpoint in +breakpoint_func (the function executed by each 'breakpoint' thread) and a +watchpoint on a global modified in watchpoint_func. The inferior is continued +until exit or a crash takes place, and the number of events seen by LLDB is +verified to match the expected number of events. +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +@skipIfWindows +@expectedFailureAll(archs=['mips64', 'mips64el']) # Atomic sequences are not supported yet for MIPS in LLDB. +class ConcurrentEventsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # + ## Tests for multiple threads that generate a single event. + # + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_many_breakpoints(self): + """Test 100 breakpoints from 100 threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=100) + + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_many_watchpoints(self): + """Test 100 watchpoints from 100 threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=100) + + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_many_signals(self): + """Test 100 signals from 100 threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=100) + + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_many_crash(self): + """Test 100 threads that cause a segfault.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=100) + + + # + ## Tests for concurrent signal and breakpoint + # + @skipIfFreeBSD # timing out on buildbot + def test_signal_break(self): + """Test signal and a breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, num_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_delay_signal_break(self): + """Test (1-second delay) signal and a breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, num_delay_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_signal_delay_break(self): + """Test signal and a (1 second delay) breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_breakpoint_threads=1, num_signal_threads=1) + + + # + ## Tests for concurrent watchpoint and breakpoint + # + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_watch_break(self): + """Test watchpoint and a breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_delay_watch_break(self): + """Test (1-second delay) watchpoint and a breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, num_delay_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_watch_break_delay(self): + """Test watchpoint and a (1 second delay) breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_breakpoint_threads=1, num_watchpoint_threads=1) + + # + ## Tests for concurrent signal and watchpoint + # + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_signal_watch(self): + """Test a watchpoint and a signal in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=1, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_delay_signal_watch(self): + """Test a watchpoint and a (1 second delay) signal in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_signal_threads=1, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + @expectedFailureAll("llvm.org/pr16714", oslist=["linux"], archs=["i386"]) + def test_signal_delay_watch(self): + """Test a (1 second delay) watchpoint and a signal in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=1, num_delay_watchpoint_threads=1) + + + # + ## Tests for multiple breakpoint threads + # + @skipIfFreeBSD # timing out on buildbot + def test_two_breakpoint_threads(self): + """Test two threads that trigger a breakpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=2) + + @skipIfFreeBSD # timing out on buildbot + def test_breakpoint_one_delay_breakpoint_threads(self): + """Test threads that trigger a breakpoint where one thread has a 1 second delay. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, + num_delay_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_two_breakpoints_one_signal(self): + """Test two threads that trigger a breakpoint and one signal thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=2, num_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_breakpoint_delay_breakpoint_one_signal(self): + """Test two threads that trigger a breakpoint (one with a 1 second delay) and one signal thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, + num_delay_breakpoint_threads=1, + num_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_two_breakpoints_one_delay_signal(self): + """Test two threads that trigger a breakpoint and one (1 second delay) signal thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=2, num_delay_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_breakpoints_one_watchpoint(self): + """Test two threads that trigger a breakpoint and one watchpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=2, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_breakpoints_delayed_breakpoint_one_watchpoint(self): + """Test a breakpoint, a delayed breakpoint, and one watchpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, + num_delay_breakpoint_threads=1, + num_watchpoint_threads=1) + + # + ## Tests for multiple watchpoint threads + # + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_watchpoint_threads(self): + """Test two threads that trigger a watchpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=2) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_watchpoint_with_delay_watchpoint_threads(self): + """Test two threads that trigger a watchpoint where one thread has a 1 second delay. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=1, + num_delay_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_watchpoints_one_breakpoint(self): + """Test two threads that trigger a watchpoint and one breakpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=2, num_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_watchpoints_one_delay_breakpoint(self): + """Test two threads that trigger a watchpoint and one (1 second delay) breakpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=2, num_delay_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_watchpoint_delay_watchpoint_one_breakpoint(self): + """Test two threads that trigger a watchpoint (one with a 1 second delay) and one breakpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=1, + num_delay_watchpoint_threads=1, + num_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_watchpoints_one_signal(self): + """Test two threads that trigger a watchpoint and one signal thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=2, num_signal_threads=1) + + # + ## Test for watchpoint, signal and breakpoint happening concurrently + # + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_signal_watch_break(self): + """Test a signal/watchpoint/breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=1, + num_watchpoint_threads=1, + num_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_signal_watch_break(self): + """Test one signal thread with 5 watchpoint and breakpoint threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=1, + num_watchpoint_threads=5, + num_breakpoint_threads=5) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_signal_watch_break(self): + """Test with 5 watchpoint and breakpoint threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=5, + num_breakpoint_threads=5) + + + # + ## Test for crashing threads happening concurrently with other events + # + @skipIfFreeBSD # timing out on buildbot + def test_crash_with_break(self): + """ Test a thread that crashes while another thread hits a breakpoint.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=1, num_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_crash_with_watchpoint(self): + """ Test a thread that crashes while another thread hits a watchpoint.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=1, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_crash_with_signal(self): + """ Test a thread that crashes while another thread generates a signal.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=1, num_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_crash_with_watchpoint_breakpoint_signal(self): + """ Test a thread that crashes while other threads generate a signal and hit a watchpoint and breakpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=1, + num_breakpoint_threads=1, + num_signal_threads=1, + num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_delayed_crash_with_breakpoint_watchpoint(self): + """ Test a thread with a delayed crash while other threads hit a watchpoint and a breakpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_crash_threads=1, + num_breakpoint_threads=1, + num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_delayed_crash_with_breakpoint_signal(self): + """ Test a thread with a delayed crash while other threads generate a signal and hit a breakpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_crash_threads=1, + num_breakpoint_threads=1, + num_signal_threads=1) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.filename = 'main.cpp' + self.thread_breakpoint_line = line_number(self.filename, '// Set breakpoint here') + self.setup_breakpoint_line = line_number(self.filename, '// Break here and adjust num') + self.finish_breakpoint_line = line_number(self.filename, '// Break here and verify one thread is active') + + def describe_threads(self): + ret = [] + for x in self.inferior_process: + id = x.GetIndexID() + reason = x.GetStopReason() + status = "stopped" if x.IsStopped() else "running" + reason_str = lldbutil.stop_reason_to_str(reason) + if reason == lldb.eStopReasonBreakpoint: + bpid = x.GetStopReasonDataAtIndex(0) + bp = self.inferior_target.FindBreakpointByID(bpid) + reason_str = "%s hit %d times" % (lldbutil.get_description(bp), bp.GetHitCount()) + elif reason == lldb.eStopReasonWatchpoint: + watchid = x.GetStopReasonDataAtIndex(0) + watch = self.inferior_target.FindWatchpointByID(watchid) + reason_str = "%s hit %d times" % (lldbutil.get_description(watch), watch.GetHitCount()) + elif reason == lldb.eStopReasonSignal: + signals = self.inferior_process.GetUnixSignals() + signal_name = signals.GetSignalAsCString(x.GetStopReasonDataAtIndex(0)) + reason_str = "signal %s" % signal_name + + location = "\t".join([lldbutil.get_description(x.GetFrameAtIndex(i)) for i in range(x.GetNumFrames())]) + ret.append("thread %d %s due to %s at\n\t%s" % (id, status, reason_str, location)) + return ret + + def add_breakpoint(self, line, descriptions): + """ Adds a breakpoint at self.filename:line and appends its description to descriptions, and + returns the LLDB SBBreakpoint object. + """ + + bpno = lldbutil.run_break_set_by_file_and_line(self, self.filename, line, num_expected_locations=-1) + bp = self.inferior_target.FindBreakpointByID(bpno) + descriptions.append(": file = 'main.cpp', line = %d" % self.finish_breakpoint_line) + return bp + + def inferior_done(self): + """ Returns true if the inferior is done executing all the event threads (and is stopped at self.finish_breakpoint, + or has terminated execution. + """ + return self.finish_breakpoint.GetHitCount() > 0 or \ + self.crash_count > 0 or \ + self.inferior_process.GetState() == lldb.eStateExited + + def count_signaled_threads(self): + count = 0 + for thread in self.inferior_process: + if thread.GetStopReason() == lldb.eStopReasonSignal and thread.GetStopReasonDataAtIndex(0) == self.inferior_process.GetUnixSignals().GetSignalNumberFromName('SIGUSR1'): + count += 1 + return count + + def do_thread_actions(self, + num_breakpoint_threads = 0, + num_signal_threads = 0, + num_watchpoint_threads = 0, + num_crash_threads = 0, + num_delay_breakpoint_threads = 0, + num_delay_signal_threads = 0, + num_delay_watchpoint_threads = 0, + num_delay_crash_threads = 0): + """ Sets a breakpoint in the main thread where test parameters (numbers of threads) can be adjusted, runs the inferior + to that point, and modifies the locals that control the event thread counts. Also sets a breakpoint in + breakpoint_func (the function executed by each 'breakpoint' thread) and a watchpoint on a global modified in + watchpoint_func. The inferior is continued until exit or a crash takes place, and the number of events seen by LLDB + is verified to match the expected number of events. + """ + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Get the target + self.inferior_target = self.dbg.GetSelectedTarget() + + expected_bps = [] + + # Initialize all the breakpoints (main thread/aux thread) + self.setup_breakpoint = self.add_breakpoint(self.setup_breakpoint_line, expected_bps) + self.finish_breakpoint = self.add_breakpoint(self.finish_breakpoint_line, expected_bps) + + # Set the thread breakpoint + if num_breakpoint_threads + num_delay_breakpoint_threads > 0: + self.thread_breakpoint = self.add_breakpoint(self.thread_breakpoint_line, expected_bps) + + # Verify breakpoints + self.expect("breakpoint list -f", "Breakpoint locations shown correctly", substrs = expected_bps) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Check we are at line self.setup_breakpoint + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + # Initialize the (single) watchpoint on the global variable (g_watchme) + if num_watchpoint_threads + num_delay_watchpoint_threads > 0: + self.runCmd("watchpoint set variable g_watchme") + for w in self.inferior_target.watchpoint_iter(): + self.thread_watchpoint = w + self.assertTrue("g_watchme" in str(self.thread_watchpoint), "Watchpoint location not shown correctly") + + # Get the process + self.inferior_process = self.inferior_target.GetProcess() + + # We should be stopped at the setup site where we can set the number of + # threads doing each action (break/crash/signal/watch) + self.assertEqual(self.inferior_process.GetNumThreads(), 1, 'Expected to stop before any additional threads are spawned.') + + self.runCmd("expr num_breakpoint_threads=%d" % num_breakpoint_threads) + self.runCmd("expr num_crash_threads=%d" % num_crash_threads) + self.runCmd("expr num_signal_threads=%d" % num_signal_threads) + self.runCmd("expr num_watchpoint_threads=%d" % num_watchpoint_threads) + + self.runCmd("expr num_delay_breakpoint_threads=%d" % num_delay_breakpoint_threads) + self.runCmd("expr num_delay_crash_threads=%d" % num_delay_crash_threads) + self.runCmd("expr num_delay_signal_threads=%d" % num_delay_signal_threads) + self.runCmd("expr num_delay_watchpoint_threads=%d" % num_delay_watchpoint_threads) + + # Continue the inferior so threads are spawned + self.runCmd("continue") + + # Make sure we see all the threads. The inferior program's threads all synchronize with a pseudo-barrier; that is, + # the inferior program ensures all threads are started and running before any thread triggers its 'event'. + num_threads = self.inferior_process.GetNumThreads() + expected_num_threads = num_breakpoint_threads + num_delay_breakpoint_threads \ + + num_signal_threads + num_delay_signal_threads \ + + num_watchpoint_threads + num_delay_watchpoint_threads \ + + num_crash_threads + num_delay_crash_threads + 1 + self.assertEqual(num_threads, expected_num_threads, + 'Expected to see %d threads, but seeing %d. Details:\n%s' % (expected_num_threads, + num_threads, + "\n\t".join(self.describe_threads()))) + + self.signal_count = self.count_signaled_threads() + self.crash_count = len(lldbutil.get_crashed_threads(self, self.inferior_process)) + + # Run to completion (or crash) + while not self.inferior_done(): + if self.TraceOn(): + self.runCmd("thread backtrace all") + self.runCmd("continue") + self.signal_count += self.count_signaled_threads() + self.crash_count += len(lldbutil.get_crashed_threads(self, self.inferior_process)) + + if num_crash_threads > 0 or num_delay_crash_threads > 0: + # Expecting a crash + self.assertTrue(self.crash_count > 0, + "Expecting at least one thread to crash. Details: %s" % "\t\n".join(self.describe_threads())) + + # Ensure the zombie process is reaped + self.runCmd("process kill") + + elif num_crash_threads == 0 and num_delay_crash_threads == 0: + # There should be a single active thread (the main one) which hit the breakpoint after joining + self.assertEqual(1, self.finish_breakpoint.GetHitCount(), "Expected main thread (finish) breakpoint to be hit once") + + num_threads = self.inferior_process.GetNumThreads() + self.assertEqual(1, num_threads, "Expecting 1 thread but seeing %d. Details:%s" % (num_threads, + "\n\t".join(self.describe_threads()))) + self.runCmd("continue") + + # The inferior process should have exited without crashing + self.assertEqual(0, self.crash_count, "Unexpected thread(s) in crashed state") + self.assertEqual(self.inferior_process.GetState(), lldb.eStateExited, PROCESS_EXITED) + + # Verify the number of actions took place matches expected numbers + expected_breakpoint_threads = num_delay_breakpoint_threads + num_breakpoint_threads + breakpoint_hit_count = self.thread_breakpoint.GetHitCount() if expected_breakpoint_threads > 0 else 0 + self.assertEqual(expected_breakpoint_threads, breakpoint_hit_count, + "Expected %d breakpoint hits, but got %d" % (expected_breakpoint_threads, breakpoint_hit_count)) + + expected_signal_threads = num_delay_signal_threads + num_signal_threads + self.assertEqual(expected_signal_threads, self.signal_count, + "Expected %d stops due to signal delivery, but got %d" % (expected_signal_threads, self.signal_count)) + + expected_watchpoint_threads = num_delay_watchpoint_threads + num_watchpoint_threads + watchpoint_hit_count = self.thread_watchpoint.GetHitCount() if expected_watchpoint_threads > 0 else 0 + self.assertEqual(expected_watchpoint_threads, watchpoint_hit_count, + "Expected %d watchpoint hits, got %d" % (expected_watchpoint_threads, watchpoint_hit_count)) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/main.cpp new file mode 100644 index 0000000..ac2535c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/main.cpp @@ -0,0 +1,200 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which multiple events +// (breakpoints, watchpoints, crashes, and signal generation/delivery) happen +// from multiple threads. The test expects the debugger to set a breakpoint on +// the main thread (before any worker threads are spawned) and modify variables +// which control the number of threads that are spawned for each action. + +#include <atomic> +#include <vector> +using namespace std; + +#include <pthread.h> + +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +typedef std::vector<std::pair<unsigned, void*(*)(void*)> > action_counts; +typedef std::vector<pthread_t> thread_vector; + +std::atomic_int g_barrier; +int g_breakpoint = 0; +int g_sigusr1_count = 0; +std::atomic_int g_watchme; + +struct action_args { + int delay; +}; + +// Perform any extra actions required by thread 'input' arg +void do_action_args(void *input) { + if (input) { + action_args *args = static_cast<action_args*>(input); + sleep(args->delay); + } +} + +void * +breakpoint_func (void *input) +{ + // Wait until all threads are running + pseudo_barrier_wait(g_barrier); + do_action_args(input); + + // Do something + g_breakpoint++; // Set breakpoint here + return 0; +} + +void * +signal_func (void *input) { + // Wait until all threads are running + pseudo_barrier_wait(g_barrier); + do_action_args(input); + + // Send a user-defined signal to the current process + //kill(getpid(), SIGUSR1); + // Send a user-defined signal to the current thread + pthread_kill(pthread_self(), SIGUSR1); + + return 0; +} + +void * +watchpoint_func (void *input) { + pseudo_barrier_wait(g_barrier); + do_action_args(input); + + g_watchme += 1; // watchpoint triggers here + return 0; +} + +void * +crash_func (void *input) { + pseudo_barrier_wait(g_barrier); + do_action_args(input); + + int *a = 0; + *a = 5; // crash happens here + return 0; +} + +void sigusr1_handler(int sig) { + if (sig == SIGUSR1) + g_sigusr1_count += 1; // Break here in signal handler +} + +/// Register a simple function for to handle signal +void register_signal_handler(int signal, void (*handler)(int)) +{ + sigset_t empty_sigset; + sigemptyset(&empty_sigset); + + struct sigaction action; + action.sa_sigaction = 0; + action.sa_mask = empty_sigset; + action.sa_flags = 0; + action.sa_handler = handler; + sigaction(SIGUSR1, &action, 0); +} + +void start_threads(thread_vector& threads, + action_counts& actions, + void* args = 0) { + action_counts::iterator b = actions.begin(), e = actions.end(); + for(action_counts::iterator i = b; i != e; ++i) { + for(unsigned count = 0; count < i->first; ++count) { + pthread_t t; + pthread_create(&t, 0, i->second, args); + threads.push_back(t); + } + } +} + +int dotest() +{ + g_watchme = 0; + + // Actions are triggered immediately after the thread is spawned + unsigned num_breakpoint_threads = 1; + unsigned num_watchpoint_threads = 0; + unsigned num_signal_threads = 1; + unsigned num_crash_threads = 0; + + // Actions below are triggered after a 1-second delay + unsigned num_delay_breakpoint_threads = 0; + unsigned num_delay_watchpoint_threads = 0; + unsigned num_delay_signal_threads = 0; + unsigned num_delay_crash_threads = 0; + + register_signal_handler(SIGUSR1, sigusr1_handler); // Break here and adjust num_[breakpoint|watchpoint|signal|crash]_threads + + unsigned total_threads = num_breakpoint_threads \ + + num_watchpoint_threads \ + + num_signal_threads \ + + num_crash_threads \ + + num_delay_breakpoint_threads \ + + num_delay_watchpoint_threads \ + + num_delay_signal_threads \ + + num_delay_crash_threads; + + // Don't let either thread do anything until they're both ready. + pseudo_barrier_init(g_barrier, total_threads); + + action_counts actions; + actions.push_back(std::make_pair(num_breakpoint_threads, breakpoint_func)); + actions.push_back(std::make_pair(num_watchpoint_threads, watchpoint_func)); + actions.push_back(std::make_pair(num_signal_threads, signal_func)); + actions.push_back(std::make_pair(num_crash_threads, crash_func)); + + action_counts delay_actions; + delay_actions.push_back(std::make_pair(num_delay_breakpoint_threads, breakpoint_func)); + delay_actions.push_back(std::make_pair(num_delay_watchpoint_threads, watchpoint_func)); + delay_actions.push_back(std::make_pair(num_delay_signal_threads, signal_func)); + delay_actions.push_back(std::make_pair(num_delay_crash_threads, crash_func)); + + // Create threads that handle instant actions + thread_vector threads; + start_threads(threads, actions); + + // Create threads that handle delayed actions + action_args delay_arg; + delay_arg.delay = 1; + start_threads(threads, delay_actions, &delay_arg); + + // Join all threads + typedef std::vector<pthread_t>::iterator thread_iterator; + for(thread_iterator t = threads.begin(); t != threads.end(); ++t) + pthread_join(*t, 0); + + return 0; +} + +int main () +{ + dotest(); + return 0; // Break here and verify one thread is active. +} + + |