[Lldb-commits] [lldb] r185889 - Add TestConcurrentEvents test for LLDB's handling of inferior threads

Daniel Malea daniel.malea at intel.com
Mon Jul 8 17:08:01 PDT 2013


Author: dmalea
Date: Mon Jul  8 19:08:01 2013
New Revision: 185889

URL: http://llvm.org/viewvc/llvm-project?rev=185889&view=rev
Log:
Add TestConcurrentEvents test for LLDB's handling of inferior threads
- Test verifies LLDB's handling of inferiors with threads that: hit breakpoints,
  modfiy variables that have watchpoints set, generate user signals, and crash.
- Add a few "stress tests" (with ~100 threads) -- run these with "-l" dotest.py
  flag.
- Fix stop_reason_to_str helper in lldbutil to handle eStopReasonThreadExited.
- Add sort_stopped_threads helper to lldbutil to separate thread lists based
  on stop reason.

Logged llvm.org/pr16566 and llvm.org/pr16567 for bugs exposed.



Added:
    lldb/trunk/test/functionalities/thread/concurrent_events/
    lldb/trunk/test/functionalities/thread/concurrent_events/Makefile
    lldb/trunk/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py
    lldb/trunk/test/functionalities/thread/concurrent_events/main.cpp
Modified:
    lldb/trunk/test/functionalities/watchpoint/multiple_threads/TestWatchpointMultipleThreads.py
    lldb/trunk/test/lldbtest.py
    lldb/trunk/test/lldbutil.py

Added: lldb/trunk/test/functionalities/thread/concurrent_events/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/thread/concurrent_events/Makefile?rev=185889&view=auto
==============================================================================
--- lldb/trunk/test/functionalities/thread/concurrent_events/Makefile (added)
+++ lldb/trunk/test/functionalities/thread/concurrent_events/Makefile Mon Jul  8 19:08:01 2013
@@ -0,0 +1,8 @@
+LEVEL = ../../../make
+
+CXX_SOURCES := main.cpp
+
+CFLAGS_EXTRAS += -std=c++11 -lpthread
+LD_EXTRAS += -lpthread
+
+include $(LEVEL)/Makefile.rules

Added: lldb/trunk/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py?rev=185889&view=auto
==============================================================================
--- lldb/trunk/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py (added)
+++ lldb/trunk/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py Mon Jul  8 19:08:01 2013
@@ -0,0 +1,485 @@
+"""
+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.
+"""
+
+import os, time
+import unittest2
+import lldb
+from lldbtest import *
+import lldbutil
+
+class ConcurrentEventsTestCase(TestBase):
+
+    mydir = os.path.join("functionalities", "thread", "concurrent_events")
+
+    #
+    ## Tests for multiple threads that generate a single event.
+    #
+    @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test")
+    @dwarf_test
+    def test_many_breakpoints_dwarf(self):
+        """Test 100 breakpoints from 100 threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_breakpoint_threads=100)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test")
+    @dwarf_test
+    def test_many_watchpoints_dwarf(self):
+        """Test 100 watchpoints from 100 threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_watchpoint_threads=100)
+
+    @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test")
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @skipIfDarwin # llvm.org/pr16567 -- thread count is incorrect during signal delivery
+    @dwarf_test
+    def test_many_signals_dwarf(self):
+        """Test 100 signals from 100 threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_signal_threads=100)
+
+    @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test")
+    @dwarf_test
+    def test_many_crash_dwarf(self):
+        """Test 100 threads that cause a segfault."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_crash_threads=100)
+
+
+    #
+    ## Tests for concurrent signal and breakpoint
+    #
+    @skipIfDarwin # llvm.org/pr16567 -- thread count is incorrect during signal delivery
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_signal_break_dwarf(self):
+        """Test signal and a breakpoint in multiple threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_breakpoint_threads=1, num_signal_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16567 -- thread count is incorrect during signal delivery
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_delay_signal_break_dwarf(self):
+        """Test (1-second delay) signal and a breakpoint in multiple threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_breakpoint_threads=1, num_delay_signal_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16567 -- thread count is incorrect during signal delivery
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_signal_delay_break_dwarf(self):
+        """Test signal and a (1 second delay) breakpoint in multiple threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_delay_breakpoint_threads=1, num_signal_threads=1)
+
+
+    #
+    ## Tests for concurrent watchpoint and breakpoint
+    #
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @dwarf_test
+    def test_watch_break_dwarf(self):
+        """Test watchpoint and a breakpoint in multiple threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_breakpoint_threads=1, num_watchpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @dwarf_test
+    def test_delay_watch_break_dwarf(self):
+        """Test (1-second delay) watchpoint and a breakpoint in multiple threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_breakpoint_threads=1, num_delay_watchpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @dwarf_test
+    def test_watch_break_dwarf(self):
+        """Test watchpoint and a (1 second delay) breakpoint in multiple threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_delay_breakpoint_threads=1, num_watchpoint_threads=1)
+
+    #
+    ## Tests for concurrent signal and watchpoint
+    #
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_signal_watch_dwarf(self):
+        """Test a watchpoint and a signal in multiple threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_signal_threads=1, num_watchpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_delay_signal_watch_dwarf(self):
+        """Test a watchpoint and a (1 second delay) signal in multiple threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_delay_signal_threads=1, num_watchpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_signal_delay_watch_dwarf(self):
+        """Test a (1 second delay) watchpoint and a signal in multiple threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_signal_threads=1, num_delay_watchpoint_threads=1)
+
+
+    #
+    ## Tests for multiple breakpoint threads
+    #
+    @dwarf_test
+    def test_two_breakpoint_threads_dwarf(self):
+        """Test two threads that trigger a breakpoint. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_breakpoint_threads=2)
+
+    @dwarf_test
+    def test_breakpoint_one_delay_breakpoint_threads_dwarf(self):
+        """Test threads that trigger a breakpoint where one thread has a 1 second delay. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_breakpoint_threads=1,
+                               num_delay_breakpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16567 -- thread count is incorrect during signal delivery
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_two_breakpoints_one_signal_dwarf(self):
+        """Test two threads that trigger a breakpoint and one signal thread. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_breakpoint_threads=2, num_signal_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16567 -- thread count is incorrect during signal delivery
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_breakpoint_delay_breakpoint_one_signal_dwarf(self):
+        """Test two threads that trigger a breakpoint (one with a 1 second delay) and one signal thread. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_breakpoint_threads=1,
+                               num_delay_breakpoint_threads=1,
+                               num_signal_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16567 -- thread count is incorrect during signal delivery
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_two_breakpoints_one_delay_signal_dwarf(self):
+        """Test two threads that trigger a breakpoint and one (1 second delay) signal thread. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_breakpoint_threads=2, num_delay_signal_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @dwarf_test
+    def test_two_breakpoints_one_watchpoint_dwarf(self):
+        """Test two threads that trigger a breakpoint and one watchpoint thread. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_breakpoint_threads=2, num_watchpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @dwarf_test
+    def test_breakpoints_delayed_breakpoint_one_watchpoint_dwarf(self):
+        """Test a breakpoint, a delayed breakpoint, and one watchpoint thread. """
+        self.buildDwarf(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
+    #
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @dwarf_test
+    def test_two_watchpoint_threads_dwarf(self):
+        """Test two threads that trigger a watchpoint. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_watchpoint_threads=2)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @dwarf_test
+    def test_watchpoint_with_delay_waychpoint_threads_dwarf(self):
+        """Test two threads that trigger a watchpoint where one thread has a 1 second delay. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_watchpoint_threads=1,
+                               num_delay_watchpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @dwarf_test
+    def test_two_watchpoints_one_breakpoint_dwarf(self):
+        """Test two threads that trigger a watchpoint and one breakpoint thread. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_watchpoint_threads=2, num_breakpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @dwarf_test
+    def test_two_watchpoints_one_delay_breakpoint_dwarf(self):
+        """Test two threads that trigger a watchpoint and one (1 second delay) breakpoint thread. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_watchpoint_threads=2, num_delay_breakpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @dwarf_test
+    def test_watchpoint_delay_watchpoint_one_breakpoint_dwarf(self):
+        """Test two threads that trigger a watchpoint (one with a 1 second delay) and one breakpoint thread. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_watchpoint_threads=1,
+                               num_delay_watchpoint_threads=1,
+                               num_breakpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_two_watchpoints_one_signal_dwarf(self):
+        """Test two threads that trigger a watchpoint and one signal thread. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_watchpoint_threads=2, num_signal_threads=1)
+
+    #
+    ## Test for watchpoint, signal and breakpoint happening concurrently
+    #
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_signal_watch_break_dwarf(self):
+        """Test a signal/watchpoint/breakpoint in multiple threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_signal_threads=1,
+                               num_watchpoint_threads=1,
+                               num_breakpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_signal_watch_break_dwarf(self):
+        """Test one signal thread with 5 watchpoint and breakpoint threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_signal_threads=1,
+                               num_watchpoint_threads=5,
+                               num_breakpoint_threads=5)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @dwarf_test
+    def test_signal_watch_break_dwarf(self):
+        """Test with 5 watchpoint and breakpoint threads."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_watchpoint_threads=5,
+                               num_breakpoint_threads=5)
+
+
+    #
+    ## Test for crashing threads happening concurrently with other events
+    #
+    @dwarf_test
+    def test_crash_with_break_dwarf(self):
+        """ Test a thread that crashes while another thread hits a breakpoint."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_crash_threads=1, num_breakpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @dwarf_test
+    def test_crash_with_watchpoint_dwarf(self):
+        """ Test a thread that crashes while another thread hits a watchpoint."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_crash_threads=1, num_watchpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16567 -- thread count is incorrect during signal delivery
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_crash_with_signal_dwarf(self):
+        """ Test a thread that crashes while another thread generates a signal."""
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_crash_threads=1, num_signal_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_crash_with_watchpoint_breakpoint_signal_dwarf(self):
+        """ Test a thread that crashes while other threads generate a signal and hit a watchpoint and breakpoint. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_crash_threads=1,
+                               num_breakpoint_threads=1,
+                               num_signal_threads=1,
+                               num_watchpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16566 -- new threads do not respect watchpoints
+    @skipIfLinux # inferior thread enters state D (disk sleep) which causes hang in lldb
+    @dwarf_test
+    def test_delayed_crash_with_breakpoint_watchpoint_dwarf(self):
+        """ Test a thread with a delayed crash while other threads hit a watchpoint and a breakpoint. """
+        self.buildDwarf(dictionary=self.getBuildFlags())
+        self.do_thread_actions(num_delay_crash_threads=1,
+                               num_breakpoint_threads=1,
+                               num_watchpoint_threads=1)
+
+    @skipIfDarwin # llvm.org/pr16567 -- thread count is incorrect during signal delivery
+    @skipIfLinux # llvm.org/pr16567 -- explicit signals cause hangs on Linux
+    @dwarf_test
+    def test_delayed_crash_with_breakpoint_signal_dwarf(self):
+        """ Test a thread with a delayed crash while other threads generate a signal and hit a breakpoint. """
+        self.buildDwarf(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.thread_breakpoint = line_number('main.cpp', '// Set breakpoint here')
+        self.setup_breakpoint = line_number('main.cpp', '// Break here and adjust num')
+
+    def print_threads(self, threads):
+        ret = ""
+        for x in threads:
+            ret += "\t thread %d stopped due to reason %s" % (x.GetIndexID(), lldbutil.stop_reason_to_str(x.GetStopReason()))
+        return ret
+
+    def debug_threads(self, bps, crashed, exiting, wps, signals, others):
+        print "%d threads stopped at bp:\n%s" % (len(bps), self.print_threads(bps))
+        print "%d threads crashed:\n%s" % (len(crashed), self.print_threads(crashed))
+        print "%d threads stopped due to watchpoint:\n%s" % (len(wps), self.print_threads(wps))
+        print "%d threads stopped at signal:\n%s" % (len(signals), self.print_threads(signals))
+        print "%d threads exiting:\n%s" % (len(exiting), self.print_threads(exiting))
+        print "%d threads stopped due to other/unknown reason:\n%s" % (len(others), self.print_threads(others))
+
+    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)
+
+        # Initialize all the breakpoints (main thread/aux thread)
+        lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.setup_breakpoint,
+            num_expected_locations=1)
+        lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.thread_breakpoint,
+            num_expected_locations=1)
+
+        # The breakpoint list should show 2 breakpoints with 1 location.
+        self.expect("breakpoint list -f", "Breakpoint location shown correctly",
+            substrs = ["1: file = 'main.cpp', line = %d, locations = 1" % self.setup_breakpoint,
+                       "2: file = 'main.cpp', line = %d, locations = 1" % self.thread_breakpoint])
+
+        # 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 watchpoint on the global variable (g_watchme)
+        if num_watchpoint_threads + num_delay_watchpoint_threads > 0:
+            self.runCmd("watchpoint set variable g_watchme")
+
+        # Get the target process
+        target = self.dbg.GetSelectedTarget()
+        process = 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(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)
+
+        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 = 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,
+            'Number of expected threads and actual threads do not match.')
+
+        # Get the thread objects
+        (breakpoint_threads, crashed_threads, exiting_threads, other_threads, signal_threads, watchpoint_threads) = ([], [], [], [], [], [])
+        lldbutil.sort_stopped_threads(process,
+                                      breakpoint_threads=breakpoint_threads,
+                                      crashed_threads=crashed_threads,
+                                      exiting_threads=exiting_threads,
+                                      signal_threads=signal_threads,
+                                      watchpoint_threads=watchpoint_threads,
+                                      other_threads=other_threads)
+
+        if self.TraceOn():
+            self.debug_threads(breakpoint_threads, crashed_threads, exiting_threads, watchpoint_threads, signal_threads, other_threads)
+
+        # The threads that are doing signal handling must be unblocked or the inferior will hang. We keep
+        # a counter of threads that stop due to a signal so we have something to verify later on.
+        seen_signal_threads = len(signal_threads)
+        seen_breakpoint_threads = len(breakpoint_threads)
+        seen_watchpoint_threads = len(watchpoint_threads)
+        seen_crashed_threads = len(crashed_threads)
+
+        # Run to completion
+        while len(crashed_threads) == 0 and process.GetState() != lldb.eStateExited:
+            if self.TraceOn():
+                self.runCmd("thread backtrace all")
+                self.debug_threads(breakpoint_threads, crashed_threads, exiting_threads, watchpoint_threads, signal_threads, other_threads)
+
+            self.runCmd("continue")
+            lldbutil.sort_stopped_threads(process,
+                                          breakpoint_threads=breakpoint_threads,
+                                          crashed_threads=crashed_threads,
+                                          exiting_threads=exiting_threads,
+                                          signal_threads=signal_threads,
+                                          watchpoint_threads=watchpoint_threads,
+                                          other_threads=other_threads)
+            seen_signal_threads += len(signal_threads)
+            seen_breakpoint_threads += len(breakpoint_threads)
+            seen_watchpoint_threads += len(watchpoint_threads)
+            seen_crashed_threads += len(crashed_threads)
+
+        if num_crash_threads > 0 or num_delay_crash_threads > 0:
+            # Expecting a crash
+            self.assertTrue(seen_crashed_threads > 0, "Expecting at least one thread to crash")
+
+            # Ensure the zombie process is reaped
+            self.runCmd("process kill")
+
+        elif num_crash_threads == 0 and num_delay_crash_threads == 0:
+            # The inferior process should have exited without crashing
+            self.assertEqual(0, seen_crashed_threads, "Unexpected thread(s) in crashed state")
+            self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED)
+
+            # Verify the number of actions took place matches expected numbers
+            self.assertEqual(num_delay_breakpoint_threads + num_breakpoint_threads, seen_breakpoint_threads)
+            self.assertEqual(num_delay_signal_threads + num_signal_threads, seen_signal_threads)
+            self.assertEqual(num_delay_watchpoint_threads + num_watchpoint_threads, seen_watchpoint_threads)
+
+
+if __name__ == '__main__':
+    import atexit
+    lldb.SBDebugger.Initialize()
+    atexit.register(lambda: lldb.SBDebugger.Terminate())
+    unittest2.main()

Added: lldb/trunk/test/functionalities/thread/concurrent_events/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/thread/concurrent_events/main.cpp?rev=185889&view=auto
==============================================================================
--- lldb/trunk/test/functionalities/thread/concurrent_events/main.cpp (added)
+++ lldb/trunk/test/functionalities/thread/concurrent_events/main.cpp Mon Jul  8 19:08:01 2013
@@ -0,0 +1,199 @@
+//===-- 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 a watchpoint will be hit
+// while a breakpoint is being handled in another thread.  The expected result is
+// that the watchpoint in the second thread will be hit while the breakpoint handler
+// in the first thread is trying to stop all threads.
+
+#include <atomic>
+#include <vector>
+using namespace std;
+
+#include <pthread.h>
+#include <semaphore.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;
+
+//sem_t g_signal_semaphore;
+
+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 both 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 both threads are running
+    pseudo_barrier_wait(g_barrier);
+    do_action_args(input);
+
+    // Generate a user-defined signal to current process
+    kill(getpid(), SIGUSR1);
+
+    // wait for notification the signal handler was executed
+    //sem_wait(&g_signal_semaphore);
+
+    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) {
+    //sem_post(&g_signal_semaphore);
+    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);
+
+    //sem_init(&g_signal_semaphore, 0, 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 main ()
+{
+    g_watchme = 0;
+
+    unsigned num_breakpoint_threads = 1;
+    unsigned num_watchpoint_threads = 0;
+    unsigned num_signal_threads = 0;
+    unsigned num_crash_threads = 1;
+
+    unsigned num_delay_breakpoint_threads = 0;
+    unsigned num_delay_watchpoint_threads = 0;
+    unsigned num_delay_signal_threads = 0;
+    unsigned num_delay_crash_threads = 0;
+
+    // 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);
+
+    thread_vector 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;
+    actions.push_back(std::make_pair(num_delay_breakpoint_threads, breakpoint_func));
+    actions.push_back(std::make_pair(num_delay_watchpoint_threads, watchpoint_func));
+    actions.push_back(std::make_pair(num_delay_signal_threads, signal_func));
+    actions.push_back(std::make_pair(num_delay_crash_threads, crash_func));
+
+    register_signal_handler(SIGUSR1, sigusr1_handler);
+
+    // Create threads that handle instant actions
+    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);
+
+    // Break here and verify one thread is active.
+    return 0;
+}

Modified: lldb/trunk/test/functionalities/watchpoint/multiple_threads/TestWatchpointMultipleThreads.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/watchpoint/multiple_threads/TestWatchpointMultipleThreads.py?rev=185889&r1=185888&r2=185889&view=diff
==============================================================================
--- lldb/trunk/test/functionalities/watchpoint/multiple_threads/TestWatchpointMultipleThreads.py (original)
+++ lldb/trunk/test/functionalities/watchpoint/multiple_threads/TestWatchpointMultipleThreads.py Mon Jul  8 19:08:01 2013
@@ -65,6 +65,7 @@ class WatchpointForMultipleThreadsTestCa
         # Add a breakpoint to set a watchpoint when stopped on the breakpoint.
         lldbutil.run_break_set_by_file_and_line (self, None, self.first_stop, num_expected_locations=1)
 
+        # llvm.org/pr16566: LLDB requires a breakpoint to be hit before watchpoints are respected on a thread created after the watchpoing is set.
         # Set this breakpoint to allow newly created thread to inherit the global watchpoint state.
         lldbutil.run_break_set_by_file_and_line (self, None, self.thread_function, num_expected_locations=1)
 

Modified: lldb/trunk/test/lldbtest.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lldbtest.py?rev=185889&r1=185888&r2=185889&view=diff
==============================================================================
--- lldb/trunk/test/lldbtest.py (original)
+++ lldb/trunk/test/lldbtest.py Mon Jul  8 19:08:01 2013
@@ -618,6 +618,22 @@ def skipIfLinux(func):
             func(*args, **kwargs)
     return wrapper
 
+def skipIfDarwin(func):
+    """Decorate the item to skip tests that should be skipped on Darwin."""
+    if isinstance(func, type) and issubclass(func, unittest2.TestCase):
+        raise Exception("@skipIfLinux can only be used to decorate a test method")
+    @wraps(func)
+    def wrapper(*args, **kwargs):
+        from unittest2 import case
+        self = args[0]
+        platform = sys.platform
+        if "darwin" in platform:
+            self.skipTest("skip on darwin")
+        else:
+            func(*args, **kwargs)
+    return wrapper
+
+
 def skipIfLinuxClang(func):
     """Decorate the item to skip tests that should be skipped if building on 
        Linux with clang.

