Skip to content

Commit 944ca8c

Browse files
committed
[LLDB] add arch-specific watchpoint behavior defaults to lldb
lldb was originally designed to get the watchpoint exception behavior from the gdb remote serial protocol stub -- exceptions are either received before the instruction executes, or after the instruction has executed. This behavior was reported via two lldb extensions to gdb RSP, so generic remote stubs like gdbserver or a JTAG stub, would not tell lldb which behavior was correct, and it would default to "exceptions are received after the instruction has executed". Two architectures hard coded their correct "exceptions before instruction" behavior, to work around this issue. Most architectures have a fixed behavior of watchpoint exceptions, and we can center that information in lldb. We can allow a remote stub to override the default behavior via our packet extensions if it's needed on a specific target. This patch also separates the fetching of the number of watchpoints from whether exceptions are before/after the insn. Currently if lldb couldn't fetch the number of watchpoints (not really needed), it also wouldn't get when exceptions are received, and watchpoint handling would fail. lldb doesn't actually use the number of watchpoints for anything beyond printing it to the user. Differential Revision: https://reviews.llvm.org/D143215 rdar://101426626 (cherry picked from commit eaeb8dd)
1 parent b4b2e26 commit 944ca8c

File tree

13 files changed

+108
-104
lines changed

13 files changed

+108
-104
lines changed

lldb/include/lldb/Target/Process.h

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <list>
1818
#include <memory>
1919
#include <mutex>
20+
#include <optional>
2021
#include <string>
2122
#include <unordered_set>
2223
#include <vector>
@@ -1834,20 +1835,35 @@ class Process : public std::enable_shared_from_this<Process>,
18341835
virtual Status
18351836
GetMemoryRegions(lldb_private::MemoryRegionInfos &region_list);
18361837

