forked from OSchip/llvm-project
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. llvm-svn: 185889
This commit is contained in:
parent
07b0e451ca
commit
b3d41a278b
|
@ -0,0 +1,8 @@
|
|||
LEVEL = ../../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
CFLAGS_EXTRAS += -std=c++11 -lpthread
|
||||
LD_EXTRAS += -lpthread
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
|
@ -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()
|
|
@ -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;
|
||||
}
|
|
@ -65,6 +65,7 @@ class WatchpointForMultipleThreadsTestCase(TestBase):
|
|||
# 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)
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
@ -257,6 +259,41 @@ def value_type_to_str(enum):
|
|||
raise Exception("Unknown ValueType 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
|
||||
# ==================================================
|
||||
|
|
Loading…
Reference in New Issue