Skip to content
Permalink
Browse files

[lldb] [Process/NetBSD] Improve threading support

Implement major improvements to multithreaded program support.  Notably,
support tracking new and exited threads, associate signals and events
with correct threads and support controlling individual threads when
resuming.

Firstly, use PT_SET_EVENT_MASK to enable reporting of created and exited
threads via SIGTRAP.  Handle TRAP_LWP events to keep track
of the currently running threads.

Secondly, update the signal (both generic and SIGTRAP) handling code
to account for per-thread signals correctly.  Signals delivered
to the whole process are reported on all threads, while per-thread
signals and events are reported only to the specific thread.
The remaining threads are marked as 'stopped with no reason'.  Note that
NetBSD always stops all threads on debugger events.

Thirdly, implement the ability to set every thread as running, stopped
or single-stepping separately while continuing the process.  This also
provides the ability to send a signal to the whole process or to one
of its thread while resuming.

Differential Revision: https://reviews.llvm.org/D70022
  • Loading branch information
mgorny committed Jul 12, 2019
1 parent d018b55 commit 8d9400b65b972cb50fe2266360443192ea107ec9
Showing with 405 additions and 110 deletions.
  1. +0 −1 lldb/packages/Python/lldbsuite/test/commands/watchpoints/hello_watchlocation/TestWatchLocation.py
  2. +0 −1 ...ython/lldbsuite/test/commands/watchpoints/watchpoint_set_command/TestWatchLocationWithWatchSet.py
  3. +0 −1 ...est/functionalities/thread/concurrent_events/TestConcurrentBreakpointOneDelayBreakpointThreads.py
  4. +0 −1 ...es/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentCrashWithBreak.py
  5. +0 −1 ...s/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentCrashWithSignal.py
  6. +0 −1 ...thon/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentCrashWithWatchpoint.py
  7. +0 −1 ...est/functionalities/thread/concurrent_events/TestConcurrentCrashWithWatchpointBreakpointSignal.py
  8. +0 −1 .../Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentDelaySignalBreak.py
  9. +0 −1 ...e/test/functionalities/thread/concurrent_events/TestConcurrentDelayedCrashWithBreakpointSignal.py
  10. +0 −1 ...st/functionalities/thread/concurrent_events/TestConcurrentDelayedCrashWithBreakpointWatchpoint.py
  11. +0 −1 ...s/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentManyBreakpoints.py
  12. +0 −1 ...ackages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentManyCrash.py
  13. +0 −1 ...kages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentSignalBreak.py
  14. +0 −1 ...hon/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentTwoBreakpointThreads.py
  15. +0 −1 lldb/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/TestExitDuringBreak.py
  16. +0 −3 lldb/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/TestExitDuringStep.py
  17. +0 −1 lldb/packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py
  18. +0 −1 lldb/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/TestThreadExit.py
  19. +0 −2 ...ython/lldbsuite/test/functionalities/thread/thread_specific_break/TestThreadSpecificBreakpoint.py
  20. +0 −1 lldb/packages/Python/lldbsuite/test/python_api/lldbutil/iter/TestLLDBIterator.py
  21. +0 −1 lldb/packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py
  22. +149 −0 lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_vContThreads.py
  23. +206 −86 lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
  24. +1 −0 lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
  25. +44 −0 lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp
  26. +5 −0 lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h