1837-
virtual Status GetWatchpointSupportInfo(uint32_t &num) {
1838-
Status error;
1839-
num = 0;
1840-
error.SetErrorString("Process::GetWatchpointSupportInfo() not supported");
1841-
return error;
1838+
/// Get the number of watchpoints supported by this target.
1839+
///
1840+
/// We may be able to determine the number of watchpoints available
1841+
/// on this target; retrieve this value if possible.
1842+
///
1843+
/// This number may be less than the number of watchpoints a user
1844+
/// can specify. This is because a single user watchpoint may require
1845+
/// multiple watchpoint slots to implement. Due to the size
1846+
/// and/or alignment of objects.
1847+
///
1848+
/// \return
1849+
/// Returns the number of watchpoints, if available.
1850+
virtual std::optional<uint32_t> GetWatchpointSlotCount() {
1851+
return std::nullopt;
18421852
}
18431853

1844-
virtual Status GetWatchpointSupportInfo(uint32_t &num, bool &after) {
1845-
Status error;
1846-
num = 0;
1847-
after = true;
1848-
error.SetErrorString("Process::GetWatchpointSupportInfo() not supported");
1849-
return error;
1850-
}
1854+
/// Whether lldb will be notified about watchpoints after
1855+
/// the instruction has completed executing, or if the
1856+
/// instruction is rolled back and it is notified before it
1857+
/// executes.
1858+
/// The default behavior is "exceptions received after instruction
1859+
/// has executed", except for certain CPU architectures.
1860+
/// Process subclasses may override this if they have additional
1861+
/// information.
1862+
///
1863+
/// \return
1864+
/// Returns true for targets where lldb is notified after
1865+
/// the instruction has completed executing.
1866+
bool GetWatchpointReportedAfter();
18511867

18521868
lldb::ModuleSP ReadModuleFromMemory(const FileSpec &file_spec,
18531869
lldb::addr_t header_addr,
@@ -2698,6 +2714,24 @@ void PruneThreadPlans();
26982714
return Status("Process::DoGetMemoryRegionInfo() not supported");
26992715
}
27002716

2717+
/// Provide an override value in the subclass for lldb's
2718+
/// CPU-based logic for whether watchpoint exceptions are
2719+
/// received before or after an instruction executes.
2720+
///
2721+
/// If a Process subclass needs to override this architecture-based
2722+
/// result, it may do so by overriding this method.
2723+
///
2724+
/// \return
2725+
/// No boolean returned means there is no override of the
2726+
/// default architecture-based behavior.
2727+
/// true is returned for targets where watchpoints are reported
2728+
/// after the instruction has completed.
2729+
/// false is returned for targets where watchpoints are reported
2730+
/// before the instruction executes.
2731+
virtual std::optional<bool> DoGetWatchpointReportedAfter() {
2732+
return std::nullopt;
2733+
}
2734+
27012735
lldb::StateType GetPrivateState();
27022736

27032737
/// The "private" side of resuming a process. This doesn't alter the state

lldb/source/API/SBProcess.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -967,7 +967,12 @@ SBProcess::GetNumSupportedHardwareWatchpoints(lldb::SBError &sb_error) const {
967967
if (process_sp) {
968968
std::lock_guard<std::recursive_mutex> guard(
969969
process_sp->GetTarget().GetAPIMutex());
970-
sb_error.SetError(process_sp->GetWatchpointSupportInfo(num));
970+
std::optional<uint32_t> actual_num = process_sp->GetWatchpointSlotCount();
971+
if (actual_num) {
972+
num = *actual_num;
973+
} else {
974+
sb_error.SetErrorString("Unable to determine number of watchpoints");
975+
}
971976
} else {
972977
sb_error.SetErrorString("SBProcess is invalid");
973978
}

lldb/source/Commands/CommandObjectWatchpoint.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -209,13 +209,13 @@ class CommandObjectWatchpointList : public CommandObjectParsed {
209209
Target *target = &GetSelectedTarget();
210210

211211
if (target->GetProcessSP() && target->GetProcessSP()->IsAlive()) {
212-
uint32_t num_supported_hardware_watchpoints;
213-
Status error = target->GetProcessSP()->GetWatchpointSupportInfo(
214-
num_supported_hardware_watchpoints);
215-
if (error.Success())
212+
std::optional<uint32_t> num_supported_hardware_watchpoints =
213+
target->GetProcessSP()->GetWatchpointSlotCount();
214+
215+
if (num_supported_hardware_watchpoints)
216216
result.AppendMessageWithFormat(
217217
"Number of supported hardware watchpoints: %u\n",
218-
num_supported_hardware_watchpoints);
218+
*num_supported_hardware_watchpoints);
219219
}
220220

221221
const WatchpointList &watchpoints = target->GetWatchpointList();

lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -835,15 +835,8 @@ void ProcessWindows::OnDebuggerError(const Status &error, uint32_t type) {
835835
}
836836
}
837837

838-
Status ProcessWindows::GetWatchpointSupportInfo(uint32_t &num) {
839-
num = RegisterContextWindows::GetNumHardwareBreakpointSlots();
840-
return {};
841-
}
842-
843-
Status ProcessWindows::GetWatchpointSupportInfo(uint32_t &num, bool &after) {
844-
num = RegisterContextWindows::GetNumHardwareBreakpointSlots();
845-
after = RegisterContextWindows::DoHardwareBreakpointsTriggerAfter();
846-
return {};
838+
std::optional<uint32_t> ProcessWindows::GetWatchpointSlotCount() {
839+
return RegisterContextWindows::GetNumHardwareBreakpointSlots();
847840
}
848841

849842
Status ProcessWindows::EnableWatchpoint(Watchpoint *wp, bool notify) {

lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@ class ProcessWindows : public Process, public ProcessDebugger {
9595
void OnDebugString(const std::string &string) override;
9696
void OnDebuggerError(const Status &error, uint32_t type) override;
9797

98-
Status GetWatchpointSupportInfo(uint32_t &num) override;
99-
Status GetWatchpointSupportInfo(uint32_t &num, bool &after) override;
98+
std::optional<uint32_t> GetWatchpointSlotCount() override;
10099
Status EnableWatchpoint(Watchpoint *wp, bool notify = true) override;
101100
Status DisableWatchpoint(Watchpoint *wp, bool notify = true) override;
102101

lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ class RegisterContextWindows : public lldb_private::RegisterContext {
3838
static constexpr uint32_t GetNumHardwareBreakpointSlots() {
3939
return NUM_HARDWARE_BREAKPOINT_SLOTS;
4040
}
41-
static constexpr bool DoHardwareBreakpointsTriggerAfter() { return true; }
4241

4342
bool AddHardwareBreakpoint(uint32_t slot, lldb::addr_t address, uint32_t size,
4443
bool read, bool write);

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp

Lines changed: 18 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,77 +1773,51 @@ Status GDBRemoteCommunicationClient::LoadQXferMemoryMap() {
17731773
return error;
17741774
}
17751775

1776-
Status GDBRemoteCommunicationClient::GetWatchpointSupportInfo(uint32_t &num) {
1777-
Status error;
1778-
1776+
std::optional<uint32_t> GDBRemoteCommunicationClient::GetWatchpointSlotCount() {
17791777
if (m_supports_watchpoint_support_info == eLazyBoolYes) {
1780-
num = m_num_supported_hardware_watchpoints;
1781-
return error;
1778+
return m_num_supported_hardware_watchpoints;
17821779
}
17831780

1784-
// Set num to 0 first.
1785-
num = 0;
1781+
std::optional<uint32_t> num;
17861782
if (m_supports_watchpoint_support_info != eLazyBoolNo) {
17871783
StringExtractorGDBRemote response;
17881784
if (SendPacketAndWaitForResponse("qWatchpointSupportInfo:", response) ==
17891785
PacketResult::Success) {
17901786
m_supports_watchpoint_support_info = eLazyBoolYes;
17911787
llvm::StringRef name;
17921788
llvm::StringRef value;
1793-
bool found_num_field = false;
17941789
while (response.GetNameColonValue(name, value)) {
17951790
if (name.equals("num")) {
17961791
value.getAsInteger(0, m_num_supported_hardware_watchpoints);
17971792
num = m_num_supported_hardware_watchpoints;
1798-
found_num_field = true;
17991793
}
18001794
}
1801-
if (!found_num_field) {
1795+
if (!num) {
18021796
m_supports_watchpoint_support_info = eLazyBoolNo;
18031797
}
18041798
} else {
18051799
m_supports_watchpoint_support_info = eLazyBoolNo;
18061800
}
18071801
}
18081802

1809-
if (m_supports_watchpoint_support_info == eLazyBoolNo) {
1810-
error.SetErrorString("qWatchpointSupportInfo is not supported");
1811-
}
1812-
return error;
1813-
}
1814-
1815-
lldb_private::Status GDBRemoteCommunicationClient::GetWatchpointSupportInfo(
1816-
uint32_t &num, bool &after, const ArchSpec &arch) {
1817-
Status error(GetWatchpointSupportInfo(num));
1818-
if (error.Success())
1819-
error = GetWatchpointsTriggerAfterInstruction(after, arch);
1820-
return error;
1803+
return num;
18211804
}
18221805

1823-
lldb_private::Status
1824-
GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction(
1825-
bool &after, const ArchSpec &arch) {
1826-
Status error;
1827-
llvm::Triple triple = arch.GetTriple();
1828-
1829-
// we assume watchpoints will happen after running the relevant opcode and we
1830-
// only want to override this behavior if we have explicitly received a
1831-
// qHostInfo telling us otherwise
1832-
if (m_qHostInfo_is_valid != eLazyBoolYes) {
1833-
// On targets like MIPS and ppc64, watchpoint exceptions are always
1834-
// generated before the instruction is executed. The connected target may
1835-
// not support qHostInfo or qWatchpointSupportInfo packets.
1836-
after = !(triple.isMIPS() || triple.isPPC64());
1837-
} else {
1838-
// For MIPS and ppc64, set m_watchpoints_trigger_after_instruction to
1839-
// eLazyBoolNo if it is not calculated before.
1840-
if (m_watchpoints_trigger_after_instruction == eLazyBoolCalculate &&
1841-
(triple.isMIPS() || triple.isPPC64()))
1842-
m_watchpoints_trigger_after_instruction = eLazyBoolNo;
1806+
std::optional<bool> GDBRemoteCommunicationClient::GetWatchpointReportedAfter() {
1807+
if (m_qHostInfo_is_valid == eLazyBoolCalculate)
1808+
GetHostInfo();
18431809

1844-
after = (m_watchpoints_trigger_after_instruction != eLazyBoolNo);
1810+
// Process determines this by target CPU, but allow for the
1811+
// remote stub to override it via the qHostInfo
1812+
// watchpoint_exceptions_received key, if it is present.
1813+
if (m_qHostInfo_is_valid == eLazyBoolYes) {
1814+
if (m_watchpoints_trigger_after_instruction == eLazyBoolNo)
1815+
return false;
1816+
if (m_watchpoints_trigger_after_instruction == eLazyBoolYes)
1817+
return true;
18451818
}
1846-
return error;
1819+
1820+
return std::nullopt;
18471821
}
18481822

18491823
int GDBRemoteCommunicationClient::SetSTDIN(const FileSpec &file_spec) {

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,9 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
194194

195195
Status GetMemoryRegionInfo(lldb::addr_t addr, MemoryRegionInfo &range_info);
196196

197-
Status GetWatchpointSupportInfo(uint32_t &num);
197+
std::optional<uint32_t> GetWatchpointSlotCount();
198198

199-
Status GetWatchpointSupportInfo(uint32_t &num, bool &after,
200-
const ArchSpec &arch);
201-
202-
Status GetWatchpointsTriggerAfterInstruction(bool &after,
203-
const ArchSpec &arch);
199+
std::optional<bool> GetWatchpointReportedAfter();
204200

205201
const ArchSpec &GetHostArchitecture();
206202

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2830,16 +2830,12 @@ Status ProcessGDBRemote::DoGetMemoryRegionInfo(addr_t load_addr,
28302830
return error;
28312831
}
28322832

2833-
Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num) {
2834-
2835-
Status error(m_gdb_comm.GetWatchpointSupportInfo(num));
2836-
return error;
2833+
std::optional<uint32_t> ProcessGDBRemote::GetWatchpointSlotCount() {
2834+
return m_gdb_comm.GetWatchpointSlotCount();
28372835
}
28382836

2839-
Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num, bool &after) {
2840-
Status error(m_gdb_comm.GetWatchpointSupportInfo(
2841-
num, after, GetTarget().GetArchitecture()));
2842-
return error;
2837+
std::optional<bool> ProcessGDBRemote::DoGetWatchpointReportedAfter() {
2838+
return m_gdb_comm.GetWatchpointReportedAfter();
28432839
}
28442840

28452841
Status ProcessGDBRemote::DoDeallocateMemory(lldb::addr_t addr) {

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class ProcessGDBRemote : public Process,
158158

159159
Status DisableWatchpoint(Watchpoint *wp, bool notify = true) override;
160160

161-
Status GetWatchpointSupportInfo(uint32_t &num) override;
161+
std::optional<uint32_t> GetWatchpointSlotCount() override;
162162

163163
llvm::Expected<TraceSupportedResponse> TraceSupported() override;
164164

@@ -171,7 +171,7 @@ class ProcessGDBRemote : public Process,
171171
llvm::Expected<std::vector<uint8_t>>
172172
TraceGetBinaryData(const TraceGetBinaryDataRequest &request) override;
173173

174-
Status GetWatchpointSupportInfo(uint32_t &num, bool &after) override;
174+
std::optional<bool> DoGetWatchpointReportedAfter() override;
175175

176176
bool StartNoticingNewThreads() override;
177177

0 commit comments

Comments
 (0)