xref: /llvm-project/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp (revision b1751faada35e3456b2a3f6b6c9559b5d74d559b)
1 //===-- GDBRemoteCommunicationClientTest.cpp ------------------------------===//
2 //
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
6 //
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"
19 #include <future>
20 #include <limits>
21 #include <optional>
22 
23 using namespace lldb_private::process_gdb_remote;
24 using namespace lldb_private;
25 using namespace lldb;
26 using namespace llvm;
27 
28 namespace {
29 
30 typedef GDBRemoteCommunication::PacketResult PacketResult;
31 
32 struct TestClient : public GDBRemoteCommunicationClient {
33   TestClient() { m_send_acks = false; }
34 };
35 
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());
40   if (supported)
41     ASSERT_EQ(PacketResult::Success, server.SendOKResponse());
42   else
43     ASSERT_EQ(PacketResult::Success, server.SendUnimplementedResponse(nullptr));
44 }
45 
46 void HandlePacket(MockServer &server,
47                   const testing::Matcher<const std::string &> &expected,
48                   StringRef response) {
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));
53 }
54 
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";
60 
61 } // end anonymous namespace
62 
63 class GDBRemoteCommunicationClientTest : public GDBRemoteTest {
64 public:
65   void SetUp() override {
66     ASSERT_THAT_ERROR(GDBRemoteCommunication::ConnectLocally(client, server),
67                       llvm::Succeeded());
68   }
69 
70 protected:
71   TestClient client;
72   MockServer server;
73 };
74 
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);
80   });
81 
82   Handle_QThreadSuffixSupported(server, true);
83 
84   HandlePacket(server, "P4=" + one_register_hex + ";thread:0047;", "OK");
85   ASSERT_TRUE(write_result.get());
86 
87   write_result = std::async(std::launch::async, [&] {
88     return client.WriteAllRegisters(tid, all_registers);
89   });
90 
91   HandlePacket(server, "G" + all_registers_hex + ";thread:0047;", "OK");
92   ASSERT_TRUE(write_result.get());
93 }
94 
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);
100   });
101 
102   Handle_QThreadSuffixSupported(server, false);
103   HandlePacket(server, "Hg47", "OK");
104   HandlePacket(server, "P4=" + one_register_hex, "OK");
105   ASSERT_TRUE(write_result.get());
106 
107   write_result = std::async(std::launch::async, [&] {
108     return client.WriteAllRegisters(tid, all_registers);
109   });
110 
111   HandlePacket(server, "G" + all_registers_hex, "OK");
112   ASSERT_TRUE(write_result.get());
113 }
114 
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());
123 
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));
129   ASSERT_EQ(0,
130             memcmp(buffer_sp->GetBytes(), one_register, sizeof one_register));
131 
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));
137   ASSERT_EQ(0,
138             memcmp(buffer_sp->GetBytes(), all_registers, sizeof all_registers));
139 }
140 
141 TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) {
142   const lldb::tid_t tid = 0x47;
143   uint32_t save_id;
144   std::future<bool> async_result = std::async(std::launch::async, [&] {
145     return client.SaveRegisterState(tid, save_id);
146   });
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);
152 
153   async_result = std::async(std::launch::async, [&] {
154     return client.RestoreRegisterState(tid, save_id);
155   });
156   HandlePacket(server, "QRestoreRegisterState:1", "OK");
157   ASSERT_TRUE(async_result.get());
158 }
159 
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());
167 }
168 
169 TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo) {
170   llvm::Triple triple("i386-pc-linux");
171 
172   FileSpec file_specs[] = {
173       FileSpec("/foo/bar.so", FileSpec::Style::posix),
174       FileSpec("/foo/baz.so", FileSpec::Style::posix),
175 
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),
181   };
182   std::future<std::optional<std::vector<ModuleSpec>>> async_result =
183       std::async(std::launch::async,
184                  [&] { return client.GetModulesInfo(file_specs, triple); });
185   HandlePacket(
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}]])");
192 
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());
201 }
202 
203 TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo_UUID20) {
204   llvm::Triple triple("i386-pc-linux");
205 
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); });
210   HandlePacket(
211       server,
212       "jModulesInfo:["
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}]])");
216 
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());
225 }
226 
227 TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse) {
228   llvm::Triple triple("i386-pc-linux");
229   FileSpec file_spec("/foo/bar.so", FileSpec::Style::posix);
230 
231   const char *invalid_responses[] = {
232       // no UUID
233       R"([{"triple":"i386-pc-linux",)"
234       R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",
235       // invalid UUID
236       R"([{"uuid":"XXXXXX","triple":"i386-pc-linux",)"
237       R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",
238       // no triple
239       R"([{"uuid":"404142434445464748494a4b4c4d4e4f",)"
240       R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",
241       // no file_path
242       R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
243       R"("file_offset":0,"file_size":1234}]])",
244       // no file_offset
245       R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
246       R"("file_path":"/foo/bar.so","file_size":1234}]])",
247       // no file_size
248       R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
249       R"("file_path":"/foo/bar.so","file_offset":0}]])",
250   };
251 
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); });
256     HandlePacket(
257         server,
258         R"(jModulesInfo:[{"file":"/foo/bar.so","triple":"i386-pc-linux"}])",
259         response);
260 
261     auto result = async_result.get();
262     ASSERT_TRUE(result);
263     ASSERT_EQ(0u, result->size()) << "response was: " << response;
264   }
265 }
266 
267 TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) {
268   std::thread server_thread([this] {
269     for (;;) {
270       StringExtractorGDBRemote request;
271       PacketResult result = server.GetPacket(request);
272       if (result == PacketResult::ErrorDisconnected)
273         return;
274       ASSERT_EQ(PacketResult::Success, result);
275       StringRef ref = request.GetStringRef();
276       ASSERT_TRUE(ref.consume_front("qSpeedTest:response_size:"));
277       int size;
278       ASSERT_FALSE(ref.consumeInteger(10, size)) << "ref: " << ref;
279       std::string response(size, 'X');
280       ASSERT_EQ(PacketResult::Success, server.SendPacket(response));
281     }
282   });
283 
284   StreamString ss;
285   client.TestPacketSpeed(10, 32, 32, 4096, true, ss);
286   client.Disconnect();
287   server_thread.join();
288 
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));
294 
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));
299 
300   size_t num_packets;
301   ASSERT_TRUE(dict_sp->GetValueForKeyAsInteger("num_packets", num_packets))
302       << ss.GetString();
303   ASSERT_EQ(10, (int)num_packets);
304 }
305 
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});
309   });
310 
311   HandlePacket(server, "QPassSignals:02;03;05;07;0b;0d;11", "OK");
312   EXPECT_TRUE(result.get().Success());
313 
314   result = std::async(std::launch::async, [&] {
315     return client.SendSignalsToIgnore(std::vector<int32_t>());
316   });
317 
318   HandlePacket(server, "QPassSignals:", "OK");
319   EXPECT_TRUE(result.get().Success());
320 }
321 
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);
327   });
328 
329   HandlePacket(server,
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
335     // that.
336     HandlePacket(server, testing::StartsWith("qSupported:"), "");
337   }
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());
347   EXPECT_EQ(MemoryRegionInfo::eDontKnow, region_info.IsShadowStack());
348 
349   result = std::async(std::launch::async, [&] {
350     return client.GetMemoryRegionInfo(addr, region_info);
351   });
352 
353   HandlePacket(server, "qMemoryRegionInfo:a000",
354                "start:a000;size:2000;flags:;type:stack;");
355   EXPECT_TRUE(result.get().Success());
356   EXPECT_EQ(MemoryRegionInfo::eNo, region_info.GetMemoryTagged());
357   EXPECT_EQ(MemoryRegionInfo::eYes, region_info.IsStackMemory());
358   EXPECT_EQ(MemoryRegionInfo::eNo, region_info.IsShadowStack());
359 
360   result = std::async(std::launch::async, [&] {
361     return client.GetMemoryRegionInfo(addr, region_info);
362   });
363 
364   HandlePacket(server, "qMemoryRegionInfo:a000",
365                "start:a000;size:2000;flags: mt  zz mt ss  ;type:ha,ha,stack;");
366   EXPECT_TRUE(result.get().Success());
367   EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetMemoryTagged());
368   EXPECT_EQ(MemoryRegionInfo::eYes, region_info.IsStackMemory());
369   EXPECT_EQ(MemoryRegionInfo::eYes, region_info.IsShadowStack());
370 
371   result = std::async(std::launch::async, [&] {
372     return client.GetMemoryRegionInfo(addr, region_info);
373   });
374 
375   HandlePacket(server, "qMemoryRegionInfo:a000",
376                "start:a000;size:2000;type:heap;");
377   EXPECT_TRUE(result.get().Success());
378   EXPECT_EQ(MemoryRegionInfo::eNo, region_info.IsStackMemory());
379 }
380 
381 TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) {
382   const lldb::addr_t addr = 0x4000;
383   MemoryRegionInfo region_info;
384   std::future<Status> result = std::async(std::launch::async, [&] {
385     return client.GetMemoryRegionInfo(addr, region_info);
386   });
387 
388   HandlePacket(server, "qMemoryRegionInfo:4000", "start:4000;size:0000;");
389   if (XMLDocument::XMLEnabled()) {
390     // In case we have XML support, this will also do a "qXfer:memory-map".
391     // Preceeded by a query for supported extensions. Pretend we don't support
392     // that.
393     HandlePacket(server, testing::StartsWith("qSupported:"), "");
394   }
395   EXPECT_FALSE(result.get().Success());
396 }
397 
398 TEST_F(GDBRemoteCommunicationClientTest, SendTraceSupportedPacket) {
399   TraceSupportedResponse trace_type;
400   std::string error_message;
401   auto callback = [&] {
402     std::chrono::seconds timeout(10);
403     if (llvm::Expected<TraceSupportedResponse> trace_type_or_err =
404             client.SendTraceSupported(timeout)) {
405       trace_type = *trace_type_or_err;
406       error_message = "";
407       return true;
408     } else {
409       trace_type = {};
410       error_message = llvm::toString(trace_type_or_err.takeError());
411       return false;
412     }
413   };
414 
415   // Success response
416   {
417     std::future<bool> result = std::async(std::launch::async, callback);
418 
419     HandlePacket(
420         server, "jLLDBTraceSupported",
421         R"({"name":"intel-pt","description":"Intel Processor Trace"}])");
422 
423     EXPECT_TRUE(result.get());
424     ASSERT_STREQ(trace_type.name.c_str(), "intel-pt");
425     ASSERT_STREQ(trace_type.description.c_str(), "Intel Processor Trace");
426   }
427 
428   // Error response - wrong json
429   {
430     std::future<bool> result = std::async(std::launch::async, callback);
431 
432     HandlePacket(server, "jLLDBTraceSupported", R"({"type":"intel-pt"}])");
433 
434     EXPECT_FALSE(result.get());
435     ASSERT_STREQ(error_message.c_str(), "missing value at TraceSupportedResponse.description");
436   }
437 
438   // Error response
439   {
440     std::future<bool> result = std::async(std::launch::async, callback);
441 
442     HandlePacket(server, "jLLDBTraceSupported", "E23");
443 
444     EXPECT_FALSE(result.get());
445   }
446 
447   // Error response with error message
448   {
449     std::future<bool> result = std::async(std::launch::async, callback);
450 
451     HandlePacket(server, "jLLDBTraceSupported",
452                  "E23;50726F63657373206E6F742072756E6E696E672E");
453 
454     EXPECT_FALSE(result.get());
455     ASSERT_STREQ(error_message.c_str(), "Process not running.");
456   }
457 }
458 
459 TEST_F(GDBRemoteCommunicationClientTest, GetQOffsets) {
460   const auto &GetQOffsets = [&](llvm::StringRef response) {
461     std::future<std::optional<QOffsets>> result =
462         std::async(std::launch::async, [&] { return client.GetQOffsets(); });
463 
464     HandlePacket(server, "qOffsets", response);
465     return result.get();
466   };
467   EXPECT_EQ((QOffsets{false, {0x1234, 0x1234}}),
468             GetQOffsets("Text=1234;Data=1234"));
469   EXPECT_EQ((QOffsets{false, {0x1234, 0x1234, 0x1234}}),
470             GetQOffsets("Text=1234;Data=1234;Bss=1234"));
471   EXPECT_EQ((QOffsets{true, {0x1234}}), GetQOffsets("TextSeg=1234"));
472   EXPECT_EQ((QOffsets{true, {0x1234, 0x2345}}),
473             GetQOffsets("TextSeg=1234;DataSeg=2345"));
474 
475   EXPECT_EQ(std::nullopt, GetQOffsets("E05"));
476   EXPECT_EQ(std::nullopt, GetQOffsets("Text=bogus"));
477   EXPECT_EQ(std::nullopt, GetQOffsets("Text=1234"));
478   EXPECT_EQ(std::nullopt, GetQOffsets("Text=1234;Data=1234;"));
479   EXPECT_EQ(std::nullopt, GetQOffsets("Text=1234;Data=1234;Bss=1234;"));
480   EXPECT_EQ(std::nullopt, GetQOffsets("TEXTSEG=1234"));
481   EXPECT_EQ(std::nullopt, GetQOffsets("TextSeg=0x1234"));
482   EXPECT_EQ(std::nullopt, GetQOffsets("TextSeg=12345678123456789"));
483 }
484 
485 static void
486 check_qmemtags(TestClient &client, MockServer &server, size_t read_len,
487                int32_t type, const char *packet, llvm::StringRef response,
488                std::optional<std::vector<uint8_t>> expected_tag_data) {
489   const auto &ReadMemoryTags = [&]() {
490     std::future<DataBufferSP> result = std::async(std::launch::async, [&] {
491       return client.ReadMemoryTags(0xDEF0, read_len, type);
492     });
493 
494     HandlePacket(server, packet, response);
495     return result.get();
496   };
497 
498   auto result = ReadMemoryTags();
499   if (expected_tag_data) {
500     ASSERT_TRUE(result);
501     llvm::ArrayRef<uint8_t> expected(*expected_tag_data);
502     llvm::ArrayRef<uint8_t> got = result->GetData();
503     ASSERT_THAT(expected, testing::ContainerEq(got));
504   } else {
505     ASSERT_FALSE(result);
506   }
507 }
508 
509 TEST_F(GDBRemoteCommunicationClientTest, ReadMemoryTags) {
510   // Zero length reads are valid
511   check_qmemtags(client, server, 0, 1, "qMemTags:def0,0:1", "m",
512                  std::vector<uint8_t>{});
513 
514   // Type can be negative. Put into the packet as the raw bytes
515   // (as opposed to a literal -1)
516   check_qmemtags(client, server, 0, -1, "qMemTags:def0,0:ffffffff", "m",
517                  std::vector<uint8_t>{});
518   check_qmemtags(client, server, 0, std::numeric_limits<int32_t>::min(),
519                  "qMemTags:def0,0:80000000", "m", std::vector<uint8_t>{});
520   check_qmemtags(client, server, 0, std::numeric_limits<int32_t>::max(),
521                  "qMemTags:def0,0:7fffffff", "m", std::vector<uint8_t>{});
522 
523   // The client layer does not check the length of the received data.
524   // All we need is the "m" and for the decode to use all of the chars
525   check_qmemtags(client, server, 32, 2, "qMemTags:def0,20:2", "m09",
526                  std::vector<uint8_t>{0x9});
527 
528   // Zero length response is fine as long as the "m" is present
529   check_qmemtags(client, server, 0, 0x34, "qMemTags:def0,0:34", "m",
530                  std::vector<uint8_t>{});
531 
532   // Normal responses
533   check_qmemtags(client, server, 16, 1, "qMemTags:def0,10:1", "m66",
534                  std::vector<uint8_t>{0x66});
535   check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m0102",
536                  std::vector<uint8_t>{0x1, 0x2});
537 
538   // Empty response is an error
539   check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "", std::nullopt);
540   // Usual error response
541   check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "E01",
542                  std::nullopt);
543   // Leading m missing
544   check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "01",
545                  std::nullopt);
546   // Anything other than m is an error
547   check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "z01",
548                  std::nullopt);
549   // Decoding tag data doesn't use all the chars in the packet
550   check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m09zz",
551                  std::nullopt);
552   // Data that is not hex bytes
553   check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "mhello",
554                  std::nullopt);
555   // Data is not a complete hex char
556   check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m9",
557                  std::nullopt);
558   // Data has a trailing hex char
559   check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m01020",
560                  std::nullopt);
561 }
562 
563 static void check_Qmemtags(TestClient &client, MockServer &server,
564                            lldb::addr_t addr, size_t len, int32_t type,
565                            const std::vector<uint8_t> &tags, const char *packet,
566                            llvm::StringRef response, bool should_succeed) {
567   const auto &WriteMemoryTags = [&]() {
568     std::future<Status> result = std::async(std::launch::async, [&] {
569       return client.WriteMemoryTags(addr, len, type, tags);
570     });
571 
572     HandlePacket(server, packet, response);
573     return result.get();
574   };
575 
576   auto result = WriteMemoryTags();
577   if (should_succeed)
578     ASSERT_TRUE(result.Success());
579   else
580     ASSERT_TRUE(result.Fail());
581 }
582 
583 TEST_F(GDBRemoteCommunicationClientTest, WriteMemoryTags) {
584   check_Qmemtags(client, server, 0xABCD, 0x20, 1,
585                  std::vector<uint8_t>{0x12, 0x34}, "QMemTags:abcd,20:1:1234",
586                  "OK", true);
587 
588   // The GDB layer doesn't care that the number of tags !=
589   // the length of the write.
590   check_Qmemtags(client, server, 0x4321, 0x20, 9, std::vector<uint8_t>{},
591                  "QMemTags:4321,20:9:", "OK", true);
592 
593   check_Qmemtags(client, server, 0x8877, 0x123, 0x34,
594                  std::vector<uint8_t>{0x55, 0x66, 0x77},
595                  "QMemTags:8877,123:34:556677", "E01", false);
596 
597   // Type is a signed integer but is packed as its raw bytes,
598   // instead of having a +/-.
599   check_Qmemtags(client, server, 0x456789, 0, -1, std::vector<uint8_t>{0x99},
600                  "QMemTags:456789,0:ffffffff:99", "E03", false);
601   check_Qmemtags(client, server, 0x456789, 0,
602                  std::numeric_limits<int32_t>::max(),
603                  std::vector<uint8_t>{0x99}, "QMemTags:456789,0:7fffffff:99",
604                  "E03", false);
605   check_Qmemtags(client, server, 0x456789, 0,
606                  std::numeric_limits<int32_t>::min(),
607                  std::vector<uint8_t>{0x99}, "QMemTags:456789,0:80000000:99",
608                  "E03", false);
609 }
610 
611 // Prior to this verison, constructing a std::future for a type without a
612 // default constructor is not possible.
613 // https://developercommunity.visualstudio.com/t/c-shared-state-futuresstate-default-constructs-the/60897
614 #if !defined(_MSC_VER) || _MSC_VER >= 1932
615 TEST_F(GDBRemoteCommunicationClientTest, CalculateMD5) {
616   FileSpec file_spec("/foo/bar", FileSpec::Style::posix);
617   std::future<ErrorOr<MD5::MD5Result>> async_result = std::async(
618       std::launch::async, [&] { return client.CalculateMD5(file_spec); });
619 
620   lldb_private::StreamString stream;
621   stream.PutCString("vFile:MD5:");
622   stream.PutStringAsRawHex8(file_spec.GetPath(false));
623   HandlePacket(server, stream.GetString().str(),
624                "F,"
625                "deadbeef01020304"
626                "05060708deadbeef");
627   auto result = async_result.get();
628 
629   // Server and client puts/parses low, and then high
630   const uint64_t expected_low = 0xdeadbeef01020304;
631   const uint64_t expected_high = 0x05060708deadbeef;
632   ASSERT_TRUE(result);
633   EXPECT_EQ(expected_low, result->low());
634   EXPECT_EQ(expected_high, result->high());
635 }
636 #endif
637