Skip to content

Commit 5953532

Browse files
authored
[lldb] Add QSupported key to report watchpoint types supported (#80376)
debugserver on arm64 devices can manage both Byte Address Select watchpoints (1-8 bytes) and MASK watchpoints (8 bytes-2 gigabytes). This adds a SupportedWatchpointTypes key to the QSupported response from debugserver with a list of these, so lldb can take full advantage of them when creating larger regions with a single hardware watchpoint. Also add documentation for this, and two other lldb extensions, to the lldb-gdb-remote.txt documentation. Re-enable TestLargeWatchpoint.py on Darwin systems when testing with the in-tree built debugserver. I can remove the "in-tree built debugserver" in the future when this new key is handled by an Xcode debugserver.
1 parent 8f80df0 commit 5953532

File tree

11 files changed

+118
-67
lines changed

11 files changed

+118
-67
lines changed

lldb/docs/lldb-gdb-remote.txt

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,45 @@ read packet: +
3838
read packet: $OK#9a
3939
send packet: +
4040

41+
//----------------------------------------------------------------------
42+
// "QSupported"
43+
//
44+
// BRIEF
45+
// Query the GDB remote server for features it supports
46+
//
47+
// PRIORITY TO IMPLEMENT
48+
// Optional.
49+
//----------------------------------------------------------------------
50+
51+
QSupported is a standard GDB Remote Serial Protocol packet, but
52+
there are several additions to the response that lldb can parse.
53+
They are not all listed here.
54+
55+
An example exchange:
56+
57+
send packet: qSupported:xmlRegisters=i386,arm,mips,arc;multiprocess+;fork-events+;vfork-events+
58+
59+
read packet: qXfer:features:read+;PacketSize=20000;qEcho+;native-signals+;SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;SupportedWatchpointTypes=aarch64-mask,aarch64-bas;
60+
61+
In the example above, three lldb extensions are shown:
4162

63+
PacketSize=20000
64+
The base 16 maximum packet size that the stub can handle.
65+
SupportedCompressions=<item,item,...>
66+
A list of compression types that the stub can use to compress packets
67+
when the QEnableCompression packet is used to request one of them.
68+
SupportedWatchpointTypes=<item,item,...>
69+
A list of watchpoint types that this stub can manage.
70+
Currently defined names are:
71+
x86_64 64-bit x86-64 watchpoints
72+
(1, 2, 4, 8 byte watchpoints aligned to those amounts)
73+
aarch64-bas AArch64 Byte Address Select watchpoints
74+
(any number of contiguous bytes within a doubleword)
75+
aarch64-mask AArch64 MASK watchpoints
76+
(any power-of-2 region of memory from 8 to 2GB, aligned)
77+
If nothing is specified, lldb will default to sending power-of-2
78+
watchpoints, up to a pointer size, `sizeof(void*)`, a reasonable
79+
baseline assumption.
4280

4381
//----------------------------------------------------------------------
4482
// "A" - launch args packet
@@ -594,7 +632,7 @@ read packet: <binary data>/E<error code>;AAAAAAAAA
594632

595633
With LLDB, for register information, remote GDB servers can add
596634
support for the "qRegisterInfoN" packet where "N" is a zero based
597-
base16 register number that must start at zero and increase by one
635+
base 16 register number that must start at zero and increase by one
598636
for each register that is supported. The response is done in typical
599637
GDB remote fashion where a series of "KEY:VALUE;" pairs are returned.
600638
An example for the x86_64 registers is included below:
@@ -1053,7 +1091,7 @@ Suggested key names:
10531091
// 64-bit slices so it may be impossible to know until you're attached to a real
10541092
// process to know what you're working with.
10551093
//
1056-
// All numeric fields return base-16 numbers without any "0x" prefix.
1094+
// All numeric fields return base 16 numbers without any "0x" prefix.
10571095
//----------------------------------------------------------------------
10581096

10591097
An i386 process:
@@ -1085,7 +1123,7 @@ main-binary-uuid: is the UUID of a firmware type binary that the gdb stub knows
10851123
main-binary-address: is the load address of the firmware type binary
10861124
main-binary-slide: is the slide of the firmware type binary, if address isn't known
10871125

1088-
binary-addresses: A comma-separated list of binary load addresses base16.
1126+
binary-addresses: A comma-separated list of binary load addresses base 16.
10891127
lldb will parse the binaries in memory to get UUIDs, then
10901128
try to find the binaries & debug info by UUID. Intended for
10911129
use with a small number of firmware type binaries where the
@@ -1302,7 +1340,7 @@ tuples to return are:
13021340

13031341
dirty-pages:[<hexaddr>][,<hexaddr]; // A list of memory pages within this
13041342
// region that are "dirty" -- they have been modified.
1305-
// Page addresses are in base16. The size of a page can
1343+
// Page addresses are in base 16. The size of a page can
13061344
// be found from the qHostInfo's page-size key-value.
13071345
//
13081346
// If the stub supports identifying dirty pages within a
@@ -1585,7 +1623,7 @@ for this region.
15851623
// describe why something stopped.
15861624
//
15871625
// For "reason:watchpoint", "description" is an ascii-hex
1588-
// encoded string with between one and three base10 numbers,
1626+
// encoded string with between one and three base 10 numbers,
15891627
// space separated. The three numbers are
15901628
// 1. watchpoint address. This address should always be within
15911629
// a memory region lldb has a watchpoint on.
@@ -1911,7 +1949,7 @@ for this region.
19111949
//
19121950
// jThreadExtendedInfo:{"thread":612910}
19131951
//
1914-
// Because this is a JSON string, the thread number is provided in base10.
1952+
// Because this is a JSON string, the thread number is provided in base 10.
19151953
// Additional key-value pairs may be provided by lldb to the gdb remote
19161954
// stub. For instance, on some versions of macOS, lldb can read offset
19171955
// information out of the system libraries. Using those offsets, debugserver
@@ -1980,13 +2018,13 @@ for this region.
19802018
//
19812019
// $N<uncompressed payload>#00
19822020
//
1983-
// $C<size of uncompressed payload in base10>:<compressed payload>#00
2021+
// $C<size of uncompressed payload in base 10>:<compressed payload>#00
19842022
//
19852023
// Where "#00" is the actual checksum value if noack mode is not enabled. The checksum
19862024
// value is for the "N<uncompressed payload>" or
1987-
// "C<size of uncompressed payload in base10>:<compressed payload>" bytes in the packet.
2025+
// "C<size of uncompressed payload in base 10>:<compressed payload>" bytes in the packet.
19882026
//
1989-
// The size of the uncompressed payload in base10 is provided because it will simplify
2027+
// The size of the uncompressed payload in base 10 is provided because it will simplify
19902028
// decompression if the final buffer size needed is known ahead of time.
19912029
//
19922030
// Compression on low-latency connections is unlikely to be an improvement. Particularly

lldb/include/lldb/Breakpoint/WatchpointAlgorithms.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
#include "lldb/Breakpoint/WatchpointResource.h"
1313
#include "lldb/Utility/ArchSpec.h"
14-
#include "lldb/lldb-public.h"
14+
#include "lldb/lldb-private.h"
1515

1616
#include <vector>
1717

@@ -58,7 +58,7 @@ class WatchpointAlgorithms {
5858
/// watchpoint cannot be set.
5959
static std::vector<lldb::WatchpointResourceSP> AtomizeWatchpointRequest(
6060
lldb::addr_t addr, size_t size, bool read, bool write,
61-
lldb::WatchpointHardwareFeature supported_features, ArchSpec &arch);
61+
WatchpointHardwareFeature supported_features, ArchSpec &arch);
6262

6363
protected:
6464
struct Region {

lldb/include/lldb/lldb-enumerations.h

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -448,32 +448,6 @@ enum WatchpointWriteType {
448448
eWatchpointWriteTypeOnModify
449449
};
450450

451-
/// The hardware and native stub capabilities for a given target,
452-
/// for translating a user's watchpoint request into hardware
453-
/// capable watchpoint resources.
454-
FLAGS_ENUM(WatchpointHardwareFeature){
455-
/// lldb will fall back to a default that assumes the target
456-
/// can watch up to pointer-size power-of-2 regions, aligned to
457-
/// power-of-2.
458-
eWatchpointHardwareFeatureUnknown = (1u << 0),
459-
460-
/// Intel systems can watch 1, 2, 4, or 8 bytes (in 64-bit targets),
461-
/// aligned naturally.
462-
eWatchpointHardwareX86 = (1u << 1),
463-
464-
/// ARM systems with Byte Address Select watchpoints
465-
/// can watch any consecutive series of bytes up to the
466-
/// size of a pointer (4 or 8 bytes), at a pointer-size
467-
/// alignment.
468-
eWatchpointHardwareArmBAS = (1u << 2),
469-
470-
/// ARM systems with MASK watchpoints can watch any power-of-2
471-
/// sized region from 8 bytes to 2 gigabytes, aligned to that
472-
/// same power-of-2 alignment.
473-
eWatchpointHardwareArmMASK = (1u << 3),
474-
};
475-
LLDB_MARK_AS_BITMASK_ENUM(WatchpointHardwareFeature)
476-
477451
/// Programming language type.
478452
///
479453
/// These enumerations use the same language enumerations as the DWARF

lldb/include/lldb/lldb-private-enumerations.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLDB_LLDB_PRIVATE_ENUMERATIONS_H
1010
#define LLDB_LLDB_PRIVATE_ENUMERATIONS_H
1111

12+
#include "lldb/lldb-enumerations.h"
1213
#include "llvm/ADT/StringRef.h"
1314
#include "llvm/Support/FormatProviders.h"
1415
#include "llvm/Support/raw_ostream.h"
@@ -282,4 +283,30 @@ enum InterruptionControl : bool {
282283
DoNotAllowInterruption = false,
283284
};
284285

286+
/// The hardware and native stub capabilities for a given target,
287+
/// for translating a user's watchpoint request into hardware
288+
/// capable watchpoint resources.
289+
FLAGS_ENUM(WatchpointHardwareFeature){
290+
/// lldb will fall back to a default that assumes the target
291+
/// can watch up to pointer-size power-of-2 regions, aligned to
292+
/// power-of-2.
293+
eWatchpointHardwareFeatureUnknown = (1u << 0),
294+
295+
/// Intel systems can watch 1, 2, 4, or 8 bytes (in 64-bit targets),
296+
/// aligned naturally.
297+
eWatchpointHardwareX86 = (1u << 1),
298+
299+
/// ARM systems with Byte Address Select watchpoints
300+
/// can watch any consecutive series of bytes up to the
301+
/// size of a pointer (4 or 8 bytes), at a pointer-size
302+
/// alignment.
303+
eWatchpointHardwareArmBAS = (1u << 2),
304+
305+
/// ARM systems with MASK watchpoints can watch any power-of-2
306+
/// sized region from 8 bytes to 2 gigabytes, aligned to that
307+
/// same power-of-2 alignment.
308+
eWatchpointHardwareArmMASK = (1u << 3),
309+
};
310+
LLDB_MARK_AS_BITMASK_ENUM(WatchpointHardwareFeature)
311+
285312
#endif // LLDB_LLDB_PRIVATE_ENUMERATIONS_H

lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,8 @@ def add_qSupported_packets(self, client_features=[]):
921921
"qSaveCore",
922922
"native-signals",
923923
"QNonStop",
924+
"SupportedWatchpointTypes",
925+
"SupportedCompressions",
924926
]
925927

926928
def parse_qSupported_response(self, context):

lldb/source/Breakpoint/WatchpointAlgorithms.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ WatchpointAlgorithms::AtomizeWatchpointRequest(
2727

2828
std::vector<Region> entries;
2929

30-
if (supported_features &
31-
WatchpointHardwareFeature::eWatchpointHardwareArmMASK) {
30+
if (supported_features & eWatchpointHardwareArmMASK) {
3231
entries =
3332
PowerOf2Watchpoints(addr, size,
3433
/*min_byte_size*/ 1,

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,18 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
403403
x.split(compressions, ',');
404404
if (!compressions.empty())
405405
MaybeEnableCompression(compressions);
406+
} else if (x.consume_front("SupportedWatchpointTypes=")) {
407+
llvm::SmallVector<llvm::StringRef, 4> watchpoint_types;
408+
x.split(watchpoint_types, ',');
409+
m_watchpoint_types = eWatchpointHardwareFeatureUnknown;
410+
for (auto wp_type : watchpoint_types) {
411+
if (wp_type == "x86_64")
412+
m_watchpoint_types |= eWatchpointHardwareX86;
413+
if (wp_type == "aarch64-mask")
414+
m_watchpoint_types |= eWatchpointHardwareArmMASK;
415+
if (wp_type == "aarch64-bas")
416+
m_watchpoint_types |= eWatchpointHardwareArmBAS;
417+
}
406418
} else if (x.consume_front("PacketSize=")) {
407419
StringExtractorGDBRemote packet_response(x);
408420
m_max_packet_size =
@@ -1828,6 +1840,11 @@ std::optional<uint32_t> GDBRemoteCommunicationClient::GetWatchpointSlotCount() {
18281840
return num;
18291841
}
18301842

1843+
WatchpointHardwareFeature
1844+
GDBRemoteCommunicationClient::GetSupportedWatchpointTypes() {
1845+
return m_watchpoint_types;
1846+
}
1847+
18311848
std::optional<bool> GDBRemoteCommunicationClient::GetWatchpointReportedAfter() {
18321849
if (m_qHostInfo_is_valid == eLazyBoolCalculate)
18331850
GetHostInfo();

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
199199

200200
std::optional<bool> GetWatchpointReportedAfter();
201201

202+
WatchpointHardwareFeature GetSupportedWatchpointTypes();
203+
202204
const ArchSpec &GetHostArchitecture();
203205

204206
std::chrono::seconds GetHostDefaultPacketTimeout();
@@ -581,6 +583,8 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
581583
lldb::tid_t m_curr_tid_run = LLDB_INVALID_THREAD_ID;
582584

583585
uint32_t m_num_supported_hardware_watchpoints = 0;
586+
WatchpointHardwareFeature m_watchpoint_types =
587+
eWatchpointHardwareFeatureUnknown;
584588
uint32_t m_low_mem_addressing_bits = 0;
585589
uint32_t m_high_mem_addressing_bits = 0;
586590

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3156,16 +3156,7 @@ Status ProcessGDBRemote::EnableWatchpoint(WatchpointSP wp_sp, bool notify) {
31563156

31573157
ArchSpec target_arch = GetTarget().GetArchitecture();
31583158
WatchpointHardwareFeature supported_features =
3159-
eWatchpointHardwareFeatureUnknown;
3160-
3161-
// LWP_TODO: enable MASK watchpoint for arm64 debugserver
3162-
// when it reports that it supports them.
3163-
if (target_arch.GetTriple().getOS() == llvm::Triple::MacOSX &&
3164-
target_arch.GetTriple().getArch() == llvm::Triple::aarch64) {
3165-
#if 0
3166-
supported_features |= eWatchpointHardwareArmMASK;
3167-
#endif
3168-
}
3159+
m_gdb_comm.GetSupportedWatchpointTypes();
31693160

31703161
std::vector<WatchpointResourceSP> resources =
31713162
WatchpointAlgorithms::AtomizeWatchpointRequest(

lldb/test/API/functionalities/watchpoint/large-watchpoint/TestLargeWatchpoint.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@ def continue_and_report_stop_reason(self, process, iter_str):
2525
@skipIf(archs=no_match(["arm64", "arm64e", "aarch64"]))
2626
@skipUnlessDarwin
2727

28-
# LWP_TODO: until debugserver advertises that it supports
29-
# MASK watchpoints, this test can't be enabled, lldb won't
30-
# try to send watchpoints larger than 8 bytes.
31-
@skipIfDarwin
32-
3328
# debugserver only gained the ability to watch larger regions
3429
# with this patch.
3530
@skipIfOutOfTreeDebugserver

lldb/tools/debugserver/source/RNBRemote.cpp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3557,10 +3557,10 @@ static bool GetProcessNameFrom_vAttach(const char *&p,
35573557
rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
35583558
uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet
35593559
// size--debugger can always use less
3560-
char buf[256];
3561-
snprintf(buf, sizeof(buf),
3562-
"qXfer:features:read+;PacketSize=%x;qEcho+;native-signals+",
3563-
max_packet_size);
3560+
std::stringstream reply;
3561+
reply << "qXfer:features:read+;PacketSize=" << std::hex << max_packet_size
3562+
<< ";";
3563+
reply << "qEcho+;native-signals+;";
35643564

35653565
bool enable_compression = false;
35663566
(void)enable_compression;
@@ -3574,15 +3574,19 @@ rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
35743574
#endif
35753575

35763576
if (enable_compression) {
3577-
strcat(buf, ";SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;"
3578-
"DefaultCompressionMinSize=");
3579-
char numbuf[16];
3580-
snprintf(numbuf, sizeof(numbuf), "%zu", m_compression_minsize);
3581-
numbuf[sizeof(numbuf) - 1] = '\0';
3582-
strcat(buf, numbuf);
3583-
}
3584-
3585-
return SendPacket(buf);
3577+
reply << "SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;";
3578+
reply << "DefaultCompressionMinSize=" << std::dec << m_compression_minsize
3579+
<< ";";
3580+
}
3581+
3582+
#if (defined(__arm64__) || defined(__aarch64__))
3583+
reply << "SupportedWatchpointTypes=aarch64-mask,aarch64-bas;";
3584+
#endif
3585+
#if defined(__x86_64__)
3586+
reply << "SupportedWatchpointTypes=x86_64;";
3587+
#endif
3588+
3589+
return SendPacket(reply.str().c_str());
35863590
}
35873591

35883592
static bool process_does_not_exist (nub_process_t pid) {

0 commit comments

Comments
 (0)