forked from OSchip/llvm-project
[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
This commit is contained in:
parent
8d9400b65b
commit
d970d4d4aa
|
@ -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 @@ public:
|
|||
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 @@ public:
|
|||
|
||||
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 @@ 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<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 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')
|
||||
|
|
Loading…
Reference in New Issue