668 lines · cpp
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-exception6//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 else44 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 namespace63 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>(std::move(pair->first)));71 server.SetConnection(72 std::make_unique<ConnectionFileDescriptor>(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(std::launch::async, [&] {84 return client.WriteRegister(tid, reg_num, one_register);85 });86 87 Handle_QThreadSuffixSupported(server, true);88 89 HandlePacket(server, "P4=" + one_register_hex + ";thread:0047;", "OK");90 ASSERT_TRUE(write_result.get());91 92 write_result = std::async(std::launch::async, [&] {93 return client.WriteAllRegisters(tid, all_registers);94 });95 96 HandlePacket(server, "G" + all_registers_hex + ";thread:0047;", "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(std::launch::async, [&] {104 return client.WriteRegister(tid, reg_num, one_register);105 });106 107 Handle_QThreadSuffixSupported(server, false);108 HandlePacket(server, "Hg47", "OK");109 HandlePacket(server, "P4=" + one_register_hex, "OK");110 ASSERT_TRUE(write_result.get());111 112 write_result = std::async(std::launch::async, [&] {113 return client.WriteAllRegisters(tid, all_registers);114 });115 116 HandlePacket(server, "G" + all_registers_hex, "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 std::launch::async, [&] { return client.GetpPacketSupported(tid); });125 Handle_QThreadSuffixSupported(server, true);126 HandlePacket(server, "p0;thread:0047;", one_register_hex);127 ASSERT_TRUE(async_result.get());128 129 std::future<DataBufferSP> read_result = std::async(130 std::launch::async, [&] { return client.ReadRegister(tid, reg_num); });131 HandlePacket(server, "p4;thread:0047;", "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(std::launch::async,138 [&] { return client.ReadAllRegisters(tid); });139 HandlePacket(server, "g;thread:0047;", 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(std::launch::async, [&] {150 return client.SaveRegisterState(tid, save_id);151 });152 Handle_QThreadSuffixSupported(server, false);153 HandlePacket(server, "Hg47", "OK");154 HandlePacket(server, "QSaveRegisterState", "1");155 ASSERT_TRUE(async_result.get());156 EXPECT_EQ(1u, save_id);157 158 async_result = std::async(std::launch::async, [&] {159 return client.RestoreRegisterState(tid, save_id);160 });161 HandlePacket(server, "QRestoreRegisterState:1", "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 std::launch::async, [&] { return client.SyncThreadState(tid); });169 HandlePacket(server, "qSyncThreadStateSupported", "OK");170 HandlePacket(server, "QSyncThreadState:0047;", "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 not182 // performing denormalization. It can go away once the users183 // (DynamicLoaderPOSIXDYLD, at least) correctly set the path syntax for184 // 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(std::launch::async,189 [&] { return client.GetModulesInfo(file_specs, triple); });190 HandlePacket(191 server, "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 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(std::launch::async,214 [&] { return client.GetModulesInfo(file_spec, triple); });215 HandlePacket(216 server,217 "jModulesInfo:["218 R"({"file":"/foo/bar.so","triple":"i386-pc-linux"}])",219 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 UUID238 R"([{"triple":"i386-pc-linux",)"239 R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",240 // invalid UUID241 R"([{"uuid":"XXXXXX","triple":"i386-pc-linux",)"242 R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",243 // no triple244 R"([{"uuid":"404142434445464748494a4b4c4d4e4f",)"245 R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",246 // no file_path247 R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"248 R"("file_offset":0,"file_size":1234}]])",249 // no file_offset250 R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"251 R"("file_path":"/foo/bar.so","file_size":1234}]])",252 // no file_size253 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(std::launch::async,260 [&] { return client.GetModulesInfo(file_spec, triple); });261 HandlePacket(262 server,263 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(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(10, 32, 32, 4096, true, ss);291 client.Disconnect();292 server_thread.join();293 294 GTEST_LOG_(INFO) << "Formatted output: " << ss.GetData();295 auto object_sp = StructuredData::ParseJSON(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("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(std::launch::async, [&] {313 return client.SendSignalsToIgnore({2, 3, 5, 7, 0xB, 0xD, 0x11});314 });315 316 HandlePacket(server, "QPassSignals:02;03;05;07;0b;0d;11", "OK");317 EXPECT_TRUE(result.get().Success());318 319 result = std::async(std::launch::async, [&] {320 return client.SendSignalsToIgnore(std::vector<int32_t>());321 });322 323 HandlePacket(server, "QPassSignals:", "OK");324 EXPECT_TRUE(result.get().Success());325}326 327TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) {328 const lldb::addr_t addr = 0xa000;329 lldb_private::MemoryRegionInfo region_info;330 std::future<Status> result = std::async(std::launch::async, [&] {331 return client.GetMemoryRegionInfo(addr, region_info);332 });333 334 HandlePacket(server,335 "qMemoryRegionInfo:a000",336 "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 support340 // that.341 HandlePacket(server, testing::StartsWith("qSupported:"), "");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(lldb_private::MemoryRegionInfo::eYes, region_info.GetReadable());347 EXPECT_EQ(lldb_private::MemoryRegionInfo::eNo, region_info.GetWritable());348 EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.GetExecutable());349 EXPECT_EQ("/foo/bar.so", region_info.GetName().GetStringRef());350 EXPECT_EQ(lldb_private::MemoryRegionInfo::eDontKnow,351 region_info.GetMemoryTagged());352 EXPECT_EQ(lldb_private::MemoryRegionInfo::eDontKnow,353 region_info.IsStackMemory());354 EXPECT_EQ(lldb_private::MemoryRegionInfo::eDontKnow,355 region_info.IsShadowStack());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:;type:stack;");363 EXPECT_TRUE(result.get().Success());364 EXPECT_EQ(lldb_private::MemoryRegionInfo::eNo, region_info.GetMemoryTagged());365 EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.IsStackMemory());366 EXPECT_EQ(lldb_private::MemoryRegionInfo::eNo, region_info.IsShadowStack());367 368 result = std::async(std::launch::async, [&] {369 return client.GetMemoryRegionInfo(addr, region_info);370 });371 372 HandlePacket(server, "qMemoryRegionInfo:a000",373 "start:a000;size:2000;flags: mt zz mt ss ;type:ha,ha,stack;");374 EXPECT_TRUE(result.get().Success());375 EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes,376 region_info.GetMemoryTagged());377 EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.IsStackMemory());378 EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.IsShadowStack());379 380 result = std::async(std::launch::async, [&] {381 return client.GetMemoryRegionInfo(addr, region_info);382 });383 384 HandlePacket(server, "qMemoryRegionInfo:a000",385 "start:a000;size:2000;type:heap;");386 EXPECT_TRUE(result.get().Success());387 EXPECT_EQ(lldb_private::MemoryRegionInfo::eNo, region_info.IsStackMemory());388}389 390TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) {391 const lldb::addr_t addr = 0x4000;392 lldb_private::MemoryRegionInfo region_info;393 std::future<Status> result = std::async(std::launch::async, [&] {394 return client.GetMemoryRegionInfo(addr, region_info);395 });396 397 HandlePacket(server, "qMemoryRegionInfo:4000", "start:4000;size:0000;");398 if (XMLDocument::XMLEnabled()) {399 // In case we have XML support, this will also do a "qXfer:memory-map".400 // Preceeded by a query for supported extensions. Pretend we don't support401 // that.402 HandlePacket(server, testing::StartsWith("qSupported:"), "");403 }404 EXPECT_FALSE(result.get().Success());405}406 407TEST_F(GDBRemoteCommunicationClientTest, SendTraceSupportedPacket) {408 TraceSupportedResponse trace_type;409 std::string error_message;410 auto callback = [&] {411 std::chrono::seconds timeout(10);412 if (llvm::Expected<TraceSupportedResponse> trace_type_or_err =413 client.SendTraceSupported(timeout)) {414 trace_type = *trace_type_or_err;415 error_message = "";416 return true;417 } else {418 trace_type = {};419 error_message = llvm::toString(trace_type_or_err.takeError());420 return false;421 }422 };423 424 // Success response425 {426 std::future<bool> result = std::async(std::launch::async, callback);427 428 HandlePacket(429 server, "jLLDBTraceSupported",430 R"({"name":"intel-pt","description":"Intel Processor Trace"}])");431 432 EXPECT_TRUE(result.get());433 ASSERT_STREQ(trace_type.name.c_str(), "intel-pt");434 ASSERT_STREQ(trace_type.description.c_str(), "Intel Processor Trace");435 }436 437 // Error response - wrong json438 {439 std::future<bool> result = std::async(std::launch::async, callback);440 441 HandlePacket(server, "jLLDBTraceSupported", R"({"type":"intel-pt"}])");442 443 EXPECT_FALSE(result.get());444 ASSERT_STREQ(error_message.c_str(), "missing value at TraceSupportedResponse.description");445 }446 447 // Error response448 {449 std::future<bool> result = std::async(std::launch::async, callback);450 451 HandlePacket(server, "jLLDBTraceSupported", "E23");452 453 EXPECT_FALSE(result.get());454 }455 456 // Error response with error message457 {458 std::future<bool> result = std::async(std::launch::async, callback);459 460 HandlePacket(server, "jLLDBTraceSupported",461 "E23;50726F63657373206E6F742072756E6E696E672E");462 463 EXPECT_FALSE(result.get());464 ASSERT_STREQ(error_message.c_str(), "Process not running.");465 }466}467 468TEST_F(GDBRemoteCommunicationClientTest, GetQOffsets) {469 const auto &GetQOffsets = [&](llvm::StringRef response) {470 std::future<std::optional<QOffsets>> result =471 std::async(std::launch::async, [&] { return client.GetQOffsets(); });472 473 HandlePacket(server, "qOffsets", response);474 return result.get();475 };476 EXPECT_EQ((QOffsets{false, {0x1234, 0x1234}}),477 GetQOffsets("Text=1234;Data=1234"));478 EXPECT_EQ((QOffsets{false, {0x1234, 0x1234, 0x1234}}),479 GetQOffsets("Text=1234;Data=1234;Bss=1234"));480 EXPECT_EQ((QOffsets{true, {0x1234}}), GetQOffsets("TextSeg=1234"));481 EXPECT_EQ((QOffsets{true, {0x1234, 0x2345}}),482 GetQOffsets("TextSeg=1234;DataSeg=2345"));483 484 EXPECT_EQ(std::nullopt, GetQOffsets("E05"));485 EXPECT_EQ(std::nullopt, GetQOffsets("Text=bogus"));486 EXPECT_EQ(std::nullopt, GetQOffsets("Text=1234"));487 EXPECT_EQ(std::nullopt, GetQOffsets("Text=1234;Data=1234;"));488 EXPECT_EQ(std::nullopt, GetQOffsets("Text=1234;Data=1234;Bss=1234;"));489 EXPECT_EQ(std::nullopt, GetQOffsets("TEXTSEG=1234"));490 EXPECT_EQ(std::nullopt, GetQOffsets("TextSeg=0x1234"));491 EXPECT_EQ(std::nullopt, GetQOffsets("TextSeg=12345678123456789"));492}493 494static void495check_qmemtags(TestClient &client, MockServer &server, size_t read_len,496 int32_t type, const char *packet, llvm::StringRef response,497 std::optional<std::vector<uint8_t>> expected_tag_data) {498 const auto &ReadMemoryTags = [&]() {499 std::future<DataBufferSP> result = std::async(std::launch::async, [&] {500 return client.ReadMemoryTags(0xDEF0, read_len, type);501 });502 503 HandlePacket(server, packet, response);504 return result.get();505 };506 507 auto result = ReadMemoryTags();508 if (expected_tag_data) {509 ASSERT_TRUE(result);510 llvm::ArrayRef<uint8_t> expected(*expected_tag_data);511 llvm::ArrayRef<uint8_t> got = result->GetData();512 ASSERT_THAT(expected, testing::ContainerEq(got));513 } else {514 ASSERT_FALSE(result);515 }516}517 518TEST_F(GDBRemoteCommunicationClientTest, ReadMemoryTags) {519 // Zero length reads are valid520 check_qmemtags(client, server, 0, 1, "qMemTags:def0,0:1", "m",521 std::vector<uint8_t>{});522 523 // Type can be negative. Put into the packet as the raw bytes524 // (as opposed to a literal -1)525 check_qmemtags(client, server, 0, -1, "qMemTags:def0,0:ffffffff", "m",526 std::vector<uint8_t>{});527 check_qmemtags(client, server, 0, std::numeric_limits<int32_t>::min(),528 "qMemTags:def0,0:80000000", "m", std::vector<uint8_t>{});529 check_qmemtags(client, server, 0, std::numeric_limits<int32_t>::max(),530 "qMemTags:def0,0:7fffffff", "m", std::vector<uint8_t>{});531 532 // The client layer does not check the length of the received data.533 // All we need is the "m" and for the decode to use all of the chars534 check_qmemtags(client, server, 32, 2, "qMemTags:def0,20:2", "m09",535 std::vector<uint8_t>{0x9});536 537 // Zero length response is fine as long as the "m" is present538 check_qmemtags(client, server, 0, 0x34, "qMemTags:def0,0:34", "m",539 std::vector<uint8_t>{});540 541 // Normal responses542 check_qmemtags(client, server, 16, 1, "qMemTags:def0,10:1", "m66",543 std::vector<uint8_t>{0x66});544 check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m0102",545 std::vector<uint8_t>{0x1, 0x2});546 547 // Empty response is an error548 check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "", std::nullopt);549 // Usual error response550 check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "E01",551 std::nullopt);552 // Leading m missing553 check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "01",554 std::nullopt);555 // Anything other than m is an error556 check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "z01",557 std::nullopt);558 // Decoding tag data doesn't use all the chars in the packet559 check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m09zz",560 std::nullopt);561 // Data that is not hex bytes562 check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "mhello",563 std::nullopt);564 // Data is not a complete hex char565 check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m9",566 std::nullopt);567 // Data has a trailing hex char568 check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m01020",569 std::nullopt);570}571 572static void check_Qmemtags(TestClient &client, MockServer &server,573 lldb::addr_t addr, size_t len, int32_t type,574 const std::vector<uint8_t> &tags, const char *packet,575 llvm::StringRef response, bool should_succeed) {576 const auto &WriteMemoryTags = [&]() {577 std::future<Status> result = std::async(std::launch::async, [&] {578 return client.WriteMemoryTags(addr, len, type, tags);579 });580 581 HandlePacket(server, packet, response);582 return result.get();583 };584 585 auto result = WriteMemoryTags();586 if (should_succeed)587 ASSERT_TRUE(result.Success());588 else589 ASSERT_TRUE(result.Fail());590}591 592TEST_F(GDBRemoteCommunicationClientTest, WriteMemoryTags) {593 check_Qmemtags(client, server, 0xABCD, 0x20, 1,594 std::vector<uint8_t>{0x12, 0x34}, "QMemTags:abcd,20:1:1234",595 "OK", true);596 597 // The GDB layer doesn't care that the number of tags !=598 // the length of the write.599 check_Qmemtags(client, server, 0x4321, 0x20, 9, std::vector<uint8_t>{},600 "QMemTags:4321,20:9:", "OK", true);601 602 check_Qmemtags(client, server, 0x8877, 0x123, 0x34,603 std::vector<uint8_t>{0x55, 0x66, 0x77},604 "QMemTags:8877,123:34:556677", "E01", false);605 606 // Type is a signed integer but is packed as its raw bytes,607 // instead of having a +/-.608 check_Qmemtags(client, server, 0x456789, 0, -1, std::vector<uint8_t>{0x99},609 "QMemTags:456789,0:ffffffff:99", "E03", false);610 check_Qmemtags(client, server, 0x456789, 0,611 std::numeric_limits<int32_t>::max(),612 std::vector<uint8_t>{0x99}, "QMemTags:456789,0:7fffffff:99",613 "E03", false);614 check_Qmemtags(client, server, 0x456789, 0,615 std::numeric_limits<int32_t>::min(),616 std::vector<uint8_t>{0x99}, "QMemTags:456789,0:80000000:99",617 "E03", false);618}619 620// Prior to this verison, constructing a std::future for a type without a621// default constructor is not possible.622// https://developercommunity.visualstudio.com/t/c-shared-state-futuresstate-default-constructs-the/60897623#if !defined(_MSC_VER) || _MSC_VER >= 1932624TEST_F(GDBRemoteCommunicationClientTest, CalculateMD5) {625 FileSpec file_spec("/foo/bar", FileSpec::Style::posix);626 std::future<ErrorOr<MD5::MD5Result>> async_result = std::async(627 std::launch::async, [&] { return client.CalculateMD5(file_spec); });628 629 lldb_private::StreamString stream;630 stream.PutCString("vFile:MD5:");631 stream.PutStringAsRawHex8(file_spec.GetPath(false));632 HandlePacket(server, stream.GetString().str(),633 "F,"634 "deadbeef01020304"635 "05060708deadbeef");636 auto result = async_result.get();637 638 // Server and client puts/parses low, and then high639 const uint64_t expected_low = 0xdeadbeef01020304;640 const uint64_t expected_high = 0x05060708deadbeef;641 ASSERT_TRUE(result);642 EXPECT_EQ(expected_low, result->low());643 EXPECT_EQ(expected_high, result->high());644}645#endif646 647TEST_F(GDBRemoteCommunicationClientTest, MultiMemReadSupported) {648 std::future<bool> async_result = std::async(std::launch::async, [&] {649 StringExtractorGDBRemote qSupported_packet_request;650 server.GetPacket(qSupported_packet_request);651 server.SendPacket("MultiMemRead+;");652 return true;653 });654 ASSERT_TRUE(client.GetMultiMemReadSupported());655 async_result.wait();656}657 658TEST_F(GDBRemoteCommunicationClientTest, MultiMemReadNotSupported) {659 std::future<bool> async_result = std::async(std::launch::async, [&] {660 StringExtractorGDBRemote qSupported_packet_request;661 server.GetPacket(qSupported_packet_request);662 server.SendPacket(";");663 return true;664 });665 ASSERT_FALSE(client.GetMultiMemReadSupported());666 async_result.wait();667}668