Modified: lldb/trunk/test/lldbutil.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lldbutil.py?rev=185889&r1=185888&r2=185889&view=diff
==============================================================================
--- lldb/trunk/test/lldbutil.py (original)
+++ lldb/trunk/test/lldbutil.py Mon Jul  8 19:08:01 2013
@@ -181,6 +181,8 @@ def stop_reason_to_str(enum):
         return "exception"
     elif enum == lldb.eStopReasonPlanComplete:
         return "plancomplete"
+    elif enum == lldb.eStopReasonThreadExiting:
+        return "threadexiting"
     else:
         raise Exception("Unknown StopReason enum")
 
@@ -258,6 +260,41 @@ def value_type_to_str(enum):
 
 
 # ==================================================
+# Get stopped threads due to each stop reason.
+# ==================================================
+
+def sort_stopped_threads(process,
+                         breakpoint_threads = None,
+                         crashed_threads = None,
+                         watchpoint_threads = None,
+                         signal_threads = None,
+                         exiting_threads = None,
+                         other_threads = None):
+    """ Fills array *_threads with threads stopped for the corresponding stop
+        reason.
+    """
+    for lst in [breakpoint_threads,
+                watchpoint_threads,
+                signal_threads,
+                exiting_threads,
+                other_threads]:
+        if lst is not None:
+            lst[:] = []
+
+    for thread in process:
+        dispatched = False
+        for (reason, list) in [(lldb.eStopReasonBreakpoint, breakpoint_threads),
+                               (lldb.eStopReasonException, crashed_threads),
+                               (lldb.eStopReasonWatchpoint, watchpoint_threads),
+                               (lldb.eStopReasonSignal, signal_threads),
+                               (lldb.eStopReasonThreadExiting, exiting_threads),
+                               (None, other_threads)]:
+            if not dispatched and list is not None:
+                if thread.GetStopReason() == reason or reason is None:
+                    list.append(thread)
+                    dispatched = True
+
+# ==================================================
 # Utility functions for setting breakpoints
 # ==================================================
 





More information about the lldb-commits mailing list