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
23using namespace lldb_private::process_gdb_remote;
24using namespace lldb_private;
25using namespace lldb;
26using namespace llvm;
27
28namespace {
29
30typedef GDBRemoteCommunication::PacketResult PacketResult;
31
32struct TestClient : public GDBRemoteCommunicationClient {
33 TestClient() { m_send_acks = false; }
34};
35
36void 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
46void 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
55uint8_t all_registers[] = {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
56 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'};
57std::string all_registers_hex = "404142434445464748494a4b4c4d4e4f";
58uint8_t one_register[] = {'A', 'B', 'C', 'D'};
59std::string one_register_hex = "41424344";
60
61} // end anonymous namespace
62
63class GDBRemoteCommunicationClientTest : public GDBRemoteTest {
64public:
65 void SetUp() override {
66 ASSERT_THAT_ERROR(GDBRemoteCommunication::ConnectLocally(client, server),
67 llvm::Succeeded());
68 }
69
70protected:
71 TestClient client;
72 MockServer server;
73};
74
75TEST_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(policy: std::launch::async, fn: [&] {
79 return client.WriteRegister(tid, reg_num, data: one_register);
80 });
81
82 Handle_QThreadSuffixSupported(server, supported: true);
83
84 HandlePacket(server, expected: "P4=" + one_register_hex + ";thread:0047;", response: "OK");
85 ASSERT_TRUE(write_result.get());
86
87 write_result = std::async(policy: std::launch::async, fn: [&] {
88 return client.WriteAllRegisters(tid, data: all_registers);
89 });
90
91 HandlePacket(server, expected: "G" + all_registers_hex + ";thread:0047;", response: "OK");
92 ASSERT_TRUE(write_result.get());
93}
94
95TEST_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(policy: std::launch::async, fn: [&] {
99 return client.WriteRegister(tid, reg_num, data: one_register);
100 });
101
102 Handle_QThreadSuffixSupported(server, supported: false);
103 HandlePacket(server, expected: "Hg47", response: "OK");
104 HandlePacket(server, expected: "P4=" + one_register_hex, response: "OK");
105 ASSERT_TRUE(write_result.get());
106
107 write_result = std::async(policy: std::launch::async, fn: [&] {
108 return client.WriteAllRegisters(tid, data: all_registers);
109 });
110
111 HandlePacket(server, expected: "G" + all_registers_hex, response: "OK");
112 ASSERT_TRUE(write_result.get());
113}
114
115TEST_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 policy: std::launch::async, fn: [&] { return client.GetpPacketSupported(tid); });
120 Handle_QThreadSuffixSupported(server, supported: true);
121 HandlePacket(server, expected: "p0;thread:0047;", response: one_register_hex);
122 ASSERT_TRUE(async_result.get());
123
124 std::future<DataBufferSP> read_result = std::async(
125 policy: std::launch::async, fn: [&] { return client.ReadRegister(tid, reg_num); });
126 HandlePacket(server, expected: "p4;thread:0047;", response: "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(policy: std::launch::async,
133 fn: [&] { return client.ReadAllRegisters(tid); });
134 HandlePacket(server, expected: "g;thread:0047;", response: 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
141TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) {
142 const lldb::tid_t tid = 0x47;
143 uint32_t save_id;
144 std::future<bool> async_result = std::async(policy: std::launch::async, fn: [&] {
145 return client.SaveRegisterState(tid, save_id);
146 });
147 Handle_QThreadSuffixSupported(server, supported: false);
148 HandlePacket(server, expected: "Hg47", response: "OK");
149 HandlePacket(server, expected: "QSaveRegisterState", response: "1");
150 ASSERT_TRUE(async_result.get());
151 EXPECT_EQ(1u, save_id);
152
153 async_result = std::async(policy: std::launch::async, fn: [&] {
154 return client.RestoreRegisterState(tid, save_id);
155 });
156 HandlePacket(server, expected: "QRestoreRegisterState:1", response: "OK");
157 ASSERT_TRUE(async_result.get());
158}
159
160TEST_F(GDBRemoteCommunicationClientTest, SyncThreadState) {
161 const lldb::tid_t tid = 0x47;
162 std::future<bool> async_result = std::async(
163 policy: std::launch::async, fn: [&] { return client.SyncThreadState(tid); });
164 HandlePacket(server, expected: "qSyncThreadStateSupported", response: "OK");
165 HandlePacket(server, expected: "QSyncThreadState:0047;", response: "OK");
166 ASSERT_TRUE(async_result.get());
167}
168
169TEST_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(policy: std::launch::async,
184 fn: [&] { return client.GetModulesInfo(module_file_specs: file_specs, triple); });
185 HandlePacket(
186 server, expected: "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 response: 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
203TEST_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(policy: std::launch::async,
209 fn: [&] { return client.GetModulesInfo(module_file_specs: file_spec, triple); });
210 HandlePacket(
211 server,
212 expected: "jModulesInfo:["
213 R"({"file":"/foo/bar.so","triple":"i386-pc-linux"}])",
214 response: 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
227TEST_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(policy: std::launch::async,
255 fn: [&] { return client.GetModulesInfo(module_file_specs: file_spec, triple); });
256 HandlePacket(
257 server,
258 expected: 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
267TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) {
268 std::thread server_thread([this] {
269 for (;;) {
270 StringExtractorGDBRemote request;
271 PacketResult result = server.GetPacket(response&: 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(num_packets: 10, max_send: 32, max_recv: 32, recv_amount: 4096, json: true, strm&: ss);
286 client.Disconnect();
287 server_thread.join();
288
289 GTEST_LOG_(INFO) << "Formatted output: " << ss.GetData();
290 auto object_sp = StructuredData::ParseJSON(json_text: 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(key: "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
306TEST_F(GDBRemoteCommunicationClientTest, SendSignalsToIgnore) {
307 std::future<Status> result = std::async(policy: std::launch::async, fn: [&] {
308 return client.SendSignalsToIgnore(signals: {2, 3, 5, 7, 0xB, 0xD, 0x11});
309 });
310
311 HandlePacket(server, expected: "QPassSignals:02;03;05;07;0b;0d;11", response: "OK");
312 EXPECT_TRUE(result.get().Success());
313
314 result = std::async(policy: std::launch::async, fn: [&] {
315 return client.SendSignalsToIgnore(signals: std::vector<int32_t>());
316 });
317
318 HandlePacket(server, expected: "QPassSignals:", response: "OK");
319 EXPECT_TRUE(result.get().Success());
320}
321
322TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) {
323 const lldb::addr_t addr = 0xa000;
324 MemoryRegionInfo region_info;
325 std::future<Status> result = std::async(policy: std::launch::async, fn: [&] {
326 return client.GetMemoryRegionInfo(addr, range_info&: region_info);
327 });
328
329 HandlePacket(server,
330 expected: "qMemoryRegionInfo:a000",
331 response: "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, expected: testing::StartsWith(prefix: "qSupported:"), response: "");
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(policy: std::launch::async, fn: [&] {
350 return client.GetMemoryRegionInfo(addr, range_info&: region_info);
351 });
352
353 HandlePacket(server, expected: "qMemoryRegionInfo:a000",
354 response: "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(policy: std::launch::async, fn: [&] {
361 return client.GetMemoryRegionInfo(addr, range_info&: region_info);
362 });
363
364 HandlePacket(server, expected: "qMemoryRegionInfo:a000",
365 response: "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(policy: std::launch::async, fn: [&] {
372 return client.GetMemoryRegionInfo(addr, range_info&: region_info);
373 });
374
375 HandlePacket(server, expected: "qMemoryRegionInfo:a000",
376 response: "start:a000;size:2000;type:heap;");
377 EXPECT_TRUE(result.get().Success());
378 EXPECT_EQ(MemoryRegionInfo::eNo, region_info.IsStackMemory());
379}
380
381TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) {
382 const lldb::addr_t addr = 0x4000;
383 MemoryRegionInfo region_info;
384 std::future<Status> result = std::async(policy: std::launch::async, fn: [&] {
385 return client.GetMemoryRegionInfo(addr, range_info&: region_info);
386 });
387
388 HandlePacket(server, expected: "qMemoryRegionInfo:4000", response: "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, expected: testing::StartsWith(prefix: "qSupported:"), response: "");
394 }
395 EXPECT_FALSE(result.get().Success());
396}
397
398TEST_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(interrupt_timeout: timeout)) {
405 trace_type = *trace_type_or_err;
406 error_message = "";
407 return true;
408 } else {
409 trace_type = {};
410 error_message = llvm::toString(E: trace_type_or_err.takeError());
411 return false;
412 }
413 };
414
415 // Success response
416 {
417 std::future<bool> result = std::async(policy: std::launch::async, fn&: callback);
418
419 HandlePacket(
420 server, expected: "jLLDBTraceSupported",
421 response: 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(policy: std::launch::async, fn&: callback);
431
432 HandlePacket(server, expected: "jLLDBTraceSupported", response: 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(policy: std::launch::async, fn&: callback);
441
442 HandlePacket(server, expected: "jLLDBTraceSupported", response: "E23");
443
444 EXPECT_FALSE(result.get());
445 }
446
447 // Error response with error message
448 {
449 std::future<bool> result = std::async(policy: std::launch::async, fn&: callback);
450
451 HandlePacket(server, expected: "jLLDBTraceSupported",
452 response: "E23;50726F63657373206E6F742072756E6E696E672E");
453
454 EXPECT_FALSE(result.get());
455 ASSERT_STREQ(error_message.c_str(), "Process not running.");
456 }
457}
458
459TEST_F(GDBRemoteCommunicationClientTest, GetQOffsets) {
460 const auto &GetQOffsets = [&](llvm::StringRef response) {
461 std::future<std::optional<QOffsets>> result =
462 std::async(policy: std::launch::async, fn: [&] { return client.GetQOffsets(); });
463
464 HandlePacket(server, expected: "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
485static void
486check_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(policy: std::launch::async, fn: [&] {
491 return client.ReadMemoryTags(addr: 0xDEF0, len: read_len, type);
492 });
493
494 HandlePacket(server, expected: 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
509TEST_F(GDBRemoteCommunicationClientTest, ReadMemoryTags) {
510 // Zero length reads are valid
511 check_qmemtags(client, server, read_len: 0, type: 1, packet: "qMemTags:def0,0:1", response: "m",
512 expected_tag_data: 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, read_len: 0, type: -1, packet: "qMemTags:def0,0:ffffffff", response: "m",
517 expected_tag_data: std::vector<uint8_t>{});
518 check_qmemtags(client, server, read_len: 0, type: std::numeric_limits<int32_t>::min(),
519 packet: "qMemTags:def0,0:80000000", response: "m", expected_tag_data: std::vector<uint8_t>{});
520 check_qmemtags(client, server, read_len: 0, type: std::numeric_limits<int32_t>::max(),
521 packet: "qMemTags:def0,0:7fffffff", response: "m", expected_tag_data: 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, read_len: 32, type: 2, packet: "qMemTags:def0,20:2", response: "m09",
526 expected_tag_data: std::vector<uint8_t>{0x9});
527
528 // Zero length response is fine as long as the "m" is present
529 check_qmemtags(client, server, read_len: 0, type: 0x34, packet: "qMemTags:def0,0:34", response: "m",
530 expected_tag_data: std::vector<uint8_t>{});
531
532 // Normal responses
533 check_qmemtags(client, server, read_len: 16, type: 1, packet: "qMemTags:def0,10:1", response: "m66",
534 expected_tag_data: std::vector<uint8_t>{0x66});
535 check_qmemtags(client, server, read_len: 32, type: 1, packet: "qMemTags:def0,20:1", response: "m0102",
536 expected_tag_data: std::vector<uint8_t>{0x1, 0x2});
537
538 // Empty response is an error
539 check_qmemtags(client, server, read_len: 17, type: 1, packet: "qMemTags:def0,11:1", response: "", expected_tag_data: std::nullopt);
540 // Usual error response
541 check_qmemtags(client, server, read_len: 17, type: 1, packet: "qMemTags:def0,11:1", response: "E01",
542 expected_tag_data: std::nullopt);
543 // Leading m missing
544 check_qmemtags(client, server, read_len: 17, type: 1, packet: "qMemTags:def0,11:1", response: "01",
545 expected_tag_data: std::nullopt);
546 // Anything other than m is an error
547 check_qmemtags(client, server, read_len: 17, type: 1, packet: "qMemTags:def0,11:1", response: "z01",
548 expected_tag_data: std::nullopt);
549 // Decoding tag data doesn't use all the chars in the packet
550 check_qmemtags(client, server, read_len: 32, type: 1, packet: "qMemTags:def0,20:1", response: "m09zz",
551 expected_tag_data: std::nullopt);
552 // Data that is not hex bytes
553 check_qmemtags(client, server, read_len: 32, type: 1, packet: "qMemTags:def0,20:1", response: "mhello",
554 expected_tag_data: std::nullopt);
555 // Data is not a complete hex char
556 check_qmemtags(client, server, read_len: 32, type: 1, packet: "qMemTags:def0,20:1", response: "m9",
557 expected_tag_data: std::nullopt);
558 // Data has a trailing hex char
559 check_qmemtags(client, server, read_len: 32, type: 1, packet: "qMemTags:def0,20:1", response: "m01020",
560 expected_tag_data: std::nullopt);
561}
562
563static 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(policy: std::launch::async, fn: [&] {
569 return client.WriteMemoryTags(addr, len, type, tags);
570 });
571
572 HandlePacket(server, expected: 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
583TEST_F(GDBRemoteCommunicationClientTest, WriteMemoryTags) {
584 check_Qmemtags(client, server, addr: 0xABCD, len: 0x20, type: 1,
585 tags: std::vector<uint8_t>{0x12, 0x34}, packet: "QMemTags:abcd,20:1:1234",
586 response: "OK", should_succeed: 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, addr: 0x4321, len: 0x20, type: 9, tags: std::vector<uint8_t>{},
591 packet: "QMemTags:4321,20:9:", response: "OK", should_succeed: true);
592
593 check_Qmemtags(client, server, addr: 0x8877, len: 0x123, type: 0x34,
594 tags: std::vector<uint8_t>{0x55, 0x66, 0x77},
595 packet: "QMemTags:8877,123:34:556677", response: "E01", should_succeed: 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, addr: 0x456789, len: 0, type: -1, tags: std::vector<uint8_t>{0x99},
600 packet: "QMemTags:456789,0:ffffffff:99", response: "E03", should_succeed: false);
601 check_Qmemtags(client, server, addr: 0x456789, len: 0,
602 type: std::numeric_limits<int32_t>::max(),
603 tags: std::vector<uint8_t>{0x99}, packet: "QMemTags:456789,0:7fffffff:99",
604 response: "E03", should_succeed: false);
605 check_Qmemtags(client, server, addr: 0x456789, len: 0,
606 type: std::numeric_limits<int32_t>::min(),
607 tags: std::vector<uint8_t>{0x99}, packet: "QMemTags:456789,0:80000000:99",
608 response: "E03", should_succeed: 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
615TEST_F(GDBRemoteCommunicationClientTest, CalculateMD5) {
616 FileSpec file_spec("/foo/bar", FileSpec::Style::posix);
617 std::future<ErrorOr<MD5::MD5Result>> async_result = std::async(
618 policy: std::launch::async, fn: [&] { return client.CalculateMD5(file_spec); });
619
620 lldb_private::StreamString stream;
621 stream.PutCString(cstr: "vFile:MD5:");
622 stream.PutStringAsRawHex8(s: file_spec.GetPath(denormalize: false));
623 HandlePacket(server, expected: stream.GetString().str(),
624 response: "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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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