Skip to content

Commit 4c54399

Browse files
committed
[lldb] [Process/FreeBSDRemote] Explicitly copy dbregs to new threads
Explicitly copy dbregs to new threads to ensure that watchpoints are propagated properly. Fixes the test failure due to apparent kernel race between reporting a new thread and resuming main thread execution that makes implicit inheritance of dbregs unreliable. By copying them explicitly, we ensure that the new thread correctly respects watchpoints that were set after the thread was created but before it was reported. The code is copied from the NetBSD plugin and modernized to use llvm::Error. Differential Revision: https://reviews.llvm.org/D91032
1 parent 194c5ac commit 4c54399

File tree

7 files changed

+54
-6
lines changed

7 files changed

+54
-6
lines changed

lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,25 @@ void NativeProcessFreeBSD::MonitorSIGTRAP(lldb::pid_t pid) {
199199
if (info.pl_flags & (PL_FLAG_BORN | PL_FLAG_EXITED)) {
200200
if (info.pl_flags & PL_FLAG_BORN) {
201201
LLDB_LOG(log, "monitoring new thread, tid = {0}", info.pl_lwpid);
202-
AddThread(info.pl_lwpid);
202+
NativeThreadFreeBSD &t = AddThread(info.pl_lwpid);
203+
204+
// Technically, the FreeBSD kernel copies the debug registers to new
205+
// threads. However, there is a non-negligible delay between acquiring
206+
// the DR values and reporting the new thread during which the user may
207+
// establish a new watchpoint. In order to ensure that watchpoints
208+
// established during this period are propagated to new threads,
209+
// explicitly copy the DR value at the time the new thread is reported.
210+
//
211+
// See also: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=250954
212+
213+
llvm::Error error = t.CopyWatchpointsFrom(
214+
static_cast<NativeThreadFreeBSD &>(*GetCurrentThread()));
215+
if (error) {
216+
LLDB_LOG(log, "failed to copy watchpoints to new thread {0}: {1}",
217+
info.pl_lwpid, llvm::toString(std::move(error)));
218+
SetState(StateType::eStateInvalid);
219+
return;
220+
}
203221
} else /*if (info.pl_flags & PL_FLAG_EXITED)*/ {
204222
LLDB_LOG(log, "thread exited, tid = {0}", info.pl_lwpid);
205223
RemoveThread(info.pl_lwpid);

lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class NativeRegisterContextFreeBSD
2929
static NativeRegisterContextFreeBSD *
3030
CreateHostNativeRegisterContextFreeBSD(const ArchSpec &target_arch,
3131
NativeThreadProtocol &native_thread);
32+
virtual llvm::Error
33+
CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) = 0;
3234

3335
protected:
3436
virtual NativeProcessFreeBSD &GetProcess();

lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,4 +1169,19 @@ int NativeRegisterContextFreeBSD_x86_64::GetDR(int num) const {
11691169
}
11701170
}
11711171

1172+
llvm::Error NativeRegisterContextFreeBSD_x86_64::CopyHardwareWatchpointsFrom(
1173+
NativeRegisterContextFreeBSD &source) {
1174+
auto &r_source = static_cast<NativeRegisterContextFreeBSD_x86_64 &>(source);
1175+
Status res = r_source.ReadRegisterSet(DBRegSet);
1176+
if (!res.Fail()) {
1177+
// copy dbregs only if any watchpoints were set
1178+
if ((r_source.m_dbr.dr[7] & 0xFF) == 0)
1179+
return llvm::Error::success();
1180+
1181+
m_dbr = r_source.m_dbr;
1182+
res = WriteRegisterSet(DBRegSet);
1183+
}
1184+
return res.ToError();
1185+
}
1186+
11721187
#endif // defined(__x86_64__)

lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ class NativeRegisterContextFreeBSD_x86_64
5050

5151
Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
5252

53+
llvm::Error
54+
CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) override;
55+
5356
private:
5457
// Private member types.
5558
enum { GPRegSet, FPRegSet, XSaveRegSet, DBRegSet };

lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,14 @@ std::string NativeThreadFreeBSD::GetName() {
165165
}
166166
if (error != 0) {
167167
len = 0;
168-
LLDB_LOG(log, "tid = {0} in state {1} failed to get thread name: {2}", GetID(),
169-
m_state, strerror(errno));
168+
LLDB_LOG(log, "tid = {0} in state {1} failed to get thread name: {2}",
169+
GetID(), m_state, strerror(errno));
170170
}
171171
kp.resize(len / sizeof(struct kinfo_proc));
172172
break;
173173
}
174174

175-
for (auto& procinfo : kp) {
175+
for (auto &procinfo : kp) {
176176
if (procinfo.ki_tid == static_cast<lwpid_t>(GetID()))
177177
return procinfo.ki_tdname;
178178
}
@@ -273,3 +273,14 @@ Status NativeThreadFreeBSD::RemoveHardwareBreakpoint(lldb::addr_t addr) {
273273

274274
return Status("Clearing hardware breakpoint failed.");
275275
}
276+
277+
llvm::Error
278+
NativeThreadFreeBSD::CopyWatchpointsFrom(NativeThreadFreeBSD &source) {
279+
llvm::Error s = GetRegisterContext().CopyHardwareWatchpointsFrom(
280+
source.GetRegisterContext());
281+
if (!s) {
282+
m_watchpoint_index_map = source.m_watchpoint_index_map;
283+
m_hw_break_index_map = source.m_hw_break_index_map;
284+
}
285+
return s;
286+
}

lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class NativeThreadFreeBSD : public NativeThreadProtocol {
6464
void SetRunning();
6565
void SetStepping();
6666

67-
Status CopyWatchpointsFrom(NativeThreadFreeBSD &source);
67+
llvm::Error CopyWatchpointsFrom(NativeThreadFreeBSD &source);
6868

6969
// Member Variables
7070
lldb::StateType m_state;

lldb/test/API/commands/watchpoints/multiple_threads/TestWatchpointMultipleThreads.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ def test_watchpoint_before_thread_start(self):
2222
"""Test that we can hit a watchpoint we set before starting another thread"""
2323
self.do_watchpoint_test("Before running the thread")
2424

25-
@expectedFailureAll(oslist=["freebsd"])
2625
def test_watchpoint_after_thread_launch(self):
2726
"""Test that we can hit a watchpoint we set after launching another thread"""
2827
self.do_watchpoint_test("After launching the thread")

0 commit comments

Comments
 (0)