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 {
TestClient__anon8a707d100111::TestClient32 TestClient() { m_send_acks = false; }
33 };
34
Handle_QThreadSuffixSupported(MockServer & server,bool supported)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
HandlePacket(MockServer & server,const testing::Matcher<const std::string &> & expected,StringRef response)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:
SetUp()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
TEST_F(GDBRemoteCommunicationClientTest,WriteRegister)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
TEST_F(GDBRemoteCommunicationClientTest,WriteRegisterNoSuffix)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
TEST_F(GDBRemoteCommunicationClientTest,ReadRegister)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
TEST_F(GDBRemoteCommunicationClientTest,SaveRestoreRegistersNoSuffix)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
TEST_F(GDBRemoteCommunicationClientTest,SyncThreadState)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
TEST_F(GDBRemoteCommunicationClientTest,GetModulesInfo)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.value()[0].GetFileSpec().GetPath());
196 EXPECT_EQ(triple, result.value()[0].GetArchitecture().GetTriple());
197 EXPECT_EQ(UUID::fromData("@ABCDEFGHIJKLMNO", 16),
198 result.value()[0].GetUUID());
199 EXPECT_EQ(0u, result.value()[0].GetObjectOffset());
200 EXPECT_EQ(1234u, result.value()[0].GetObjectSize());
201 }
202
TEST_F(GDBRemoteCommunicationClientTest,GetModulesInfo_UUID20)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<llvm::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.value()[0].GetFileSpec().GetPath());
221 EXPECT_EQ(triple, result.value()[0].GetArchitecture().GetTriple());
222 EXPECT_EQ(UUID::fromData("@ABCDEFGHIJKLMNOPQRS", 20),
223 result.value()[0].GetUUID());
224 EXPECT_EQ(0u, result.value()[0].GetObjectOffset());
225 EXPECT_EQ(1234u, result.value()[0].GetObjectSize());
226 }
227
TEST_F(GDBRemoteCommunicationClientTest,GetModulesInfoInvalidResponse)228 TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse) {
229 llvm::Triple triple("i386-pc-linux");
230 FileSpec file_spec("/foo/bar.so", FileSpec::Style::posix);
231
232 const char *invalid_responses[] = {
233 // no UUID
234 R"([{"triple":"i386-pc-linux",)"
235 R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",
236 // invalid UUID
237 R"([{"uuid":"XXXXXX","triple":"i386-pc-linux",)"
238 R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",
239 // no triple
240 R"([{"uuid":"404142434445464748494a4b4c4d4e4f",)"
241 R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",
242 // no file_path
243 R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
244 R"("file_offset":0,"file_size":1234}]])",
245 // no file_offset
246 R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
247 R"("file_path":"/foo/bar.so","file_size":1234}]])",
248 // no file_size
249 R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
250 R"("file_path":"/foo/bar.so","file_offset":0}]])",
251 };
252
253 for (const char *response : invalid_responses) {
254 std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result =
255 std::async(std::launch::async,
256 [&] { return client.GetModulesInfo(file_spec, triple); });
257 HandlePacket(
258 server,
259 R"(jModulesInfo:[{"file":"/foo/bar.so","triple":"i386-pc-linux"}])",
260 response);
261
262 auto result = async_result.get();
263 ASSERT_TRUE(result);
264 ASSERT_EQ(0u, result->size()) << "response was: " << response;
265 }
266 }
267
TEST_F(GDBRemoteCommunicationClientTest,TestPacketSpeedJSON)268 TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) {
269 std::thread server_thread([this] {
270 for (;;) {
271 StringExtractorGDBRemote request;
272 PacketResult result = server.GetPacket(request);
273 if (result == PacketResult::ErrorDisconnected)
274 return;
275 ASSERT_EQ(PacketResult::Success, result);
276 StringRef ref = request.GetStringRef();
277 ASSERT_TRUE(ref.consume_front("qSpeedTest:response_size:"));
278 int size;
279 ASSERT_FALSE(ref.consumeInteger(10, size)) << "ref: " << ref;
280 std::string response(size, 'X');
281 ASSERT_EQ(PacketResult::Success, server.SendPacket(response));
282 }
283 });
284
285 StreamString ss;
286 client.TestPacketSpeed(10, 32, 32, 4096, true, ss);
287 client.Disconnect();
288 server_thread.join();
289
290 GTEST_LOG_(INFO) << "Formatted output: " << ss.GetData();
291 auto object_sp = StructuredData::ParseJSON(std::string(ss.GetString()));
292 ASSERT_TRUE(bool(object_sp));
293 auto dict_sp = object_sp->GetAsDictionary();
294 ASSERT_TRUE(bool(dict_sp));
295
296 object_sp = dict_sp->GetValueForKey("packet_speeds");
297 ASSERT_TRUE(bool(object_sp));
298 dict_sp = object_sp->GetAsDictionary();
299 ASSERT_TRUE(bool(dict_sp));
300
301 int num_packets;
302 ASSERT_TRUE(dict_sp->GetValueForKeyAsInteger("num_packets", num_packets))
303 << ss.GetString();
304 ASSERT_EQ(10, num_packets);
305 }
306
TEST_F(GDBRemoteCommunicationClientTest,SendSignalsToIgnore)307 TEST_F(GDBRemoteCommunicationClientTest, SendSignalsToIgnore) {
308 std::future<Status> result = std::async(std::launch::async, [&] {
309 return client.SendSignalsToIgnore({2, 3, 5, 7, 0xB, 0xD, 0x11});
310 });
311
312 HandlePacket(server, "QPassSignals:02;03;05;07;0b;0d;11", "OK");
313 EXPECT_TRUE(result.get().Success());
314
315 result = std::async(std::launch::async, [&] {
316 return client.SendSignalsToIgnore(std::vector<int32_t>());
317 });
318
319 HandlePacket(server, "QPassSignals:", "OK");
320 EXPECT_TRUE(result.get().Success());
321 }
322
TEST_F(GDBRemoteCommunicationClientTest,GetMemoryRegionInfo)323 TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) {
324 const lldb::addr_t addr = 0xa000;
325 MemoryRegionInfo region_info;
326 std::future<Status> result = std::async(std::launch::async, [&] {
327 return client.GetMemoryRegionInfo(addr, region_info);
328 });
329
330 HandlePacket(server,
331 "qMemoryRegionInfo:a000",
332 "start:a000;size:2000;permissions:rx;name:2f666f6f2f6261722e736f;");
333 if (XMLDocument::XMLEnabled()) {
334 // In case we have XML support, this will also do a "qXfer:memory-map".
335 // Preceeded by a query for supported extensions. Pretend we don't support
336 // that.
337 HandlePacket(server, testing::StartsWith("qSupported:"), "");
338 }
339 EXPECT_TRUE(result.get().Success());
340 EXPECT_EQ(addr, region_info.GetRange().GetRangeBase());
341 EXPECT_EQ(0x2000u, region_info.GetRange().GetByteSize());
342 EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetReadable());
343 EXPECT_EQ(MemoryRegionInfo::eNo, region_info.GetWritable());
344 EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetExecutable());
345 EXPECT_EQ("/foo/bar.so", region_info.GetName().GetStringRef());
346 EXPECT_EQ(MemoryRegionInfo::eDontKnow, region_info.GetMemoryTagged());
347
348 result = std::async(std::launch::async, [&] {
349 return client.GetMemoryRegionInfo(addr, region_info);
350 });
351
352 HandlePacket(server, "qMemoryRegionInfo:a000",
353 "start:a000;size:2000;flags:;");
354 EXPECT_TRUE(result.get().Success());
355 EXPECT_EQ(MemoryRegionInfo::eNo, region_info.GetMemoryTagged());
356
357 result = std::async(std::launch::async, [&] {
358 return client.GetMemoryRegionInfo(addr, region_info);
359 });
360
361 HandlePacket(server, "qMemoryRegionInfo:a000",
362 "start:a000;size:2000;flags: mt zz mt ;");
363 EXPECT_TRUE(result.get().Success());
364 EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetMemoryTagged());
365 }
366
TEST_F(GDBRemoteCommunicationClientTest,GetMemoryRegionInfoInvalidResponse)367 TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) {
368 const lldb::addr_t addr = 0x4000;
369 MemoryRegionInfo region_info;
370 std::future<Status> result = std::async(std::launch::async, [&] {
371 return client.GetMemoryRegionInfo(addr, region_info);
372 });
373
374 HandlePacket(server, "qMemoryRegionInfo:4000", "start:4000;size:0000;");
375 if (XMLDocument::XMLEnabled()) {
376 // In case we have XML support, this will also do a "qXfer:memory-map".
377 // Preceeded by a query for supported extensions. Pretend we don't support
378 // that.
379 HandlePacket(server, testing::StartsWith("qSupported:"), "");
380 }
381 EXPECT_FALSE(result.get().Success());
382 }
383
TEST_F(GDBRemoteCommunicationClientTest,SendTraceSupportedPacket)384 TEST_F(GDBRemoteCommunicationClientTest, SendTraceSupportedPacket) {
385 TraceSupportedResponse trace_type;
386 std::string error_message;
387 auto callback = [&] {
388 std::chrono::seconds timeout(10);
389 if (llvm::Expected<TraceSupportedResponse> trace_type_or_err =
390 client.SendTraceSupported(timeout)) {
391 trace_type = *trace_type_or_err;
392 error_message = "";
393 return true;
394 } else {
395 trace_type = {};
396 error_message = llvm::toString(trace_type_or_err.takeError());
397 return false;
398 }
399 };
400
401 // Success response
402 {
403 std::future<bool> result = std::async(std::launch::async, callback);
404
405 HandlePacket(
406 server, "jLLDBTraceSupported",
407 R"({"name":"intel-pt","description":"Intel Processor Trace"}])");
408
409 EXPECT_TRUE(result.get());
410 ASSERT_STREQ(trace_type.name.c_str(), "intel-pt");
411 ASSERT_STREQ(trace_type.description.c_str(), "Intel Processor Trace");
412 }
413
414 // Error response - wrong json
415 {
416 std::future<bool> result = std::async(std::launch::async, callback);
417
418 HandlePacket(server, "jLLDBTraceSupported", R"({"type":"intel-pt"}])");
419
420 EXPECT_FALSE(result.get());
421 ASSERT_STREQ(error_message.c_str(), "missing value at TraceSupportedResponse.description");
422 }
423
424 // Error response
425 {
426 std::future<bool> result = std::async(std::launch::async, callback);
427
428 HandlePacket(server, "jLLDBTraceSupported", "E23");
429
430 EXPECT_FALSE(result.get());
431 }
432
433 // Error response with error message
434 {
435 std::future<bool> result = std::async(std::launch::async, callback);
436
437 HandlePacket(server, "jLLDBTraceSupported",
438 "E23;50726F63657373206E6F742072756E6E696E672E");
439
440 EXPECT_FALSE(result.get());
441 ASSERT_STREQ(error_message.c_str(), "Process not running.");
442 }
443 }
444
TEST_F(GDBRemoteCommunicationClientTest,GetQOffsets)445 TEST_F(GDBRemoteCommunicationClientTest, GetQOffsets) {
446 const auto &GetQOffsets = [&](llvm::StringRef response) {
447 std::future<Optional<QOffsets>> result = std::async(
448 std::launch::async, [&] { return client.GetQOffsets(); });
449
450 HandlePacket(server, "qOffsets", response);
451 return result.get();
452 };
453 EXPECT_EQ((QOffsets{false, {0x1234, 0x1234}}),
454 GetQOffsets("Text=1234;Data=1234"));
455 EXPECT_EQ((QOffsets{false, {0x1234, 0x1234, 0x1234}}),
456 GetQOffsets("Text=1234;Data=1234;Bss=1234"));
457 EXPECT_EQ((QOffsets{true, {0x1234}}), GetQOffsets("TextSeg=1234"));
458 EXPECT_EQ((QOffsets{true, {0x1234, 0x2345}}),
459 GetQOffsets("TextSeg=1234;DataSeg=2345"));
460
461 EXPECT_EQ(llvm::None, GetQOffsets("E05"));
462 EXPECT_EQ(llvm::None, GetQOffsets("Text=bogus"));
463 EXPECT_EQ(llvm::None, GetQOffsets("Text=1234"));
464 EXPECT_EQ(llvm::None, GetQOffsets("Text=1234;Data=1234;"));
465 EXPECT_EQ(llvm::None, GetQOffsets("Text=1234;Data=1234;Bss=1234;"));
466 EXPECT_EQ(llvm::None, GetQOffsets("TEXTSEG=1234"));
467 EXPECT_EQ(llvm::None, GetQOffsets("TextSeg=0x1234"));
468 EXPECT_EQ(llvm::None, GetQOffsets("TextSeg=12345678123456789"));
469 }
470
471 static void
check_qmemtags(TestClient & client,MockServer & server,size_t read_len,int32_t type,const char * packet,llvm::StringRef response,llvm::Optional<std::vector<uint8_t>> expected_tag_data)472 check_qmemtags(TestClient &client, MockServer &server, size_t read_len,
473 int32_t type, const char *packet, llvm::StringRef response,
474 llvm::Optional<std::vector<uint8_t>> expected_tag_data) {
475 const auto &ReadMemoryTags = [&]() {
476 std::future<DataBufferSP> result = std::async(std::launch::async, [&] {
477 return client.ReadMemoryTags(0xDEF0, read_len, type);
478 });
479
480 HandlePacket(server, packet, response);
481 return result.get();
482 };
483
484 auto result = ReadMemoryTags();
485 if (expected_tag_data) {
486 ASSERT_TRUE(result);
487 llvm::ArrayRef<uint8_t> expected(*expected_tag_data);
488 llvm::ArrayRef<uint8_t> got = result->GetData();
489 ASSERT_THAT(expected, testing::ContainerEq(got));
490 } else {
491 ASSERT_FALSE(result);
492 }
493 }
494
TEST_F(GDBRemoteCommunicationClientTest,ReadMemoryTags)495 TEST_F(GDBRemoteCommunicationClientTest, ReadMemoryTags) {
496 // Zero length reads are valid
497 check_qmemtags(client, server, 0, 1, "qMemTags:def0,0:1", "m",
498 std::vector<uint8_t>{});
499
500 // Type can be negative. Put into the packet as the raw bytes
501 // (as opposed to a literal -1)
502 check_qmemtags(client, server, 0, -1, "qMemTags:def0,0:ffffffff", "m",
503 std::vector<uint8_t>{});
504 check_qmemtags(client, server, 0, std::numeric_limits<int32_t>::min(),
505 "qMemTags:def0,0:80000000", "m", std::vector<uint8_t>{});
506 check_qmemtags(client, server, 0, std::numeric_limits<int32_t>::max(),
507 "qMemTags:def0,0:7fffffff", "m", std::vector<uint8_t>{});
508
509 // The client layer does not check the length of the received data.
510 // All we need is the "m" and for the decode to use all of the chars
511 check_qmemtags(client, server, 32, 2, "qMemTags:def0,20:2", "m09",
512 std::vector<uint8_t>{0x9});
513
514 // Zero length response is fine as long as the "m" is present
515 check_qmemtags(client, server, 0, 0x34, "qMemTags:def0,0:34", "m",
516 std::vector<uint8_t>{});
517
518 // Normal responses
519 check_qmemtags(client, server, 16, 1, "qMemTags:def0,10:1", "m66",
520 std::vector<uint8_t>{0x66});
521 check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m0102",
522 std::vector<uint8_t>{0x1, 0x2});
523
524 // Empty response is an error
525 check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "", llvm::None);
526 // Usual error response
527 check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "E01",
528 llvm::None);
529 // Leading m missing
530 check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "01", llvm::None);
531 // Anything other than m is an error
532 check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "z01",
533 llvm::None);
534 // Decoding tag data doesn't use all the chars in the packet
535 check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m09zz",
536 llvm::None);
537 // Data that is not hex bytes
538 check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "mhello",
539 llvm::None);
540 // Data is not a complete hex char
541 check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m9", llvm::None);
542 // Data has a trailing hex char
543 check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m01020",
544 llvm::None);
545 }
546
check_Qmemtags(TestClient & client,MockServer & server,lldb::addr_t addr,size_t len,int32_t type,const std::vector<uint8_t> & tags,const char * packet,llvm::StringRef response,bool should_succeed)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
TEST_F(GDBRemoteCommunicationClientTest,WriteMemoryTags)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