From d970d4d4aa7345ebf8b7169b09f2775a93f86c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Sat, 9 Nov 2019 11:56:08 +0100 Subject: [PATCH] [lldb] [Process/NetBSD] Copy watchpoints to newly-created threads NetBSD ptrace interface does not populate watchpoints to newly-created threads. Solve this via copying the watchpoints from the current thread when new thread is reported via TRAP_LWP. Add a test that verifies that when the user does not have permissions to set watchpoints on NetBSD, the 'watchpoint set' errors out gracefully and thread monitoring does not crash on being unable to copy watchpoints to new threads. Differential Revision: https://reviews.llvm.org/D70023 --- .../TestWatchpointMultipleThreads.py | 2 -- ...eakpointsDelayedBreakpointOneWatchpoint.py | 1 - .../TestConcurrentDelaySignalWatch.py | 1 - .../TestConcurrentDelayWatchBreak.py | 1 - .../TestConcurrentSignalWatch.py | 1 - ...stConcurrentTwoBreakpointsOneWatchpoint.py | 1 - .../TestConcurrentWatchBreak.py | 1 - .../TestConcurrentWatchBreakDelay.py | 1 - ...tWatchpointDelayWatchpointOneBreakpoint.py | 1 - ...entWatchpointWithDelayWatchpointThreads.py | 1 - .../Process/NetBSD/NativeProcessNetBSD.cpp | 15 +++++++++--- .../NetBSD/NativeRegisterContextNetBSD.cpp | 2 ++ .../NetBSD/NativeRegisterContextNetBSD.h | 5 +++- .../NativeRegisterContextNetBSD_x86_64.cpp | 15 ++++++++++++ .../NativeRegisterContextNetBSD_x86_64.h | 3 +++ .../Process/NetBSD/NativeThreadNetBSD.cpp | 14 +++++++++-- .../Process/NetBSD/NativeThreadNetBSD.h | 8 +++++-- .../Shell/Watchpoint/Inputs/thread-dbreg.c | 23 +++++++++++++++++++ .../Shell/Watchpoint/netbsd-nouserdbregs.test | 22 ++++++++++++++++++ lldb/test/Shell/lit.cfg.py | 15 ++++++++++++ 20 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 lldb/test/Shell/Watchpoint/Inputs/thread-dbreg.c create mode 100644 lldb/test/Shell/Watchpoint/netbsd-nouserdbregs.test diff --git a/lldb/packages/Python/lldbsuite/test/commands/watchpoints/multiple_threads/TestWatchpointMultipleThreads.py b/lldb/packages/Python/lldbsuite/test/commands/watchpoints/multiple_threads/TestWatchpointMultipleThreads.py index e02cce661297..3e6329eb91fb 100644 --- a/lldb/packages/Python/lldbsuite/test/commands/watchpoints/multiple_threads/TestWatchpointMultipleThreads.py +++ b/lldb/packages/Python/lldbsuite/test/commands/watchpoints/multiple_threads/TestWatchpointMultipleThreads.py @@ -18,12 +18,10 @@ class WatchpointForMultipleThreadsTestCase(TestBase): NO_DEBUG_INFO_TESTCASE = True main_spec = lldb.SBFileSpec("main.cpp", False) - @expectedFailureNetBSD def test_watchpoint_before_thread_start(self): """Test that we can hit a watchpoint we set before starting another thread""" self.do_watchpoint_test("Before running the thread") - @expectedFailureNetBSD def test_watchpoint_after_thread_start(self): """Test that we can hit a watchpoint we set after starting another thread""" self.do_watchpoint_test("After running the thread") diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentBreakpointsDelayedBreakpointOneWatchpoint.py b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentBreakpointsDelayedBreakpointOneWatchpoint.py index 1d85dd046c37..275d54d2149c 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentBreakpointsDelayedBreakpointOneWatchpoint.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentBreakpointsDelayedBreakpointOneWatchpoint.py @@ -16,7 +16,6 @@ class ConcurrentBreakpointsDelayedBreakpointOneWatchpoint( @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 breakpoint, a delayed breakpoint, and one watchpoint thread. """ diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentDelaySignalWatch.py b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentDelaySignalWatch.py index 84565bf98bc2..28c5c68d4506 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentDelaySignalWatch.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentDelaySignalWatch.py @@ -15,7 +15,6 @@ class ConcurrentDelaySignalWatch(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 watchpoint and a (1 second delay) signal in multiple threads.""" diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentDelayWatchBreak.py b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentDelayWatchBreak.py index cd67116f3181..2d7c984e0e1c 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentDelayWatchBreak.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentDelayWatchBreak.py @@ -15,7 +15,6 @@ class ConcurrentDelayWatchBreak(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 (1-second delay) watchpoint and a breakpoint in multiple threads.""" diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentSignalWatch.py b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentSignalWatch.py index 508084cac0c5..0fbaf364045d 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentSignalWatch.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentSignalWatch.py @@ -15,7 +15,6 @@ class ConcurrentSignalWatch(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 watchpoint and a signal in multiple threads.""" diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentTwoBreakpointsOneWatchpoint.py b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentTwoBreakpointsOneWatchpoint.py index 762b23c89a8d..687be17ddc5a 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentTwoBreakpointsOneWatchpoint.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentTwoBreakpointsOneWatchpoint.py @@ -15,7 +15,6 @@ class ConcurrentTwoBreakpointsOneWatchpoint(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 two threads that trigger a breakpoint and one watchpoint thread. """ diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchBreak.py b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchBreak.py index cf86df9b324b..241ea5b64a03 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchBreak.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchBreak.py @@ -15,7 +15,6 @@ class ConcurrentWatchBreak(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 watchpoint and a breakpoint in multiple threads.""" diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchBreakDelay.py b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchBreakDelay.py index 1cef5936ad1f..79a54b620e53 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchBreakDelay.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchBreakDelay.py @@ -15,7 +15,6 @@ class ConcurrentWatchBreakDelay(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 watchpoint and a (1 second delay) breakpoint in multiple threads.""" diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchpointDelayWatchpointOneBreakpoint.py b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchpointDelayWatchpointOneBreakpoint.py index adbd2b036d8c..6a37abdbcbb8 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchpointDelayWatchpointOneBreakpoint.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchpointDelayWatchpointOneBreakpoint.py @@ -15,7 +15,6 @@ class ConcurrentWatchpointDelayWatchpointOneBreakpoint(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 two threads that trigger a watchpoint (one with a 1 second delay) and one breakpoint thread. """ diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchpointWithDelayWatchpointThreads.py b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchpointWithDelayWatchpointThreads.py index 357982271281..67ac92b853cd 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchpointWithDelayWatchpointThreads.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchpointWithDelayWatchpointThreads.py @@ -15,7 +15,6 @@ class ConcurrentWatchpointWithDelayWatchpointThreads(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 two threads that trigger a watchpoint where one thread has a 1 second delay. """ diff --git a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp index 0b21c7862b05..372ac059cb5d 100644 --- a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp +++ b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp @@ -272,12 +272,21 @@ void NativeProcessNetBSD::MonitorSIGTRAP(lldb::pid_t pid) { } switch (pst.pe_report_event) { - case PTRACE_LWP_CREATE: + case PTRACE_LWP_CREATE: { LLDB_LOG(log, "monitoring new thread, pid = {0}, LWP = {1}", pid, pst.pe_lwp); - AddThread(pst.pe_lwp); - break; + NativeThreadNetBSD& t = AddThread(pst.pe_lwp); + error = t.CopyWatchpointsFrom( + static_cast(*GetCurrentThread())); + if (error.Fail()) { + LLDB_LOG(log, + "failed to copy watchpoints to new thread {0}: {1}", + pst.pe_lwp, error); + SetState(StateType::eStateInvalid); + return; + } + } break; case PTRACE_LWP_EXIT: LLDB_LOG(log, "removing exited thread, pid = {0}, LWP = {1}", pid, diff --git a/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp b/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp index 3a9caaad74c8..a8afa0b20305 100644 --- a/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp +++ b/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp @@ -8,6 +8,8 @@ #include "NativeRegisterContextNetBSD.h" +#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h" + #include "lldb/Host/common/NativeProcessProtocol.h" using namespace lldb_private; diff --git a/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h b/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h index f5dd0c33b677..2947454af2d8 100644 --- a/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h +++ b/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h @@ -11,12 +11,13 @@ #include "lldb/Host/common/NativeThreadProtocol.h" -#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h" #include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" namespace lldb_private { namespace process_netbsd { +class NativeProcessNetBSD; + class NativeRegisterContextNetBSD : public NativeRegisterContextRegisterInfo { public: NativeRegisterContextNetBSD(NativeThreadProtocol &native_thread, @@ -30,6 +31,8 @@ public: static NativeRegisterContextNetBSD * CreateHostNativeRegisterContextNetBSD(const ArchSpec &target_arch, NativeThreadProtocol &native_thread); + virtual Status + CopyHardwareWatchpointsFrom(NativeRegisterContextNetBSD &source) = 0; protected: Status DoRegisterSet(int req, void *buf); diff --git a/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp b/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp index 35ebbedf26a8..afb62b8170ef 100644 --- a/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp +++ b/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp @@ -988,4 +988,19 @@ uint32_t NativeRegisterContextNetBSD_x86_64::NumSupportedHardwareWatchpoints() { return 4; } +Status NativeRegisterContextNetBSD_x86_64::CopyHardwareWatchpointsFrom( + NativeRegisterContextNetBSD &source) { + auto &r_source = static_cast(source); + Status res = r_source.ReadRegisterSet(DBRegSet); + if (!res.Fail()) { + // copy dbregs only if any watchpoints were set + if ((r_source.m_dbr_x86_64.dr[7] & 0xFF) == 0) + return res; + + m_dbr_x86_64 = r_source.m_dbr_x86_64; + res = WriteRegisterSet(DBRegSet); + } + return res; +} + #endif // defined(__x86_64__) diff --git a/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h b/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h index 4ef8989ae938..0da0351a7b0c 100644 --- a/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h +++ b/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h @@ -71,6 +71,9 @@ public: uint32_t NumSupportedHardwareWatchpoints() override; + Status + CopyHardwareWatchpointsFrom(NativeRegisterContextNetBSD &source) override; + private: // Private member types. enum { GPRegSet, FPRegSet, XStateRegSet, DBRegSet }; diff --git a/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp b/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp index c3251c88a420..3dd14f0e1f68 100644 --- a/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp +++ b/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp @@ -221,9 +221,9 @@ bool NativeThreadNetBSD::GetStopReason(ThreadStopInfo &stop_info, llvm_unreachable("unhandled StateType!"); } -NativeRegisterContext& NativeThreadNetBSD::GetRegisterContext() { +NativeRegisterContextNetBSD &NativeThreadNetBSD::GetRegisterContext() { assert(m_reg_context_up); -return *m_reg_context_up; + return *m_reg_context_up; } Status NativeThreadNetBSD::SetWatchpoint(lldb::addr_t addr, size_t size, @@ -284,3 +284,13 @@ Status NativeThreadNetBSD::RemoveHardwareBreakpoint(lldb::addr_t addr) { return Status("Clearing hardware breakpoint failed."); } + +Status NativeThreadNetBSD::CopyWatchpointsFrom(NativeThreadNetBSD &source) { + Status s = GetRegisterContext().CopyHardwareWatchpointsFrom( + source.GetRegisterContext()); + if (!s.Fail()) { + m_watchpoint_index_map = source.m_watchpoint_index_map; + m_hw_break_index_map = source.m_hw_break_index_map; + } + return s; +} diff --git a/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h b/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h index f944b50efe07..89b61ef86722 100644 --- a/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h +++ b/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h @@ -11,6 +11,8 @@ #include "lldb/Host/common/NativeThreadProtocol.h" +#include "Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h" + #include #include #include @@ -34,7 +36,7 @@ public: bool GetStopReason(ThreadStopInfo &stop_info, std::string &description) override; - NativeRegisterContext& GetRegisterContext() override; + NativeRegisterContextNetBSD &GetRegisterContext() override; Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) override; @@ -62,10 +64,12 @@ private: void SetRunning(); void SetStepping(); + Status CopyWatchpointsFrom(NativeThreadNetBSD& source); + // Member Variables lldb::StateType m_state; ThreadStopInfo m_stop_info; - std::unique_ptr m_reg_context_up; + std::unique_ptr m_reg_context_up; std::string m_stop_description; using WatchpointIndexMap = std::map; WatchpointIndexMap m_watchpoint_index_map; diff --git a/lldb/test/Shell/Watchpoint/Inputs/thread-dbreg.c b/lldb/test/Shell/Watchpoint/Inputs/thread-dbreg.c new file mode 100644 index 000000000000..14f3474789af --- /dev/null +++ b/lldb/test/Shell/Watchpoint/Inputs/thread-dbreg.c @@ -0,0 +1,23 @@ +#include + +int g_watchme = 0; + +void *thread_func(void *arg) { + /* watchpoint trigger from subthread */ + g_watchme = 2; + return 0; +} + +int main() { + pthread_t thread; + if (pthread_create(&thread, 0, thread_func, 0)) + return 1; + + /* watchpoint trigger from main thread */ + g_watchme = 1; + + if (pthread_join(thread, 0)) + return 2; + + return 0; +} diff --git a/lldb/test/Shell/Watchpoint/netbsd-nouserdbregs.test b/lldb/test/Shell/Watchpoint/netbsd-nouserdbregs.test new file mode 100644 index 000000000000..884636c7cdf8 --- /dev/null +++ b/lldb/test/Shell/Watchpoint/netbsd-nouserdbregs.test @@ -0,0 +1,22 @@ +# Check that 'watchpoint set' errors out gracefully when we can't set dbregs +# and that new threads are monitored correctly even though we can't copy dbregs. + +# REQUIRES: system-netbsd && (target-x86 || target-x86_64) && !dbregs-set +# RUN: %clang_host %p/Inputs/thread-dbreg.c -pthread -g -o %t.out +# RUN: %lldb -b -o 'settings set interpreter.stop-command-source-on-error false' -s %s %t.out 2>&1 | FileCheck %s + +settings show interpreter.stop-command-source-on-error +# CHECK: interpreter.stop-command-source-on-error (boolean) = false + +b main +# CHECK: Breakpoint {{[0-9]+}}: where = {{.*}}`main +b thread_func +# CHECK: Breakpoint {{[0-9]+}}: where = {{.*}}`thread_func +run +# CHECK: stop reason = breakpoint +watchpoint set variable g_watchme +# CHECK: error: Watchpoint creation failed +cont +# CHECK: stop reason = breakpoint +cont +# CHECK: Process {{[0-9]+}} exited with status = 0 diff --git a/lldb/test/Shell/lit.cfg.py b/lldb/test/Shell/lit.cfg.py index cc54234faf78..84c5b730dd31 100644 --- a/lldb/test/Shell/lit.cfg.py +++ b/lldb/test/Shell/lit.cfg.py @@ -5,6 +5,7 @@ import platform import re import shutil import site +import subprocess import sys import lit.formats @@ -103,3 +104,17 @@ if config.lldb_enable_lzma: if find_executable('xz') != None: config.available_features.add('xz') + +# NetBSD permits setting dbregs either if one is root +# or if user_set_dbregs is enabled +can_set_dbregs = True +if platform.system() == 'NetBSD' and os.geteuid() != 0: + try: + output = subprocess.check_output(["/sbin/sysctl", "-n", + "security.models.extensions.user_set_dbregs"]).decode().strip() + if output != "1": + can_set_dbregs = False + except subprocess.CalledProcessError: + can_set_dbregs = False +if can_set_dbregs: + config.available_features.add('dbregs-set')