@@ -36,7 +36,6 @@ def setUp(self):
@expectedFailureAll(triple=re.compile('^mips'))
# SystemZ and PowerPC also currently supports only one H/W watchpoint
@expectedFailureAll(archs=['powerpc64le', 's390x'])
@expectedFailureNetBSD
@skipIfDarwin
def test_hello_watchlocation(self):
"""Test watching a location with '-s size' option."""
@@ -34,7 +34,6 @@ def setUp(self):
'aarch64',
'arm'],
bugnumber="llvm.org/pr26031")
@expectedFailureNetBSD
def test_watchlocation_using_watchpoint_set(self):
"""Test watching a location with 'watchpoint set expression -w write -s size' option."""
self.build()
@@ -15,7 +15,6 @@ class ConcurrentBreakpointOneDelayBreakpointThreads(ConcurrentEventsBase):
@skipIfFreeBSD # timing out on buildbot
# Atomic sequences are not supported yet for MIPS in LLDB.
@skipIf(triple='^mips')
@expectedFailureNetBSD
def test(self):
"""Test threads that trigger a breakpoint where one thread has a 1 second delay. """
self.build(dictionary=self.getBuildFlags())
@@ -15,7 +15,6 @@ class ConcurrentCrashWithBreak(ConcurrentEventsBase):
@skipIfFreeBSD # timing out on buildbot
# Atomic sequences are not supported yet for MIPS in LLDB.
@skipIf(triple='^mips')
@expectedFailureNetBSD
def test(self):
""" Test a thread that crashes while another thread hits a breakpoint."""
self.build(dictionary=self.getBuildFlags())
@@ -15,7 +15,6 @@ class ConcurrentCrashWithSignal(ConcurrentEventsBase):
@skipIfFreeBSD # timing out on buildbot
# Atomic sequences are not supported yet for MIPS in LLDB.
@skipIf(triple='^mips')
@expectedFailureNetBSD
def test(self):
""" Test a thread that crashes while another thread generates a signal."""
self.build(dictionary=self.getBuildFlags())
@@ -15,7 +15,6 @@ class ConcurrentCrashWithWatchpoint(ConcurrentEventsBase):
@skipIfFreeBSD # timing out on buildbot
# Atomic sequences are not supported yet for MIPS in LLDB.
@skipIf(triple='^mips')
@expectedFailureNetBSD
@add_test_categories(["watchpoint"])
def test(self):
""" Test a thread that crashes while another thread hits a watchpoint."""
@@ -15,7 +15,6 @@ class ConcurrentCrashWithWatchpointBreakpointSignal(ConcurrentEventsBase):
@skipIfFreeBSD # timing out on buildbot
# Atomic sequences are not supported yet for MIPS in LLDB.
@skipIf(triple='^mips')
@expectedFailureNetBSD
@add_test_categories(["watchpoint"])
def test(self):
""" Test a thread that crashes while other threads generate a signal and hit a watchpoint and breakpoint. """
@@ -15,7 +15,6 @@ class ConcurrentDelaySignalBreak(ConcurrentEventsBase):
@skipIfFreeBSD # timing out on buildbot
# Atomic sequences are not supported yet for MIPS in LLDB.
@skipIf(triple='^mips')
@expectedFailureNetBSD
def test(self):
"""Test (1-second delay) signal and a breakpoint in multiple threads."""
self.build(dictionary=self.getBuildFlags())
@@ -15,7 +15,6 @@ class ConcurrentDelayedCrashWithBreakpointSignal(ConcurrentEventsBase):
@skipIfFreeBSD # timing out on buildbot
# Atomic sequences are not supported yet for MIPS in LLDB.
@skipIf(triple='^mips')
@expectedFailureNetBSD
def test(self):
""" Test a thread with a delayed crash while other threads generate a signal and hit a breakpoint. """
self.build(dictionary=self.getBuildFlags())
@@ -15,7 +15,6 @@ class ConcurrentDelayedCrashWithBreakpointWatchpoint(ConcurrentEventsBase):
@skipIfFreeBSD # timing out on buildbot
# Atomic sequences are not supported yet for MIPS in LLDB.
@skipIf(triple='^mips')
@expectedFailureNetBSD
@add_test_categories(["watchpoint"])
def test(self):
""" Test a thread with a delayed crash while other threads hit a watchpoint and a breakpoint. """
@@ -14,7 +14,6 @@ class ConcurrentManyBreakpoints(ConcurrentEventsBase):

# Atomic sequences are not supported yet for MIPS in LLDB.
@skipIf(triple='^mips')
@expectedFailureNetBSD
@skipIfOutOfTreeDebugserver
def test(self):
"""Test 100 breakpoints from 100 threads."""
@@ -14,7 +14,6 @@ class ConcurrentManyCrash(ConcurrentEventsBase):

# Atomic sequences are not supported yet for MIPS in LLDB.
@skipIf(triple='^mips')
@expectedFailureNetBSD
@skipIfOutOfTreeDebugserver
def test(self):
"""Test 100 threads that cause a segfault."""
@@ -15,7 +15,6 @@ class ConcurrentSignalBreak(ConcurrentEventsBase):
@skipIfFreeBSD # timing out on buildbot
# Atomic sequences are not supported yet for MIPS in LLDB.
@skipIf(triple='^mips')
@expectedFailureNetBSD
def test(self):
"""Test signal and a breakpoint in multiple threads."""
self.build(dictionary=self.getBuildFlags())
@@ -15,7 +15,6 @@ class ConcurrentTwoBreakpointThreads(ConcurrentEventsBase):
@skipIfFreeBSD # timing out on buildbot
# Atomic sequences are not supported yet for MIPS in LLDB.
@skipIf(triple='^mips')
@expectedFailureNetBSD
def test(self):
"""Test two threads that trigger a breakpoint. """
self.build(dictionary=self.getBuildFlags())
@@ -21,7 +21,6 @@ def setUp(self):
# Find the line number for our breakpoint.
self.breakpoint = line_number('main.cpp', '// Set breakpoint here')

