1 //===-- GDBRemoteCommunicationClientTest.cpp ------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
8 #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h"
9 #include "GDBRemoteTestUtils.h"
10 #include "lldb/Core/ModuleSpec.h"
11 #include "lldb/Host/XML.h"
12 #include "lldb/Target/MemoryRegionInfo.h"
13 #include "lldb/Utility/DataBuffer.h"
14 #include "lldb/Utility/StructuredData.h"
15 #include "lldb/lldb-enumerations.h"
16 #include "llvm/ADT/ArrayRef.h"
17 #include "llvm/Testing/Support/Error.h"
18 #include "gmock/gmock.h"
23 using namespace lldb_private::process_gdb_remote
;
24 using namespace lldb_private
;
30 typedef GDBRemoteCommunication::PacketResult PacketResult
;
32 struct TestClient
: public GDBRemoteCommunicationClient
{
33 TestClient() { m_send_acks
= false; }
36 void Handle_QThreadSuffixSupported(MockServer
&server
, bool supported
) {
37 StringExtractorGDBRemote request
;
38 ASSERT_EQ(PacketResult::Success
, server
.GetPacket(request
));
39 ASSERT_EQ("QThreadSuffixSupported", request
.GetStringRef());
41 ASSERT_EQ(PacketResult::Success
, server
.SendOKResponse());
43 ASSERT_EQ(PacketResult::Success
, server
.SendUnimplementedResponse(nullptr));
46 void HandlePacket(MockServer
&server
,
47 const testing::Matcher
<const std::string
&> &expected
,
49 StringExtractorGDBRemote request
;
50 ASSERT_EQ(PacketResult::Success
, server
.GetPacket(request
));
51 ASSERT_THAT(std::string(request
.GetStringRef()), expected
);
52 ASSERT_EQ(PacketResult::Success
, server
.SendPacket(response
));
55 uint8_t all_registers
[] = {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
56 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'};
57 std::string all_registers_hex
= "404142434445464748494a4b4c4d4e4f";
58 uint8_t one_register
[] = {'A', 'B', 'C', 'D'};
59 std::string one_register_hex
= "41424344";
61 } // end anonymous namespace
63 class GDBRemoteCommunicationClientTest
: public GDBRemoteTest
{
65 void SetUp() override
{
66 ASSERT_THAT_ERROR(GDBRemoteCommunication::ConnectLocally(client
, server
),
75 TEST_F(GDBRemoteCommunicationClientTest
, WriteRegister
) {
76 const lldb::tid_t tid
= 0x47;
77 const uint32_t reg_num
= 4;
78 std::future
<bool> write_result
= std::async(std::launch::async
, [&] {
79 return client
.WriteRegister(tid
, reg_num
, one_register
);
82 Handle_QThreadSuffixSupported(server
, true);
84 HandlePacket(server
, "P4=" + one_register_hex
+ ";thread:0047;", "OK");
85 ASSERT_TRUE(write_result
.get());
87 write_result
= std::async(std::launch::async
, [&] {
88 return client
.WriteAllRegisters(tid
, all_registers
);
91 HandlePacket(server
, "G" + all_registers_hex
+ ";thread:0047;", "OK");
92 ASSERT_TRUE(write_result
.get());
95 TEST_F(GDBRemoteCommunicationClientTest
, WriteRegisterNoSuffix
) {
96 const lldb::tid_t tid
= 0x47;
97 const uint32_t reg_num
= 4;
98 std::future
<bool> write_result
= std::async(std::launch::async
, [&] {
99 return client
.WriteRegister(tid
, reg_num
, one_register
);
102 Handle_QThreadSuffixSupported(server
, false);
103 HandlePacket(server
, "Hg47", "OK");
104 HandlePacket(server
, "P4=" + one_register_hex
, "OK");
105 ASSERT_TRUE(write_result
.get());
107 write_result
= std::async(std::launch::async
, [&] {
108 return client
.WriteAllRegisters(tid
, all_registers
);
111 HandlePacket(server
, "G" + all_registers_hex
, "OK");
112 ASSERT_TRUE(write_result
.get());
115 TEST_F(GDBRemoteCommunicationClientTest
, ReadRegister
) {
116 const lldb::tid_t tid
= 0x47;
117 const uint32_t reg_num
= 4;
118 std::future
<bool> async_result
= std::async(
119 std::launch::async
, [&] { return client
.GetpPacketSupported(tid
); });
120 Handle_QThreadSuffixSupported(server
, true);
121 HandlePacket(server
, "p0;thread:0047;", one_register_hex
);
122 ASSERT_TRUE(async_result
.get());
124 std::future
<DataBufferSP
> read_result
= std::async(
125 std::launch::async
, [&] { return client
.ReadRegister(tid
, reg_num
); });
126 HandlePacket(server
, "p4;thread:0047;", "41424344");
127 auto buffer_sp
= read_result
.get();
128 ASSERT_TRUE(bool(buffer_sp
));
130 memcmp(buffer_sp
->GetBytes(), one_register
, sizeof one_register
));
132 read_result
= std::async(std::launch::async
,
133 [&] { return client
.ReadAllRegisters(tid
); });
134 HandlePacket(server
, "g;thread:0047;", all_registers_hex
);
135 buffer_sp
= read_result
.get();
136 ASSERT_TRUE(bool(buffer_sp
));
138 memcmp(buffer_sp
->GetBytes(), all_registers
, sizeof all_registers
));
141 TEST_F(GDBRemoteCommunicationClientTest
, SaveRestoreRegistersNoSuffix
) {
142 const lldb::tid_t tid
= 0x47;
144 std::future
<bool> async_result
= std::async(std::launch::async
, [&] {
145 return client
.SaveRegisterState(tid
, save_id
);
147 Handle_QThreadSuffixSupported(server
, false);
148 HandlePacket(server
, "Hg47", "OK");
149 HandlePacket(server
, "QSaveRegisterState", "1");
150 ASSERT_TRUE(async_result
.get());
151 EXPECT_EQ(1u, save_id
);
153 async_result
= std::async(std::launch::async
, [&] {
154 return client
.RestoreRegisterState(tid
, save_id
);
156 HandlePacket(server
, "QRestoreRegisterState:1", "OK");
157 ASSERT_TRUE(async_result
.get());
160 TEST_F(GDBRemoteCommunicationClientTest
, SyncThreadState
) {
161 const lldb::tid_t tid
= 0x47;
162 std::future
<bool> async_result
= std::async(
163 std::launch::async
, [&] { return client
.SyncThreadState(tid
); });
164 HandlePacket(server
, "qSyncThreadStateSupported", "OK");
165 HandlePacket(server
, "QSyncThreadState:0047;", "OK");
166 ASSERT_TRUE(async_result
.get());
169 TEST_F(GDBRemoteCommunicationClientTest
, GetModulesInfo
) {
170 llvm::Triple
triple("i386-pc-linux");
172 FileSpec file_specs
[] = {
173 FileSpec("/foo/bar.so", FileSpec::Style::posix
),
174 FileSpec("/foo/baz.so", FileSpec::Style::posix
),
176 // This is a bit dodgy but we currently depend on GetModulesInfo not
177 // performing denormalization. It can go away once the users
178 // (DynamicLoaderPOSIXDYLD, at least) correctly set the path syntax for
179 // the FileSpecs they create.
180 FileSpec("/foo/baw.so", FileSpec::Style::windows
),
182 std::future
<std::optional
<std::vector
<ModuleSpec
>>> async_result
=
183 std::async(std::launch::async
,
184 [&] { return client
.GetModulesInfo(file_specs
, triple
); });
186 server
, "jModulesInfo:["
187 R
"({"file
":"/foo
/bar
.so
","triple
":"i386
-pc
-linux
"},)"
188 R
"({"file
":"/foo
/baz
.so
","triple
":"i386
-pc
-linux
"},)"
189 R
"({"file
":"/foo
/baw
.so
","triple
":"i386
-pc
-linux
"}])",
190 R
"([{"uuid
":"404142434445464748494a4b4c4d4e4f
","triple
":"i386
-pc
-linux
",)"
191 R
"("file_path
":"/foo
/bar
.so
","file_offset
":0,"file_size
":1234}]])");
193 auto result
= async_result
.get();
194 ASSERT_TRUE(result
.has_value());
195 ASSERT_EQ(1u, result
->size());
196 EXPECT_EQ("/foo/bar.so", (*result
)[0].GetFileSpec().GetPath());
197 EXPECT_EQ(triple
, (*result
)[0].GetArchitecture().GetTriple());
198 EXPECT_EQ(UUID("@ABCDEFGHIJKLMNO", 16), (*result
)[0].GetUUID());
199 EXPECT_EQ(0u, (*result
)[0].GetObjectOffset());
200 EXPECT_EQ(1234u, (*result
)[0].GetObjectSize());
203 TEST_F(GDBRemoteCommunicationClientTest
, GetModulesInfo_UUID20
) {
204 llvm::Triple
triple("i386-pc-linux");
206 FileSpec
file_spec("/foo/bar.so", FileSpec::Style::posix
);
207 std::future
<std::optional
<std::vector
<ModuleSpec
>>> async_result
=
208 std::async(std::launch::async
,
209 [&] { return client
.GetModulesInfo(file_spec
, triple
); });
213 R
"({"file
":"/foo
/bar
.so
","triple
":"i386
-pc
-linux
"}])",
214 R
"([{"uuid
":"404142434445464748494a4b4c4d4e4f50515253
","triple
":"i386
-pc
-linux
",)"
215 R
"("file_path
":"/foo
/bar
.so
","file_offset
":0,"file_size
":1234}]])");
217 auto result
= async_result
.get();
218 ASSERT_TRUE(result
.has_value());
219 ASSERT_EQ(1u, result
->size());
220 EXPECT_EQ("/foo/bar.so", (*result
)[0].GetFileSpec().GetPath());
221 EXPECT_EQ(triple
, (*result
)[0].GetArchitecture().GetTriple());
222 EXPECT_EQ(UUID("@ABCDEFGHIJKLMNOPQRS", 20), (*result
)[0].GetUUID());
223 EXPECT_EQ(0u, (*result
)[0].GetObjectOffset());
224 EXPECT_EQ(1234u, (*result
)[0].GetObjectSize());
227 TEST_F(GDBRemoteCommunicationClientTest
, GetModulesInfoInvalidResponse
) {
228 llvm::Triple
triple("i386-pc-linux");
229 FileSpec
file_spec("/foo/bar.so", FileSpec::Style::posix
);
231 const char *invalid_responses
[] = {
233 R
"([{"triple
":"i386
-pc
-linux
",)"
234 R
"("file_path
":"/foo
/bar
.so
","file_offset
":0,"file_size
":1234}]])",
236 R
"([{"uuid
":"XXXXXX
","triple
":"i386
-pc
-linux
",)"
237 R
"("file_path
":"/foo
/bar
.so
","file_offset
":0,"file_size
":1234}]])",
239 R
"([{"uuid
":"404142434445464748494a4b4c4d4e4f
",)"
240 R
"("file_path
":"/foo
/bar
.so
","file_offset
":0,"file_size
":1234}]])",
242 R
"([{"uuid
":"404142434445464748494a4b4c4d4e4f
","triple
":"i386
-pc
-linux
",)"
243 R
"("file_offset
":0,"file_size
":1234}]])",
245 R
"([{"uuid
":"404142434445464748494a4b4c4d4e4f
","triple
":"i386
-pc
-linux
",)"
246 R
"("file_path
":"/foo
/bar
.so
","file_size
":1234}]])",
248 R
"([{"uuid
":"404142434445464748494a4b4c4d4e4f
","triple
":"i386
-pc
-linux
",)"
249 R
"("file_path
":"/foo
/bar
.so
","file_offset
":0}]])",
252 for (const char *response
: invalid_responses
) {
253 std::future
<std::optional
<std::vector
<ModuleSpec
>>> async_result
=
254 std::async(std::launch::async
,
255 [&] { return client
.GetModulesInfo(file_spec
, triple
); });
258 R
"(jModulesInfo:[{"file
":"/foo
/bar
.so
","triple
":"i386
-pc
-linux
"}])",
261 auto result
= async_result
.get();
263 ASSERT_EQ(0u, result
->size()) << "response was: " << response
;
267 TEST_F(GDBRemoteCommunicationClientTest
, TestPacketSpeedJSON
) {
268 std::thread
server_thread([this] {
270 StringExtractorGDBRemote request
;
271 PacketResult result
= server
.GetPacket(request
);
272 if (result
== PacketResult::ErrorDisconnected
)
274 ASSERT_EQ(PacketResult::Success
, result
);
275 StringRef ref
= request
.GetStringRef();
276 ASSERT_TRUE(ref
.consume_front("qSpeedTest:response_size:"));
278 ASSERT_FALSE(ref
.consumeInteger(10, size
)) << "ref: " << ref
;
279 std::string
response(size
, 'X');
280 ASSERT_EQ(PacketResult::Success
, server
.SendPacket(response
));
285 client
.TestPacketSpeed(10, 32, 32, 4096, true, ss
);
287 server_thread
.join();
289 GTEST_LOG_(INFO
) << "Formatted output: " << ss
.GetData();
290 auto object_sp
= StructuredData::ParseJSON(ss
.GetString());
291 ASSERT_TRUE(bool(object_sp
));
292 auto dict_sp
= object_sp
->GetAsDictionary();
293 ASSERT_TRUE(bool(dict_sp
));
295 object_sp
= dict_sp
->GetValueForKey("packet_speeds");
296 ASSERT_TRUE(bool(object_sp
));
297 dict_sp
= object_sp
->GetAsDictionary();
298 ASSERT_TRUE(bool(dict_sp
));
301 ASSERT_TRUE(dict_sp
->GetValueForKeyAsInteger("num_packets", num_packets
))
303 ASSERT_EQ(10, (int)num_packets
);
306 TEST_F(GDBRemoteCommunicationClientTest
, SendSignalsToIgnore
) {
307 std::future
<Status
> result
= std::async(std::launch::async
, [&] {
308 return client
.SendSignalsToIgnore({2, 3, 5, 7, 0xB, 0xD, 0x11});
311 HandlePacket(server
, "QPassSignals:02;03;05;07;0b;0d;11", "OK");
312 EXPECT_TRUE(result
.get().Success());
314 result
= std::async(std::launch::async
, [&] {
315 return client
.SendSignalsToIgnore(std::vector
<int32_t>());
318 HandlePacket(server
, "QPassSignals:", "OK");
319 EXPECT_TRUE(result
.get().Success());
322 TEST_F(GDBRemoteCommunicationClientTest
, GetMemoryRegionInfo
) {
323 const lldb::addr_t addr
= 0xa000;
324 MemoryRegionInfo region_info
;
325 std::future
<Status
> result
= std::async(std::launch::async
, [&] {
326 return client
.GetMemoryRegionInfo(addr
, region_info
);
330 "qMemoryRegionInfo:a000",
331 "start:a000;size:2000;permissions:rx;name:2f666f6f2f6261722e736f;");
332 if (XMLDocument::XMLEnabled()) {
333 // In case we have XML support, this will also do a "qXfer:memory-map".
334 // Preceeded by a query for supported extensions. Pretend we don't support
336 HandlePacket(server
, testing::StartsWith("qSupported:"), "");
338 EXPECT_TRUE(result
.get().Success());
339 EXPECT_EQ(addr
, region_info
.GetRange().GetRangeBase());
340 EXPECT_EQ(0x2000u
, region_info
.GetRange().GetByteSize());
341 EXPECT_EQ(MemoryRegionInfo::eYes
, region_info
.GetReadable());
342 EXPECT_EQ(MemoryRegionInfo::eNo
, region_info
.GetWritable());
343 EXPECT_EQ(MemoryRegionInfo::eYes
, region_info
.GetExecutable());
344 EXPECT_EQ("/foo/bar.so", region_info
.GetName().GetStringRef());
345 EXPECT_EQ(MemoryRegionInfo::eDontKnow
, region_info
.GetMemoryTagged());
346 EXPECT_EQ(MemoryRegionInfo::eDontKnow
, region_info
.IsStackMemory());
348 result
= std::async(std::launch::async
, [&] {
349 return client
.GetMemoryRegionInfo(addr
, region_info
);
352 HandlePacket(server
, "qMemoryRegionInfo:a000",
353 "start:a000;size:2000;flags:;type:stack;");
354 EXPECT_TRUE(result
.get().Success());
355 EXPECT_EQ(MemoryRegionInfo::eNo
, region_info
.GetMemoryTagged());
356 EXPECT_EQ(MemoryRegionInfo::eYes
, region_info
.IsStackMemory());
358 result
= std::async(std::launch::async
, [&] {
359 return client
.GetMemoryRegionInfo(addr
, region_info
);
362 HandlePacket(server
, "qMemoryRegionInfo:a000",
363 "start:a000;size:2000;flags: mt zz mt ;type:ha,ha,stack;");
364 EXPECT_TRUE(result
.get().Success());
365 EXPECT_EQ(MemoryRegionInfo::eYes
, region_info
.GetMemoryTagged());
366 EXPECT_EQ(MemoryRegionInfo::eYes
, region_info
.IsStackMemory());
368 result
= std::async(std::launch::async
, [&] {
369 return client
.GetMemoryRegionInfo(addr
, region_info
);
372 HandlePacket(server
, "qMemoryRegionInfo:a000",
373 "start:a000;size:2000;type:heap;");
374 EXPECT_TRUE(result
.get().Success());
375 EXPECT_EQ(MemoryRegionInfo::eNo
, region_info
.IsStackMemory());
378 TEST_F(GDBRemoteCommunicationClientTest
, GetMemoryRegionInfoInvalidResponse
) {
379 const lldb::addr_t addr
= 0x4000;
380 MemoryRegionInfo region_info
;
381 std::future
<Status
> result
= std::async(std::launch::async
, [&] {
382 return client
.GetMemoryRegionInfo(addr
, region_info
);
385 HandlePacket(server
, "qMemoryRegionInfo:4000", "start:4000;size:0000;");
386 if (XMLDocument::XMLEnabled()) {
387 // In case we have XML support, this will also do a "qXfer:memory-map".
388 // Preceeded by a query for supported extensions. Pretend we don't support
390 HandlePacket(server
, testing::StartsWith("qSupported:"), "");
392 EXPECT_FALSE(result
.get().Success());
395 TEST_F(GDBRemoteCommunicationClientTest
, SendTraceSupportedPacket
) {
396 TraceSupportedResponse trace_type
;
397 std::string error_message
;
398 auto callback
= [&] {
399 std::chrono::seconds
timeout(10);
400 if (llvm::Expected
<TraceSupportedResponse
> trace_type_or_err
=
401 client
.SendTraceSupported(timeout
)) {
402 trace_type
= *trace_type_or_err
;
407 error_message
= llvm::toString(trace_type_or_err
.takeError());
414 std::future
<bool> result
= std::async(std::launch::async
, callback
);
417 server
, "jLLDBTraceSupported",
418 R
"({"name
":"intel
-pt
","description
":"Intel Processor Trace
"}])");
420 EXPECT_TRUE(result
.get());
421 ASSERT_STREQ(trace_type
.name
.c_str(), "intel-pt");
422 ASSERT_STREQ(trace_type
.description
.c_str(), "Intel Processor Trace");
425 // Error response - wrong json
427 std::future
<bool> result
= std::async(std::launch::async
, callback
);
429 HandlePacket(server
, "jLLDBTraceSupported", R
"({"type
":"intel
-pt
"}])");
431 EXPECT_FALSE(result
.get());
432 ASSERT_STREQ(error_message
.c_str(), "missing value at TraceSupportedResponse.description");
437 std::future
<bool> result
= std::async(std::launch::async
, callback
);
439 HandlePacket(server
, "jLLDBTraceSupported", "E23");
441 EXPECT_FALSE(result
.get());
444 // Error response with error message
446 std::future
<bool> result
= std::async(std::launch::async
, callback
);
448 HandlePacket(server
, "jLLDBTraceSupported",
449 "E23;50726F63657373206E6F742072756E6E696E672E");
451 EXPECT_FALSE(result
.get());
452 ASSERT_STREQ(error_message
.c_str(), "Process not running.");
456 TEST_F(GDBRemoteCommunicationClientTest
, GetQOffsets
) {
457 const auto &GetQOffsets
= [&](llvm::StringRef response
) {
458 std::future
<std::optional
<QOffsets
>> result
=
459 std::async(std::launch::async
, [&] { return client
.GetQOffsets(); });
461 HandlePacket(server
, "qOffsets", response
);
464 EXPECT_EQ((QOffsets
{false, {0x1234, 0x1234}}),
465 GetQOffsets("Text=1234;Data=1234"));
466 EXPECT_EQ((QOffsets
{false, {0x1234, 0x1234, 0x1234}}),
467 GetQOffsets("Text=1234;Data=1234;Bss=1234"));
468 EXPECT_EQ((QOffsets
{true, {0x1234}}), GetQOffsets("TextSeg=1234"));
469 EXPECT_EQ((QOffsets
{true, {0x1234, 0x2345}}),
470 GetQOffsets("TextSeg=1234;DataSeg=2345"));
472 EXPECT_EQ(std::nullopt
, GetQOffsets("E05"));
473 EXPECT_EQ(std::nullopt
, GetQOffsets("Text=bogus"));
474 EXPECT_EQ(std::nullopt
, GetQOffsets("Text=1234"));
475 EXPECT_EQ(std::nullopt
, GetQOffsets("Text=1234;Data=1234;"));
476 EXPECT_EQ(std::nullopt
, GetQOffsets("Text=1234;Data=1234;Bss=1234;"));
477 EXPECT_EQ(std::nullopt
, GetQOffsets("TEXTSEG=1234"));
478 EXPECT_EQ(std::nullopt
, GetQOffsets("TextSeg=0x1234"));
479 EXPECT_EQ(std::nullopt
, GetQOffsets("TextSeg=12345678123456789"));
483 check_qmemtags(TestClient
&client
, MockServer
&server
, size_t read_len
,
484 int32_t type
, const char *packet
, llvm::StringRef response
,
485 std::optional
<std::vector
<uint8_t>> expected_tag_data
) {
486 const auto &ReadMemoryTags
= [&]() {
487 std::future
<DataBufferSP
> result
= std::async(std::launch::async
, [&] {
488 return client
.ReadMemoryTags(0xDEF0, read_len
, type
);
491 HandlePacket(server
, packet
, response
);
495 auto result
= ReadMemoryTags();
496 if (expected_tag_data
) {
498 llvm::ArrayRef
<uint8_t> expected(*expected_tag_data
);
499 llvm::ArrayRef
<uint8_t> got
= result
->GetData();
500 ASSERT_THAT(expected
, testing::ContainerEq(got
));
502 ASSERT_FALSE(result
);
506 TEST_F(GDBRemoteCommunicationClientTest
, ReadMemoryTags
) {
507 // Zero length reads are valid
508 check_qmemtags(client
, server
, 0, 1, "qMemTags:def0,0:1", "m",
509 std::vector
<uint8_t>{});
511 // Type can be negative. Put into the packet as the raw bytes
512 // (as opposed to a literal -1)
513 check_qmemtags(client
, server
, 0, -1, "qMemTags:def0,0:ffffffff", "m",
514 std::vector
<uint8_t>{});
515 check_qmemtags(client
, server
, 0, std::numeric_limits
<int32_t>::min(),
516 "qMemTags:def0,0:80000000", "m", std::vector
<uint8_t>{});
517 check_qmemtags(client
, server
, 0, std::numeric_limits
<int32_t>::max(),
518 "qMemTags:def0,0:7fffffff", "m", std::vector
<uint8_t>{});
520 // The client layer does not check the length of the received data.
521 // All we need is the "m" and for the decode to use all of the chars
522 check_qmemtags(client
, server
, 32, 2, "qMemTags:def0,20:2", "m09",
523 std::vector
<uint8_t>{0x9});
525 // Zero length response is fine as long as the "m" is present
526 check_qmemtags(client
, server
, 0, 0x34, "qMemTags:def0,0:34", "m",
527 std::vector
<uint8_t>{});
530 check_qmemtags(client
, server
, 16, 1, "qMemTags:def0,10:1", "m66",
531 std::vector
<uint8_t>{0x66});
532 check_qmemtags(client
, server
, 32, 1, "qMemTags:def0,20:1", "m0102",
533 std::vector
<uint8_t>{0x1, 0x2});
535 // Empty response is an error
536 check_qmemtags(client
, server
, 17, 1, "qMemTags:def0,11:1", "", std::nullopt
);
537 // Usual error response
538 check_qmemtags(client
, server
, 17, 1, "qMemTags:def0,11:1", "E01",
541 check_qmemtags(client
, server
, 17, 1, "qMemTags:def0,11:1", "01",
543 // Anything other than m is an error
544 check_qmemtags(client
, server
, 17, 1, "qMemTags:def0,11:1", "z01",
546 // Decoding tag data doesn't use all the chars in the packet
547 check_qmemtags(client
, server
, 32, 1, "qMemTags:def0,20:1", "m09zz",
549 // Data that is not hex bytes
550 check_qmemtags(client
, server
, 32, 1, "qMemTags:def0,20:1", "mhello",
552 // Data is not a complete hex char
553 check_qmemtags(client
, server
, 32, 1, "qMemTags:def0,20:1", "m9",
555 // Data has a trailing hex char
556 check_qmemtags(client
, server
, 32, 1, "qMemTags:def0,20:1", "m01020",
560 static void check_Qmemtags(TestClient
&client
, MockServer
&server
,
561 lldb::addr_t addr
, size_t len
, int32_t type
,
562 const std::vector
<uint8_t> &tags
, const char *packet
,
563 llvm::StringRef response
, bool should_succeed
) {
564 const auto &WriteMemoryTags
= [&]() {
565 std::future
<Status
> result
= std::async(std::launch::async
, [&] {
566 return client
.WriteMemoryTags(addr
, len
, type
, tags
);
569 HandlePacket(server
, packet
, response
);
573 auto result
= WriteMemoryTags();
575 ASSERT_TRUE(result
.Success());
577 ASSERT_TRUE(result
.Fail());
580 TEST_F(GDBRemoteCommunicationClientTest
, WriteMemoryTags
) {
581 check_Qmemtags(client
, server
, 0xABCD, 0x20, 1,
582 std::vector
<uint8_t>{0x12, 0x34}, "QMemTags:abcd,20:1:1234",
585 // The GDB layer doesn't care that the number of tags !=
586 // the length of the write.
587 check_Qmemtags(client
, server
, 0x4321, 0x20, 9, std::vector
<uint8_t>{},
588 "QMemTags:4321,20:9:", "OK", true);
590 check_Qmemtags(client
, server
, 0x8877, 0x123, 0x34,
591 std::vector
<uint8_t>{0x55, 0x66, 0x77},
592 "QMemTags:8877,123:34:556677", "E01", false);
594 // Type is a signed integer but is packed as its raw bytes,
595 // instead of having a +/-.
596 check_Qmemtags(client
, server
, 0x456789, 0, -1, std::vector
<uint8_t>{0x99},
597 "QMemTags:456789,0:ffffffff:99", "E03", false);
598 check_Qmemtags(client
, server
, 0x456789, 0,
599 std::numeric_limits
<int32_t>::max(),
600 std::vector
<uint8_t>{0x99}, "QMemTags:456789,0:7fffffff:99",
602 check_Qmemtags(client
, server
, 0x456789, 0,
603 std::numeric_limits
<int32_t>::min(),
604 std::vector
<uint8_t>{0x99}, "QMemTags:456789,0:80000000:99",
608 // Prior to this verison, constructing a std::future for a type without a
609 // default constructor is not possible.
610 // https://developercommunity.visualstudio.com/t/c-shared-state-futuresstate-default-constructs-the/60897
611 #if !defined(_MSC_VER) || _MSC_VER >= 1932
612 TEST_F(GDBRemoteCommunicationClientTest
, CalculateMD5
) {
613 FileSpec
file_spec("/foo/bar", FileSpec::Style::posix
);
614 std::future
<ErrorOr
<MD5::MD5Result
>> async_result
= std::async(
615 std::launch::async
, [&] { return client
.CalculateMD5(file_spec
); });
617 lldb_private::StreamString stream
;
618 stream
.PutCString("vFile:MD5:");
619 stream
.PutStringAsRawHex8(file_spec
.GetPath(false));
620 HandlePacket(server
, stream
.GetString().str(),
624 auto result
= async_result
.get();
626 // Server and client puts/parses low, and then high
627 const uint64_t expected_low
= 0xdeadbeef01020304;
628 const uint64_t expected_high
= 0x05060708deadbeef;
630 EXPECT_EQ(expected_low
, result
->low());
631 EXPECT_EQ(expected_high
, result
->high());