xref: /llvm-project/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp (revision 9464bd8c78d142225957abbf38ed3c2abaa9e180)
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 
22 using namespace lldb_private::process_gdb_remote;
23 using namespace lldb_private;
24 using namespace lldb;
25 using namespace llvm;
26 
27 namespace {
28 
29 typedef GDBRemoteCommunication::PacketResult PacketResult;
30 
31 struct TestClient : public GDBRemoteCommunicationClient {
32   TestClient() { m_send_acks = false; }
33 };
34 
35 void Handle_QThreadSuffixSupported(MockServer &server, bool supported) {
36   StringExtractorGDBRemote request;
37   ASSERT_EQ(PacketResult::Success, server.GetPacket(request));
38   ASSERT_EQ("QThreadSuffixSupported", request.GetStringRef());
39   if (supported)
40     ASSERT_EQ(PacketResult::Success, server.SendOKResponse());
41   else
42     ASSERT_EQ(PacketResult::Success, server.SendUnimplementedResponse(nullptr));
43 }
44 
45 void HandlePacket(MockServer &server,
46                   const testing::Matcher<const std::string &> &expected,
47                   StringRef response) {
48   StringExtractorGDBRemote request;
49   ASSERT_EQ(PacketResult::Success, server.GetPacket(request));
50   ASSERT_THAT(std::string(request.GetStringRef()), expected);
51   ASSERT_EQ(PacketResult::Success, server.SendPacket(response));
52 }
53 
54 uint8_t all_registers[] = {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
55                            'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'};
56 std::string all_registers_hex = "404142434445464748494a4b4c4d4e4f";
57 uint8_t one_register[] = {'A', 'B', 'C', 'D'};
58 std::string one_register_hex = "41424344";
59 
60 } // end anonymous namespace
61 
62 class GDBRemoteCommunicationClientTest : public GDBRemoteTest {
63 public:
64   void SetUp() override {
65     ASSERT_THAT_ERROR(GDBRemoteCommunication::ConnectLocally(client, server),
66                       llvm::Succeeded());
67   }
68 
69 protected:
70   TestClient client;
71   MockServer server;
72 };
73 
74 TEST_F(GDBRemoteCommunicationClientTest, WriteRegister) {
75   const lldb::tid_t tid = 0x47;
76   const uint32_t reg_num = 4;
77   std::future<bool> write_result = std::async(std::launch::async, [&] {
78     return client.WriteRegister(tid, reg_num, one_register);
79   });
80 
81   Handle_QThreadSuffixSupported(server, true);
82 
83   HandlePacket(server, "P4=" + one_register_hex + ";thread:0047;", "OK");
84   ASSERT_TRUE(write_result.get());
85 
86   write_result = std::async(std::launch::async, [&] {
87     return client.WriteAllRegisters(tid, all_registers);
88   });
89 
90   HandlePacket(server, "G" + all_registers_hex + ";thread:0047;", "OK");
91   ASSERT_TRUE(write_result.get());
92 }
93 
94 TEST_F(GDBRemoteCommunicationClientTest, WriteRegisterNoSuffix) {
95   const lldb::tid_t tid = 0x47;
96   const uint32_t reg_num = 4;
97   std::future<bool> write_result = std::async(std::launch::async, [&] {
98     return client.WriteRegister(tid, reg_num, one_register);
99   });
100 
101   Handle_QThreadSuffixSupported(server, false);
102   HandlePacket(server, "Hg47", "OK");
103   HandlePacket(server, "P4=" + one_register_hex, "OK");
104   ASSERT_TRUE(write_result.get());
105 
106   write_result = std::async(std::launch::async, [&] {
107     return client.WriteAllRegisters(tid, all_registers);
108   });
109 
110   HandlePacket(server, "G" + all_registers_hex, "OK");
111   ASSERT_TRUE(write_result.get());
112 }
113 
114 TEST_F(GDBRemoteCommunicationClientTest, ReadRegister) {
115   const lldb::tid_t tid = 0x47;
116   const uint32_t reg_num = 4;
117   std::future<bool> async_result = std::async(
118       std::launch::async, [&] { return client.GetpPacketSupported(tid); });
119   Handle_QThreadSuffixSupported(server, true);
120   HandlePacket(server, "p0;thread:0047;", one_register_hex);
121   ASSERT_TRUE(async_result.get());
122 
123   std::future<DataBufferSP> read_result = std::async(
124       std::launch::async, [&] { return client.ReadRegister(tid, reg_num); });
125   HandlePacket(server, "p4;thread:0047;", "41424344");
126   auto buffer_sp = read_result.get();
127   ASSERT_TRUE(bool(buffer_sp));
128   ASSERT_EQ(0,
129             memcmp(buffer_sp->GetBytes(), one_register, sizeof one_register));
130 
131   read_result = std::async(std::launch::async,
132                            [&] { return client.ReadAllRegisters(tid); });
133   HandlePacket(server, "g;thread:0047;", all_registers_hex);
134   buffer_sp = read_result.get();
135   ASSERT_TRUE(bool(buffer_sp));
136   ASSERT_EQ(0,
137             memcmp(buffer_sp->GetBytes(), all_registers, sizeof all_registers));
138 }
139 
140 TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) {
141   const lldb::tid_t tid = 0x47;
142   uint32_t save_id;
143   std::future<bool> async_result = std::async(std::launch::async, [&] {
144     return client.SaveRegisterState(tid, save_id);
145   });
146   Handle_QThreadSuffixSupported(server, false);
147   HandlePacket(server, "Hg47", "OK");
148   HandlePacket(server, "QSaveRegisterState", "1");
149   ASSERT_TRUE(async_result.get());
150   EXPECT_EQ(1u, save_id);
151 
152   async_result = std::async(std::launch::async, [&] {
153     return client.RestoreRegisterState(tid, save_id);
154   });
155   HandlePacket(server, "QRestoreRegisterState:1", "OK");
156   ASSERT_TRUE(async_result.get());
157 }
158 
159 TEST_F(GDBRemoteCommunicationClientTest, SyncThreadState) {
160   const lldb::tid_t tid = 0x47;
161   std::future<bool> async_result = std::async(
162       std::launch::async, [&] { return client.SyncThreadState(tid); });
163   HandlePacket(server, "qSyncThreadStateSupported", "OK");
164   HandlePacket(server, "QSyncThreadState:0047;", "OK");
165   ASSERT_TRUE(async_result.get());
166 }
167 
168 TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo) {
169   llvm::Triple triple("i386-pc-linux");
170 
171   FileSpec file_specs[] = {
172       FileSpec("/foo/bar.so", FileSpec::Style::posix),
173       FileSpec("/foo/baz.so", FileSpec::Style::posix),
174 
175       // This is a bit dodgy but we currently depend on GetModulesInfo not
176       // performing denormalization. It can go away once the users
177       // (DynamicLoaderPOSIXDYLD, at least) correctly set the path syntax for
178       // the FileSpecs they create.
179       FileSpec("/foo/baw.so", FileSpec::Style::windows),
180   };
181   std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result =
182       std::async(std::launch::async,
183                  [&] { return client.GetModulesInfo(file_specs, triple); });
184   HandlePacket(
185       server, "jModulesInfo:["
186               R"({"file":"/foo/bar.so","triple":"i386-pc-linux"},)"
187               R"({"file":"/foo/baz.so","triple":"i386-pc-linux"},)"
188               R"({"file":"/foo/baw.so","triple":"i386-pc-linux"}])",
189       R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
190       R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])");
191 
192   auto result = async_result.get();
193   ASSERT_TRUE(result.has_value());
194   ASSERT_EQ(1u, result->size());
195   EXPECT_EQ("/foo/bar.so", (*result)[0].GetFileSpec().GetPath());
196   EXPECT_EQ(triple, (*result)[0].GetArchitecture().GetTriple());
197   EXPECT_EQ(UUID("@ABCDEFGHIJKLMNO", 16), (*result)[0].GetUUID());
198   EXPECT_EQ(0u, (*result)[0].GetObjectOffset());
199   EXPECT_EQ(1234u, (*result)[0].GetObjectSize());
200 }
201 
202 TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo_UUID20) {
203   llvm::Triple triple("i386-pc-linux");
204 
205   FileSpec file_spec("/foo/bar.so", FileSpec::Style::posix);
206   std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result =
207       std::async(std::launch::async,
208                  [&] { return client.GetModulesInfo(file_spec, triple); });
209   HandlePacket(
210       server,
211       "jModulesInfo:["
212       R"({"file":"/foo/bar.so","triple":"i386-pc-linux"}])",
213       R"([{"uuid":"404142434445464748494a4b4c4d4e4f50515253","triple":"i386-pc-linux",)"
214       R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])");
215 
216   auto result = async_result.get();
217   ASSERT_TRUE(result.has_value());
218   ASSERT_EQ(1u, result->size());
219   EXPECT_EQ("/foo/bar.so", (*result)[0].GetFileSpec().GetPath());
220   EXPECT_EQ(triple, (*result)[0].GetArchitecture().GetTriple());
221   EXPECT_EQ(UUID("@ABCDEFGHIJKLMNOPQRS", 20), (*result)[0].GetUUID());
222   EXPECT_EQ(0u, (*result)[0].GetObjectOffset());
223   EXPECT_EQ(1234u, (*result)[0].GetObjectSize());
224 }
225 
226 TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse) {
227   llvm::Triple triple("i386-pc-linux");
228   FileSpec file_spec("/foo/bar.so", FileSpec::Style::posix);
229 
230   const char *invalid_responses[] = {
231       // no UUID
232       R"([{"triple":"i386-pc-linux",)"
233       R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",
234       // invalid UUID
235       R"([{"uuid":"XXXXXX","triple":"i386-pc-linux",)"
236       R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",
237       // no triple
238       R"([{"uuid":"404142434445464748494a4b4c4d4e4f",)"
239       R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",
240       // no file_path
241       R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
242       R"("file_offset":0,"file_size":1234}]])",
243       // no file_offset
244       R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
245       R"("file_path":"/foo/bar.so","file_size":1234}]])",
246       // no file_size
247       R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
248       R"("file_path":"/foo/bar.so","file_offset":0}]])",
249   };
250 
251   for (const char *response : invalid_responses) {
252     std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result =
253         std::async(std::launch::async,
254                    [&] { return client.GetModulesInfo(file_spec, triple); });
255     HandlePacket(
256         server,
257         R"(jModulesInfo:[{"file":"/foo/bar.so","triple":"i386-pc-linux"}])",
258         response);
259 
260     auto result = async_result.get();
261     ASSERT_TRUE(result);
262     ASSERT_EQ(0u, result->size()) << "response was: " << response;
263   }
264 }
265 
266 TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) {
267   std::thread server_thread([this] {
268     for (;;) {
269       StringExtractorGDBRemote request;
270       PacketResult result = server.GetPacket(request);
271       if (result == PacketResult::ErrorDisconnected)
272         return;
273       ASSERT_EQ(PacketResult::Success, result);
274       StringRef ref = request.GetStringRef();
275       ASSERT_TRUE(ref.consume_front("qSpeedTest:response_size:"));
276       int size;
277       ASSERT_FALSE(ref.consumeInteger(10, size)) << "ref: " << ref;
278       std::string response(size, 'X');
279       ASSERT_EQ(PacketResult::Success, server.SendPacket(response));
280     }
281   });
282 
283   StreamString ss;
284   client.TestPacketSpeed(10, 32, 32, 4096, true, ss);
285   client.Disconnect();
286   server_thread.join();
287 
288   GTEST_LOG_(INFO) << "Formatted output: " << ss.GetData();
289   auto object_sp = StructuredData::ParseJSON(std::string(ss.GetString()));
290   ASSERT_TRUE(bool(object_sp));
291   auto dict_sp = object_sp->GetAsDictionary();
292   ASSERT_TRUE(bool(dict_sp));
293 
294   object_sp = dict_sp->GetValueForKey("packet_speeds");
295   ASSERT_TRUE(bool(object_sp));
296   dict_sp = object_sp->GetAsDictionary();
297   ASSERT_TRUE(bool(dict_sp));
298 
299   int num_packets;
300   ASSERT_TRUE(dict_sp->GetValueForKeyAsInteger("num_packets", num_packets))
301       << ss.GetString();
302   ASSERT_EQ(10, num_packets);
303 }
304 
305 TEST_F(GDBRemoteCommunicationClientTest, SendSignalsToIgnore) {
306   std::future<Status> result = std::async(std::launch::async, [&] {
307     return client.SendSignalsToIgnore({2, 3, 5, 7, 0xB, 0xD, 0x11});
308   });
309 
310   HandlePacket(server, "QPassSignals:02;03;05;07;0b;0d;11", "OK");
311   EXPECT_TRUE(result.get().Success());
312 
313   result = std::async(std::launch::async, [&] {
314     return client.SendSignalsToIgnore(std::vector<int32_t>());
315   });
316 
317   HandlePacket(server, "QPassSignals:", "OK");
318   EXPECT_TRUE(result.get().Success());
319 }
320 
321 TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) {
322   const lldb::addr_t addr = 0xa000;
323   MemoryRegionInfo region_info;
324   std::future<Status> result = std::async(std::launch::async, [&] {
325     return client.GetMemoryRegionInfo(addr, region_info);
326   });
327 
328   HandlePacket(server,
329       "qMemoryRegionInfo:a000",
330       "start:a000;size:2000;permissions:rx;name:2f666f6f2f6261722e736f;");
331   if (XMLDocument::XMLEnabled()) {
332     // In case we have XML support, this will also do a "qXfer:memory-map".
333     // Preceeded by a query for supported extensions. Pretend we don't support
334     // that.
335     HandlePacket(server, testing::StartsWith("qSupported:"), "");
336   }
337   EXPECT_TRUE(result.get().Success());
338   EXPECT_EQ(addr, region_info.GetRange().GetRangeBase());
339   EXPECT_EQ(0x2000u, region_info.GetRange().GetByteSize());
340   EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetReadable());
341   EXPECT_EQ(MemoryRegionInfo::eNo, region_info.GetWritable());
342   EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetExecutable());
343   EXPECT_EQ("/foo/bar.so", region_info.GetName().GetStringRef());
344   EXPECT_EQ(MemoryRegionInfo::eDontKnow, region_info.GetMemoryTagged());
345 
346   result = std::async(std::launch::async, [&] {
347     return client.GetMemoryRegionInfo(addr, region_info);
348   });
349 
350   HandlePacket(server, "qMemoryRegionInfo:a000",
351                "start:a000;size:2000;flags:;");
352   EXPECT_TRUE(result.get().Success());
353   EXPECT_EQ(MemoryRegionInfo::eNo, region_info.GetMemoryTagged());
354 
355   result = std::async(std::launch::async, [&] {
356     return client.GetMemoryRegionInfo(addr, region_info);
357   });
358 
359   HandlePacket(server, "qMemoryRegionInfo:a000",
360                "start:a000;size:2000;flags: mt  zz mt  ;");
361   EXPECT_TRUE(result.get().Success());
362   EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetMemoryTagged());
363 }
364 
365 TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) {
366   const lldb::addr_t addr = 0x4000;
367   MemoryRegionInfo region_info;
368   std::future<Status> result = std::async(std::launch::async, [&] {
369     return client.GetMemoryRegionInfo(addr, region_info);
370   });
371 
372   HandlePacket(server, "qMemoryRegionInfo:4000", "start:4000;size:0000;");
373   if (XMLDocument::XMLEnabled()) {
374     // In case we have XML support, this will also do a "qXfer:memory-map".
375     // Preceeded by a query for supported extensions. Pretend we don't support
376     // that.
377     HandlePacket(server, testing::StartsWith("qSupported:"), "");
378   }
379   EXPECT_FALSE(result.get().Success());
380 }
381 
382 TEST_F(GDBRemoteCommunicationClientTest, SendTraceSupportedPacket) {
383   TraceSupportedResponse trace_type;
384   std::string error_message;
385   auto callback = [&] {
386     std::chrono::seconds timeout(10);
387     if (llvm::Expected<TraceSupportedResponse> trace_type_or_err =
388             client.SendTraceSupported(timeout)) {
389       trace_type = *trace_type_or_err;
390       error_message = "";
391       return true;
392     } else {
393       trace_type = {};
394       error_message = llvm::toString(trace_type_or_err.takeError());
395       return false;
396     }
397   };
398 
399   // Success response
400   {
401     std::future<bool> result = std::async(std::launch::async, callback);
402 
403     HandlePacket(
404         server, "jLLDBTraceSupported",
405         R"({"name":"intel-pt","description":"Intel Processor Trace"}])");
406 
407     EXPECT_TRUE(result.get());
408     ASSERT_STREQ(trace_type.name.c_str(), "intel-pt");
409     ASSERT_STREQ(trace_type.description.c_str(), "Intel Processor Trace");
410   }
411 
412   // Error response - wrong json
413   {
414     std::future<bool> result = std::async(std::launch::async, callback);
415 
416     HandlePacket(server, "jLLDBTraceSupported", R"({"type":"intel-pt"}])");
417 
418     EXPECT_FALSE(result.get());
419     ASSERT_STREQ(error_message.c_str(), "missing value at TraceSupportedResponse.description");
420   }
421 
422   // Error response
423   {
424     std::future<bool> result = std::async(std::launch::async, callback);
425 
426     HandlePacket(server, "jLLDBTraceSupported", "E23");
427 
428     EXPECT_FALSE(result.get());
429   }
430 
431   // Error response with error message
432   {
433     std::future<bool> result = std::async(std::launch::async, callback);
434 
435     HandlePacket(server, "jLLDBTraceSupported",
436                  "E23;50726F63657373206E6F742072756E6E696E672E");
437 
438     EXPECT_FALSE(result.get());
439     ASSERT_STREQ(error_message.c_str(), "Process not running.");
440   }
441 }
442 
443 TEST_F(GDBRemoteCommunicationClientTest, GetQOffsets) {
444   const auto &GetQOffsets = [&](llvm::StringRef response) {
445     std::future<Optional<QOffsets>> result = std::async(
446         std::launch::async, [&] { return client.GetQOffsets(); });
447 
448     HandlePacket(server, "qOffsets", response);
449     return result.get();
450   };
451   EXPECT_EQ((QOffsets{false, {0x1234, 0x1234}}),
452             GetQOffsets("Text=1234;Data=1234"));
453   EXPECT_EQ((QOffsets{false, {0x1234, 0x1234, 0x1234}}),
454             GetQOffsets("Text=1234;Data=1234;Bss=1234"));
455   EXPECT_EQ((QOffsets{true, {0x1234}}), GetQOffsets("TextSeg=1234"));
456   EXPECT_EQ((QOffsets{true, {0x1234, 0x2345}}),
457             GetQOffsets("TextSeg=1234;DataSeg=2345"));
458 
459   EXPECT_EQ(std::nullopt, GetQOffsets("E05"));
460   EXPECT_EQ(std::nullopt, GetQOffsets("Text=bogus"));
461   EXPECT_EQ(std::nullopt, GetQOffsets("Text=1234"));
462   EXPECT_EQ(std::nullopt, GetQOffsets("Text=1234;Data=1234;"));
463   EXPECT_EQ(std::nullopt, GetQOffsets("Text=1234;Data=1234;Bss=1234;"));
464   EXPECT_EQ(std::nullopt, GetQOffsets("TEXTSEG=1234"));
465   EXPECT_EQ(std::nullopt, GetQOffsets("TextSeg=0x1234"));
466   EXPECT_EQ(std::nullopt, GetQOffsets("TextSeg=12345678123456789"));
467 }
468 
469 static void
470 check_qmemtags(TestClient &client, MockServer &server, size_t read_len,
471                int32_t type, const char *packet, llvm::StringRef response,
472                llvm::Optional<std::vector<uint8_t>> expected_tag_data) {
473   const auto &ReadMemoryTags = [&]() {
474     std::future<DataBufferSP> result = std::async(std::launch::async, [&] {
475       return client.ReadMemoryTags(0xDEF0, read_len, type);
476     });
477 
478     HandlePacket(server, packet, response);
479     return result.get();
480   };
481 
482   auto result = ReadMemoryTags();
483   if (expected_tag_data) {
484     ASSERT_TRUE(result);
485     llvm::ArrayRef<uint8_t> expected(*expected_tag_data);
486     llvm::ArrayRef<uint8_t> got = result->GetData();
487     ASSERT_THAT(expected, testing::ContainerEq(got));
488   } else {
489     ASSERT_FALSE(result);
490   }
491 }
492 
493 TEST_F(GDBRemoteCommunicationClientTest, ReadMemoryTags) {
494   // Zero length reads are valid
495   check_qmemtags(client, server, 0, 1, "qMemTags:def0,0:1", "m",
496                  std::vector<uint8_t>{});
497 
498   // Type can be negative. Put into the packet as the raw bytes
499   // (as opposed to a literal -1)
500   check_qmemtags(client, server, 0, -1, "qMemTags:def0,0:ffffffff", "m",
501                  std::vector<uint8_t>{});
502   check_qmemtags(client, server, 0, std::numeric_limits<int32_t>::min(),
503                  "qMemTags:def0,0:80000000", "m", std::vector<uint8_t>{});
504   check_qmemtags(client, server, 0, std::numeric_limits<int32_t>::max(),
505                  "qMemTags:def0,0:7fffffff", "m", std::vector<uint8_t>{});
506 
507   // The client layer does not check the length of the received data.
508   // All we need is the "m" and for the decode to use all of the chars
509   check_qmemtags(client, server, 32, 2, "qMemTags:def0,20:2", "m09",
510                  std::vector<uint8_t>{0x9});
511 
512   // Zero length response is fine as long as the "m" is present
513   check_qmemtags(client, server, 0, 0x34, "qMemTags:def0,0:34", "m",
514                  std::vector<uint8_t>{});
515 
516   // Normal responses
517   check_qmemtags(client, server, 16, 1, "qMemTags:def0,10:1", "m66",
518                  std::vector<uint8_t>{0x66});
519   check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m0102",
520                  std::vector<uint8_t>{0x1, 0x2});
521 
522   // Empty response is an error
523   check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "", std::nullopt);
524   // Usual error response
525   check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "E01",
526                  std::nullopt);
527   // Leading m missing
528   check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "01",
529                  std::nullopt);
530   // Anything other than m is an error
531   check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "z01",
532                  std::nullopt);
533   // Decoding tag data doesn't use all the chars in the packet
534   check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m09zz",
535                  std::nullopt);
536   // Data that is not hex bytes
537   check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "mhello",
538                  std::nullopt);
539   // Data is not a complete hex char
540   check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m9",
541                  std::nullopt);
542   // Data has a trailing hex char
543   check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m01020",
544                  std::nullopt);
545 }
546 
547 static void check_Qmemtags(TestClient &client, MockServer &server,
548                            lldb::addr_t addr, size_t len, int32_t type,
549                            const std::vector<uint8_t> &tags, const char *packet,
550                            llvm::StringRef response, bool should_succeed) {
551   const auto &WriteMemoryTags = [&]() {
552     std::future<Status> result = std::async(std::launch::async, [&] {
553       return client.WriteMemoryTags(addr, len, type, tags);
554     });
555 
556     HandlePacket(server, packet, response);
557     return result.get();
558   };
559 
560   auto result = WriteMemoryTags();
561   if (should_succeed)
562     ASSERT_TRUE(result.Success());
563   else
564     ASSERT_TRUE(result.Fail());
565 }
566 
567 TEST_F(GDBRemoteCommunicationClientTest, WriteMemoryTags) {
568   check_Qmemtags(client, server, 0xABCD, 0x20, 1,
569                  std::vector<uint8_t>{0x12, 0x34}, "QMemTags:abcd,20:1:1234",
570                  "OK", true);
571 
572   // The GDB layer doesn't care that the number of tags !=
573   // the length of the write.
574   check_Qmemtags(client, server, 0x4321, 0x20, 9, std::vector<uint8_t>{},
575                  "QMemTags:4321,20:9:", "OK", true);
576 
577   check_Qmemtags(client, server, 0x8877, 0x123, 0x34,
578                  std::vector<uint8_t>{0x55, 0x66, 0x77},
579                  "QMemTags:8877,123:34:556677", "E01", false);
580 
581   // Type is a signed integer but is packed as its raw bytes,
582   // instead of having a +/-.
583   check_Qmemtags(client, server, 0x456789, 0, -1, std::vector<uint8_t>{0x99},
584                  "QMemTags:456789,0:ffffffff:99", "E03", false);
585   check_Qmemtags(client, server, 0x456789, 0,
586                  std::numeric_limits<int32_t>::max(),
587                  std::vector<uint8_t>{0x99}, "QMemTags:456789,0:7fffffff:99",
588                  "E03", false);
589   check_Qmemtags(client, server, 0x456789, 0,
590                  std::numeric_limits<int32_t>::min(),
591                  std::vector<uint8_t>{0x99}, "QMemTags:456789,0:80000000:99",
592                  "E03", false);
593 }
594