@expectedFailureNetBSD
def test(self):
"""Test thread exit during breakpoint handling."""
self.build(dictionary=self.getBuildFlags())
@@ -17,7 +17,6 @@ class ExitDuringStepTestCase(TestBase):

@skipIfFreeBSD # llvm.org/pr21411: test is hanging
@skipIfWindows # This is flakey on Windows: llvm.org/pr38373
@expectedFailureNetBSD
def test(self):
"""Test thread exit during step handling."""
self.build(dictionary=self.getBuildFlags())
@@ -28,7 +27,6 @@ def test(self):

@skipIfFreeBSD # llvm.org/pr21411: test is hanging
@skipIfWindows # This is flakey on Windows: llvm.org/pr38373
@expectedFailureNetBSD
def test_step_over(self):
"""Test thread exit during step-over handling."""
self.build(dictionary=self.getBuildFlags())
@@ -39,7 +37,6 @@ def test_step_over(self):

@skipIfFreeBSD # llvm.org/pr21411: test is hanging
@skipIfWindows # This is flakey on Windows: llvm.org/pr38373
@expectedFailureNetBSD
def test_step_in(self):
"""Test thread exit during step-in handling."""
self.build(dictionary=self.getBuildFlags())
@@ -23,7 +23,6 @@ def setUp(self):
self.thread3_notify_all_line = line_number('main.cpp', '// Set thread3 break point on notify_all at this line.')
self.thread3_before_lock_line = line_number('main.cpp', '// thread3-before-lock')

@expectedFailureNetBSD
def test_number_of_threads(self):
"""Test number of threads."""
self.build()
@@ -25,7 +25,6 @@ def setUp(self):
self.break_4 = line_number('main.cpp', '// Set fourth breakpoint here')

@skipIfWindows # This is flakey on Windows: llvm.org/pr38373
@expectedFailureNetBSD
def test(self):
"""Test thread exit handling."""
self.build(dictionary=self.getBuildFlags())
@@ -25,13 +25,11 @@ class ThreadSpecificBreakTestCase(TestBase):
@add_test_categories(['pyapi'])

@expectedFailureAll(oslist=['ios', 'watchos', 'tvos', 'bridgeos'], archs=['armv7', 'armv7k'], bugnumber='rdar://problem/34563920') # armv7 ios problem - breakpoint with tid qualifier isn't working
@expectedFailureNetBSD
def test_thread_id(self):
self.do_test(set_thread_id)

@skipUnlessDarwin
@expectedFailureAll(oslist=['ios', 'watchos', 'tvos', 'bridgeos'], archs=['armv7', 'armv7k'], bugnumber='rdar://problem/34563920') # armv7 ios problem - breakpoint with tid qualifier isn't working
@expectedFailureNetBSD
def test_thread_name(self):
self.do_test(set_thread_name)

@@ -91,7 +91,6 @@ def test_lldb_iter_breakpoint(self):
self.assertTrue(yours[i] == mine[i],
"ID of yours[{0}] and mine[{0}] matches".format(i))

@expectedFailureNetBSD
@add_test_categories(['pyapi'])
def test_lldb_iter_frame(self):
"""Test iterator works correctly for SBProcess->SBThread->SBFrame."""
@@ -27,7 +27,6 @@ def setUp(self):
self.violating_func = "do_bad_thing_with_location"

@add_test_categories(['pyapi'])
@expectedFailureNetBSD
def test_watch_address(self):
"""Exercise SBTarget.WatchAddress() API to set a watchpoint."""
self.build()
@@ -0,0 +1,149 @@
from __future__ import print_function

import json
import re

import gdbremote_testcase
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil

class TestGdbRemote_vContThreads(gdbremote_testcase.GdbRemoteTestCaseBase):
mydir = TestBase.compute_mydir(__file__)

