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

source code of lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp