Skip to content
Permalink
Browse files

[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
  • Loading branch information
mgorny committed Nov 9, 2019
1 parent 8d9400b commit d970d4d4aa7345ebf8b7169b09f2775a93f86c33
Showing with 114 additions and 19 deletions.
  1. +0 −2 ...ages/Python/lldbsuite/test/commands/watchpoints/multiple_threads/TestWatchpointMultipleThreads.py
  2. +0 −1 ...nctionalities/thread/concurrent_events/TestConcurrentBreakpointsDelayedBreakpointOneWatchpoint.py
  3. +0 −1 .../Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentDelaySignalWatch.py
  4. +0 −1 ...s/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentDelayWatchBreak.py
  5. +0 −1 ...kages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentSignalWatch.py
  6. +0 −1 ...bsuite/test/functionalities/thread/concurrent_events/TestConcurrentTwoBreakpointsOneWatchpoint.py
  7. +0 −1 ...ckages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchBreak.py
  8. +0 −1 ...s/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentWatchBreakDelay.py
  9. +0 −1 .../functionalities/thread/concurrent_events/TestConcurrentWatchpointDelayWatchpointOneBreakpoint.py
  10. +0 −1 ...st/functionalities/thread/concurrent_events/TestConcurrentWatchpointWithDelayWatchpointThreads.py
  11. +12 −3 lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
  12. +2 −0 lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp
  13. +4 −1 lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h
  14. +15 −0 lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp
  15. +3 −0 lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h
  16. +12 −2 lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp
  17. +6 −2 lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h
  18. +23 −0 lldb/test/Shell/Watchpoint/Inputs/thread-dbreg.c
  19. +22 −0 lldb/test/Shell/Watchpoint/netbsd-nouserdbregs.test
  20. +15 −0 lldb/test/Shell/lit.cfg.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")
@@ -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. """
@@ -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."""
@@ -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."""
@@ -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."""
@@ -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. """
@@ -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."""
@@ -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."""
@@ -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. """
@@ -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. """
@@ -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<NativeThreadNetBSD &>(*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,
@@ -8,6 +8,8 @@

#include "NativeRegisterContextNetBSD.h"

#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h"

#include "lldb/Host/common/NativeProcessProtocol.h"

using namespace lldb_private;
@@ -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 @@ class NativeRegisterContextNetBSD : public NativeRegisterContextRegisterInfo {
static NativeRegisterContextNetBSD *
CreateHostNativeRegisterContextNetBSD(const ArchSpec &target_arch,
NativeThreadProtocol &native_thread);
virtual Status
CopyHardwareWatchpointsFrom(NativeRegisterContextNetBSD &source) = 0;

protected:
Status DoRegisterSet(int req, void *buf);
@@ -988,4 +988,19 @@ uint32_t NativeRegisterContextNetBSD_x86_64::NumSupportedHardwareWatchpoints() {
return 4;
}

Status NativeRegisterContextNetBSD_x86_64::CopyHardwareWatchpointsFrom(
NativeRegisterContextNetBSD &source) {
auto &r_source = static_cast<NativeRegisterContextNetBSD_x86_64&>(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__)
@@ -71,6 +71,9 @@ class NativeRegisterContextNetBSD_x86_64 : public NativeRegisterContextNetBSD {

uint32_t NumSupportedHardwareWatchpoints() override;

Status
CopyHardwareWatchpointsFrom(NativeRegisterContextNetBSD &source) override;

private:
// Private member types.
enum { GPRegSet, FPRegSet, XStateRegSet, DBRegSet };
@@ -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;
}
@@ -11,6 +11,8 @@

#include "lldb/Host/common/NativeThreadProtocol.h"

#include "Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h"

#include <csignal>
#include <map>
#include <string>
@@ -34,7 +36,7 @@ class NativeThreadNetBSD : public NativeThreadProtocol {
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 @@ class NativeThreadNetBSD : public NativeThreadProtocol {
void SetRunning();
void SetStepping();

Status CopyWatchpointsFrom(NativeThreadNetBSD& source);

// Member Variables
lldb::StateType m_state;
ThreadStopInfo m_stop_info;
std::unique_ptr<NativeRegisterContext> m_reg_context_up;
std::unique_ptr<NativeRegisterContextNetBSD> m_reg_context_up;
std::string m_stop_description;
using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>;
WatchpointIndexMap m_watchpoint_index_map;
@@ -0,0 +1,23 @@
#include <pthread.h>

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;
}
@@ -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
@@ -5,6 +5,7 @@
import re
import shutil
import site
import subprocess
import sys

import lit.formats
@@ -103,3 +104,17 @@ def calculate_arch_features(arch_string):

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')

0 comments on commit d970d4d

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