def start_threads(self, num):
procs = self.prep_debug_monitor_and_inferior(
inferior_args=['thread:new'] * num + ['@started'])
# start the process and wait for output
self.test_sequence.add_log_lines([
"read packet: $c#63",
{"type": "output_match", "regex": self.maybe_strict_output_regex(
r"@started\r\n")},
], True)
# then interrupt it
self.add_interrupt_packets()
self.add_threadinfo_collection_packets()

context = self.expect_gdbremote_sequence()
self.assertIsNotNone(context)
threads = self.parse_threadinfo_packets(context)
self.assertIsNotNone(threads)
self.assertEqual(len(threads), num + 1)

self.reset_test_sequence()
return threads

def signal_one_thread(self):
threads = self.start_threads(1)
# try sending a signal to one of the two threads
self.test_sequence.add_log_lines([
"read packet: $vCont;C{0:x}:{1:x};c#00".format(
lldbutil.get_signal_number('SIGUSR1'), threads[0]),
{"direction": "send", "regex": r"^\$W00#b7$"},
], True)

context = self.expect_gdbremote_sequence()
self.assertIsNotNone(context)

@skipUnlessPlatform(["netbsd"])
@debugserver_test
def test_signal_one_thread_debugserver(self):
self.init_debugserver_test()
self.build()
self.set_inferior_startup_launch()
self.signal_one_thread()

@skipUnlessPlatform(["netbsd"])
@llgs_test
def test_signal_one_thread_llgs(self):
self.init_llgs_test()
self.build()
self.set_inferior_startup_launch()
self.signal_one_thread()

def signal_all_threads(self):
threads = self.start_threads(1)
# try sending a signal to two threads (= the process)
self.test_sequence.add_log_lines([
"read packet: $vCont;C{0:x}:{1:x};C{0:x}:{2:x}#00".format(
lldbutil.get_signal_number('SIGUSR1'),
threads[0], threads[1]),
{"direction": "send", "regex": r"^\$W00#b7$"},
], True)

context = self.expect_gdbremote_sequence()
self.assertIsNotNone(context)

@skipUnlessPlatform(["netbsd"])
@debugserver_test
def test_signal_all_threads_debugserver(self):
self.init_debugserver_test()
self.build()
self.set_inferior_startup_launch()
self.signal_all_threads()

@skipUnlessPlatform(["netbsd"])
@llgs_test
def test_signal_all_threads_llgs(self):
self.init_llgs_test()
self.build()
self.set_inferior_startup_launch()
self.signal_all_threads()

def signal_two_of_three_threads(self):
threads = self.start_threads(2)
# try sending a signal to 2 out of 3 threads
self.test_sequence.add_log_lines([
"read packet: $vCont;C{0:x}:{1:x};C{0:x}:{2:x};c#00".format(
lldbutil.get_signal_number('SIGUSR1'),
threads[1], threads[2]),
{"direction": "send", "regex": r"^\$E1e#db$"},
], True)

context = self.expect_gdbremote_sequence()
self.assertIsNotNone(context)

@skipUnlessPlatform(["netbsd"])
@debugserver_test
def test_signal_two_of_three_threads_debugserver(self):
self.init_debugserver_test()
self.build()
self.set_inferior_startup_launch()
self.signal_two_of_three_threads()

@skipUnlessPlatform(["netbsd"])
@llgs_test
def test_signal_two_of_three_threads_llgs(self):
self.init_llgs_test()
self.build()
self.set_inferior_startup_launch()
self.signal_two_of_three_threads()

def signal_two_signals(self):
threads = self.start_threads(1)
# try sending two different signals to two threads
self.test_sequence.add_log_lines([
"read packet: $vCont;C{0:x}:{1:x};C{2:x}:{3:x}#00".format(
lldbutil.get_signal_number('SIGUSR1'), threads[0],
lldbutil.get_signal_number('SIGUSR2'), threads[1]),
{"direction": "send", "regex": r"^\$E1e#db$"},
], True)

context = self.expect_gdbremote_sequence()
self.assertIsNotNone(context)

@skipUnlessPlatform(["netbsd"])
@debugserver_test
def test_signal_two_signals_debugserver(self):
self.init_debugserver_test()
self.build()
self.set_inferior_startup_launch()
self.signal_two_signals()

@skipUnlessPlatform(["netbsd"])
@llgs_test
def test_signal_two_signals_llgs(self):
self.init_llgs_test()
self.build()
self.set_inferior_startup_launch()
self.signal_two_signals()

0 comments on commit 8d9400b

Please sign in to comment.
You can’t perform that action at this time.