llvm-project/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
David Spickett 555cd03193 [lldb] Correct format of qMemTags type field
The type field is a signed integer.
(https://sourceware.org/gdb/current/onlinedocs/gdb/General-Query-Packets.html)

However it's not packed in the packet in the way
you might think. For example the type -1 should be:
qMemTags:<addr>,<len>:ffffffff
Instead of:
qMemTags:<addr>,<len>:-1

This change makes lldb-server's parsing more strict
and adds more tests to check that we handle negative types
correctly in lldb and lldb-server.

We only support one tag type value at this point,
for AArch64 MTE, which is positive. So this doesn't change
any of those interactions. It just brings us in line with GDB.

Also check that the test target has MTE. Previously
we just checked that we were AArch64 with a toolchain
that supports MTE.

Finally, update the tag type check for QMemTags to use
the same conversion steps that qMemTags now does.
Using static_cast can invoke UB and though we do do a limit
check to avoid this, I think it's clearer with the new method.

Reviewed By: omjavaid

Differential Revision: https://reviews.llvm.org/D104914
2021-07-30 11:06:57 +01:00

594 lines
22 KiB
C++

//===-- GDBRemoteCommunicationClientTest.cpp ------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h"
#include "GDBRemoteTestUtils.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Host/XML.h"
#include "lldb/Target/MemoryRegionInfo.h"
#include "lldb/Utility/DataBuffer.h"
#include "lldb/Utility/StructuredData.h"
#include "lldb/lldb-enumerations.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include <future>
#include <limits>
using namespace lldb_private::process_gdb_remote;
using namespace lldb_private;
using namespace lldb;
using namespace llvm;
namespace {
typedef GDBRemoteCommunication::PacketResult PacketResult;
struct TestClient : public GDBRemoteCommunicationClient {
TestClient() { m_send_acks = false; }
};
void Handle_QThreadSuffixSupported(MockServer &server, bool supported) {
StringExtractorGDBRemote request;
ASSERT_EQ(PacketResult::Success, server.GetPacket(request));
ASSERT_EQ("QThreadSuffixSupported", request.GetStringRef());
if (supported)
ASSERT_EQ(PacketResult::Success, server.SendOKResponse());
else
ASSERT_EQ(PacketResult::Success, server.SendUnimplementedResponse(nullptr));
}
void HandlePacket(MockServer &server,
const testing::Matcher<const std::string &> &expected,
StringRef response) {
StringExtractorGDBRemote request;
ASSERT_EQ(PacketResult::Success, server.GetPacket(request));
ASSERT_THAT(std::string(request.GetStringRef()), expected);
ASSERT_EQ(PacketResult::Success, server.SendPacket(response));
}
uint8_t all_registers[] = {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'};
std::string all_registers_hex = "404142434445464748494a4b4c4d4e4f";
uint8_t one_register[] = {'A', 'B', 'C', 'D'};
std::string one_register_hex = "41424344";
} // end anonymous namespace
class GDBRemoteCommunicationClientTest : public GDBRemoteTest {
public:
void SetUp() override {
ASSERT_THAT_ERROR(GDBRemoteCommunication::ConnectLocally(client, server),
llvm::Succeeded());
}
protected:
TestClient client;
MockServer server;
};
TEST_F(GDBRemoteCommunicationClientTest, WriteRegister) {
const lldb::tid_t tid = 0x47;
const uint32_t reg_num = 4;
std::future<bool> write_result = std::async(std::launch::async, [&] {
return client.WriteRegister(tid, reg_num, one_register);
});
Handle_QThreadSuffixSupported(server, true);
HandlePacket(server, "P4=" + one_register_hex + ";thread:0047;", "OK");
ASSERT_TRUE(write_result.get());
write_result = std::async(std::launch::async, [&] {
return client.WriteAllRegisters(tid, all_registers);
});
HandlePacket(server, "G" + all_registers_hex + ";thread:0047;", "OK");
ASSERT_TRUE(write_result.get());
}
TEST_F(GDBRemoteCommunicationClientTest, WriteRegisterNoSuffix) {
const lldb::tid_t tid = 0x47;
const uint32_t reg_num = 4;
std::future<bool> write_result = std::async(std::launch::async, [&] {
return client.WriteRegister(tid, reg_num, one_register);
});
Handle_QThreadSuffixSupported(server, false);
HandlePacket(server, "Hg47", "OK");
HandlePacket(server, "P4=" + one_register_hex, "OK");
ASSERT_TRUE(write_result.get());
write_result = std::async(std::launch::async, [&] {
return client.WriteAllRegisters(tid, all_registers);
});
HandlePacket(server, "G" + all_registers_hex, "OK");
ASSERT_TRUE(write_result.get());
}
TEST_F(GDBRemoteCommunicationClientTest, ReadRegister) {
const lldb::tid_t tid = 0x47;
const uint32_t reg_num = 4;
std::future<bool> async_result = std::async(
std::launch::async, [&] { return client.GetpPacketSupported(tid); });
Handle_QThreadSuffixSupported(server, true);
HandlePacket(server, "p0;thread:0047;", one_register_hex);
ASSERT_TRUE(async_result.get());
std::future<DataBufferSP> read_result = std::async(
std::launch::async, [&] { return client.ReadRegister(tid, reg_num); });
HandlePacket(server, "p4;thread:0047;", "41424344");
auto buffer_sp = read_result.get();
ASSERT_TRUE(bool(buffer_sp));
ASSERT_EQ(0,
memcmp(buffer_sp->GetBytes(), one_register, sizeof one_register));
read_result = std::async(std::launch::async,
[&] { return client.ReadAllRegisters(tid); });
HandlePacket(server, "g;thread:0047;", all_registers_hex);
buffer_sp = read_result.get();
ASSERT_TRUE(bool(buffer_sp));
ASSERT_EQ(0,
memcmp(buffer_sp->GetBytes(), all_registers, sizeof all_registers));
}
TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) {
const lldb::tid_t tid = 0x47;
uint32_t save_id;
std::future<bool> async_result = std::async(std::launch::async, [&] {
return client.SaveRegisterState(tid, save_id);
});
Handle_QThreadSuffixSupported(server, false);
HandlePacket(server, "Hg47", "OK");
HandlePacket(server, "QSaveRegisterState", "1");
ASSERT_TRUE(async_result.get());
EXPECT_EQ(1u, save_id);
async_result = std::async(std::launch::async, [&] {
return client.RestoreRegisterState(tid, save_id);
});
HandlePacket(server, "QRestoreRegisterState:1", "OK");
ASSERT_TRUE(async_result.get());
}
TEST_F(GDBRemoteCommunicationClientTest, SyncThreadState) {
const lldb::tid_t tid = 0x47;
std::future<bool> async_result = std::async(
std::launch::async, [&] { return client.SyncThreadState(tid); });
HandlePacket(server, "qSyncThreadStateSupported", "OK");
HandlePacket(server, "QSyncThreadState:0047;", "OK");
ASSERT_TRUE(async_result.get());
}
TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo) {
llvm::Triple triple("i386-pc-linux");
FileSpec file_specs[] = {
FileSpec("/foo/bar.so", FileSpec::Style::posix),
FileSpec("/foo/baz.so", FileSpec::Style::posix),
// This is a bit dodgy but we currently depend on GetModulesInfo not
// performing denormalization. It can go away once the users
// (DynamicLoaderPOSIXDYLD, at least) correctly set the path syntax for
// the FileSpecs they create.
FileSpec("/foo/baw.so", FileSpec::Style::windows),
};
std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result =
std::async(std::launch::async,
[&] { return client.GetModulesInfo(file_specs, triple); });
HandlePacket(
server, "jModulesInfo:["
R"({"file":"/foo/bar.so","triple":"i386-pc-linux"},)"
R"({"file":"/foo/baz.so","triple":"i386-pc-linux"},)"
R"({"file":"/foo/baw.so","triple":"i386-pc-linux"}])",
R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])");
auto result = async_result.get();
ASSERT_TRUE(result.hasValue());
ASSERT_EQ(1u, result->size());
EXPECT_EQ("/foo/bar.so", result.getValue()[0].GetFileSpec().GetPath());
EXPECT_EQ(triple, result.getValue()[0].GetArchitecture().GetTriple());
EXPECT_EQ(UUID::fromData("@ABCDEFGHIJKLMNO", 16),
result.getValue()[0].GetUUID());
EXPECT_EQ(0u, result.getValue()[0].GetObjectOffset());
EXPECT_EQ(1234u, result.getValue()[0].GetObjectSize());
}
TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo_UUID20) {
llvm::Triple triple("i386-pc-linux");
FileSpec file_spec("/foo/bar.so", FileSpec::Style::posix);
std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result =
std::async(std::launch::async,
[&] { return client.GetModulesInfo(file_spec, triple); });
HandlePacket(
server,
"jModulesInfo:["
R"({"file":"/foo/bar.so","triple":"i386-pc-linux"}])",
R"([{"uuid":"404142434445464748494a4b4c4d4e4f50515253","triple":"i386-pc-linux",)"
R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])");
auto result = async_result.get();
ASSERT_TRUE(result.hasValue());
ASSERT_EQ(1u, result->size());
EXPECT_EQ("/foo/bar.so", result.getValue()[0].GetFileSpec().GetPath());
EXPECT_EQ(triple, result.getValue()[0].GetArchitecture().GetTriple());
EXPECT_EQ(UUID::fromData("@ABCDEFGHIJKLMNOPQRS", 20),
result.getValue()[0].GetUUID());
EXPECT_EQ(0u, result.getValue()[0].GetObjectOffset());
EXPECT_EQ(1234u, result.getValue()[0].GetObjectSize());
}
TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse) {
llvm::Triple triple("i386-pc-linux");
FileSpec file_spec("/foo/bar.so", FileSpec::Style::posix);
const char *invalid_responses[] = {
// no UUID
R"([{"triple":"i386-pc-linux",)"
R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",
// invalid UUID
R"([{"uuid":"XXXXXX","triple":"i386-pc-linux",)"
R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",
// no triple
R"([{"uuid":"404142434445464748494a4b4c4d4e4f",)"
R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])",
// no file_path
R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
R"("file_offset":0,"file_size":1234}]])",
// no file_offset
R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
R"("file_path":"/foo/bar.so","file_size":1234}]])",
// no file_size
R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
R"("file_path":"/foo/bar.so","file_offset":0}]])",
};
for (const char *response : invalid_responses) {
std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result =
std::async(std::launch::async,
[&] { return client.GetModulesInfo(file_spec, triple); });
HandlePacket(
server,
R"(jModulesInfo:[{"file":"/foo/bar.so","triple":"i386-pc-linux"}])",
response);
auto result = async_result.get();
ASSERT_TRUE(result);
ASSERT_EQ(0u, result->size()) << "response was: " << response;
}
}
TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) {
std::thread server_thread([this] {
for (;;) {
StringExtractorGDBRemote request;
PacketResult result = server.GetPacket(request);
if (result == PacketResult::ErrorDisconnected)
return;
ASSERT_EQ(PacketResult::Success, result);
StringRef ref = request.GetStringRef();
ASSERT_TRUE(ref.consume_front("qSpeedTest:response_size:"));
int size;
ASSERT_FALSE(ref.consumeInteger(10, size)) << "ref: " << ref;
std::string response(size, 'X');
ASSERT_EQ(PacketResult::Success, server.SendPacket(response));
}
});
StreamString ss;
client.TestPacketSpeed(10, 32, 32, 4096, true, ss);
client.Disconnect();
server_thread.join();
GTEST_LOG_(INFO) << "Formatted output: " << ss.GetData();
auto object_sp = StructuredData::ParseJSON(std::string(ss.GetString()));
ASSERT_TRUE(bool(object_sp));
auto dict_sp = object_sp->GetAsDictionary();
ASSERT_TRUE(bool(dict_sp));
object_sp = dict_sp->GetValueForKey("packet_speeds");
ASSERT_TRUE(bool(object_sp));
dict_sp = object_sp->GetAsDictionary();
ASSERT_TRUE(bool(dict_sp));
int num_packets;
ASSERT_TRUE(dict_sp->GetValueForKeyAsInteger("num_packets", num_packets))
<< ss.GetString();
ASSERT_EQ(10, num_packets);
}
TEST_F(GDBRemoteCommunicationClientTest, SendSignalsToIgnore) {
std::future<Status> result = std::async(std::launch::async, [&] {
return client.SendSignalsToIgnore({2, 3, 5, 7, 0xB, 0xD, 0x11});
});
HandlePacket(server, "QPassSignals:02;03;05;07;0b;0d;11", "OK");
EXPECT_TRUE(result.get().Success());
result = std::async(std::launch::async, [&] {
return client.SendSignalsToIgnore(std::vector<int32_t>());
});
HandlePacket(server, "QPassSignals:", "OK");
EXPECT_TRUE(result.get().Success());
}
TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) {
const lldb::addr_t addr = 0xa000;
MemoryRegionInfo region_info;
std::future<Status> result = std::async(std::launch::async, [&] {
return client.GetMemoryRegionInfo(addr, region_info);
});
HandlePacket(server,
"qMemoryRegionInfo:a000",
"start:a000;size:2000;permissions:rx;name:2f666f6f2f6261722e736f;");
if (XMLDocument::XMLEnabled()) {
// In case we have XML support, this will also do a "qXfer:memory-map".
// Preceeded by a query for supported extensions. Pretend we don't support
// that.
HandlePacket(server, testing::StartsWith("qSupported:"), "");
}
EXPECT_TRUE(result.get().Success());
EXPECT_EQ(addr, region_info.GetRange().GetRangeBase());
EXPECT_EQ(0x2000u, region_info.GetRange().GetByteSize());
EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetReadable());
EXPECT_EQ(MemoryRegionInfo::eNo, region_info.GetWritable());
EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetExecutable());
EXPECT_EQ("/foo/bar.so", region_info.GetName().GetStringRef());
EXPECT_EQ(MemoryRegionInfo::eDontKnow, region_info.GetMemoryTagged());
result = std::async(std::launch::async, [&] {
return client.GetMemoryRegionInfo(addr, region_info);
});
HandlePacket(server, "qMemoryRegionInfo:a000",
"start:a000;size:2000;flags:;");
EXPECT_TRUE(result.get().Success());
EXPECT_EQ(MemoryRegionInfo::eNo, region_info.GetMemoryTagged());
result = std::async(std::launch::async, [&] {
return client.GetMemoryRegionInfo(addr, region_info);
});
HandlePacket(server, "qMemoryRegionInfo:a000",
"start:a000;size:2000;flags: mt zz mt ;");
EXPECT_TRUE(result.get().Success());
EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetMemoryTagged());
}
TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) {
const lldb::addr_t addr = 0x4000;
MemoryRegionInfo region_info;
std::future<Status> result = std::async(std::launch::async, [&] {
return client.GetMemoryRegionInfo(addr, region_info);
});
HandlePacket(server, "qMemoryRegionInfo:4000", "start:4000;size:0000;");
if (XMLDocument::XMLEnabled()) {
// In case we have XML support, this will also do a "qXfer:memory-map".
// Preceeded by a query for supported extensions. Pretend we don't support
// that.
HandlePacket(server, testing::StartsWith("qSupported:"), "");
}
EXPECT_FALSE(result.get().Success());
}
TEST_F(GDBRemoteCommunicationClientTest, SendTraceSupportedPacket) {
TraceSupportedResponse trace_type;
std::string error_message;
auto callback = [&] {
std::chrono::seconds timeout(10);
if (llvm::Expected<TraceSupportedResponse> trace_type_or_err =
client.SendTraceSupported(timeout)) {
trace_type = *trace_type_or_err;
error_message = "";
return true;
} else {
trace_type = {};
error_message = llvm::toString(trace_type_or_err.takeError());
return false;
}
};
// Success response
{
std::future<bool> result = std::async(std::launch::async, callback);
HandlePacket(
server, "jLLDBTraceSupported",
R"({"name":"intel-pt","description":"Intel Processor Trace"}])");
EXPECT_TRUE(result.get());
ASSERT_STREQ(trace_type.name.c_str(), "intel-pt");
ASSERT_STREQ(trace_type.description.c_str(), "Intel Processor Trace");
}
// Error response - wrong json
{
std::future<bool> result = std::async(std::launch::async, callback);
HandlePacket(server, "jLLDBTraceSupported", R"({"type":"intel-pt"}])");
EXPECT_FALSE(result.get());
ASSERT_STREQ(error_message.c_str(), "missing value at TraceSupportedResponse.description");
}
// Error response
{
std::future<bool> result = std::async(std::launch::async, callback);
HandlePacket(server, "jLLDBTraceSupported", "E23");
EXPECT_FALSE(result.get());
}
// Error response with error message
{
std::future<bool> result = std::async(std::launch::async, callback);
HandlePacket(server, "jLLDBTraceSupported",
"E23;50726F63657373206E6F742072756E6E696E672E");
EXPECT_FALSE(result.get());
ASSERT_STREQ(error_message.c_str(), "Process not running.");
}
}
TEST_F(GDBRemoteCommunicationClientTest, GetQOffsets) {
const auto &GetQOffsets = [&](llvm::StringRef response) {
std::future<Optional<QOffsets>> result = std::async(
std::launch::async, [&] { return client.GetQOffsets(); });
HandlePacket(server, "qOffsets", response);
return result.get();
};
EXPECT_EQ((QOffsets{false, {0x1234, 0x1234}}),
GetQOffsets("Text=1234;Data=1234"));
EXPECT_EQ((QOffsets{false, {0x1234, 0x1234, 0x1234}}),
GetQOffsets("Text=1234;Data=1234;Bss=1234"));
EXPECT_EQ((QOffsets{true, {0x1234}}), GetQOffsets("TextSeg=1234"));
EXPECT_EQ((QOffsets{true, {0x1234, 0x2345}}),
GetQOffsets("TextSeg=1234;DataSeg=2345"));
EXPECT_EQ(llvm::None, GetQOffsets("E05"));
EXPECT_EQ(llvm::None, GetQOffsets("Text=bogus"));
EXPECT_EQ(llvm::None, GetQOffsets("Text=1234"));
EXPECT_EQ(llvm::None, GetQOffsets("Text=1234;Data=1234;"));
EXPECT_EQ(llvm::None, GetQOffsets("Text=1234;Data=1234;Bss=1234;"));
EXPECT_EQ(llvm::None, GetQOffsets("TEXTSEG=1234"));
EXPECT_EQ(llvm::None, GetQOffsets("TextSeg=0x1234"));
EXPECT_EQ(llvm::None, GetQOffsets("TextSeg=12345678123456789"));
}
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) {
const auto &ReadMemoryTags = [&]() {
std::future<DataBufferSP> result = std::async(std::launch::async, [&] {
return client.ReadMemoryTags(0xDEF0, read_len, type);
});
HandlePacket(server, packet, response);
return result.get();
};
auto result = ReadMemoryTags();
if (expected_tag_data) {
ASSERT_TRUE(result);
llvm::ArrayRef<uint8_t> expected(*expected_tag_data);
llvm::ArrayRef<uint8_t> got = result->GetData();
ASSERT_THAT(expected, testing::ContainerEq(got));
} else {
ASSERT_FALSE(result);
}
}
TEST_F(GDBRemoteCommunicationClientTest, ReadMemoryTags) {
// Zero length reads are valid
check_qmemtags(client, server, 0, 1, "qMemTags:def0,0:1", "m",
std::vector<uint8_t>{});
// Type can be negative. Put into the packet as the raw bytes
// (as opposed to a literal -1)
check_qmemtags(client, server, 0, -1, "qMemTags:def0,0:ffffffff", "m",
std::vector<uint8_t>{});
check_qmemtags(client, server, 0, std::numeric_limits<int32_t>::min(),
"qMemTags:def0,0:80000000", "m", std::vector<uint8_t>{});
check_qmemtags(client, server, 0, std::numeric_limits<int32_t>::max(),
"qMemTags:def0,0:7fffffff", "m", std::vector<uint8_t>{});
// The client layer does not check the length of the received data.
// All we need is the "m" and for the decode to use all of the chars
check_qmemtags(client, server, 32, 2, "qMemTags:def0,20:2", "m09",
std::vector<uint8_t>{0x9});
// Zero length response is fine as long as the "m" is present
check_qmemtags(client, server, 0, 0x34, "qMemTags:def0,0:34", "m",
std::vector<uint8_t>{});
// Normal responses
check_qmemtags(client, server, 16, 1, "qMemTags:def0,10:1", "m66",
std::vector<uint8_t>{0x66});
check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m0102",
std::vector<uint8_t>{0x1, 0x2});
// Empty response is an error
check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "", llvm::None);
// Usual error response
check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "E01",
llvm::None);
// Leading m missing
check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "01", llvm::None);
// Anything other than m is an error
check_qmemtags(client, server, 17, 1, "qMemTags:def0,11:1", "z01",
llvm::None);
// Decoding tag data doesn't use all the chars in the packet
check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m09zz",
llvm::None);
// Data that is not hex bytes
check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "mhello",
llvm::None);
// Data is not a complete hex char
check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m9", llvm::None);
// Data has a trailing hex char
check_qmemtags(client, server, 32, 1, "qMemTags:def0,20:1", "m01020",
llvm::None);
}
static void 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) {
const auto &WriteMemoryTags = [&]() {
std::future<Status> result = std::async(std::launch::async, [&] {
return client.WriteMemoryTags(addr, len, type, tags);
});
HandlePacket(server, packet, response);
return result.get();
};
auto result = WriteMemoryTags();
if (should_succeed)
ASSERT_TRUE(result.Success());
else
ASSERT_TRUE(result.Fail());
}
TEST_F(GDBRemoteCommunicationClientTest, WriteMemoryTags) {
check_Qmemtags(client, server, 0xABCD, 0x20, 1,
std::vector<uint8_t>{0x12, 0x34}, "QMemTags:abcd,20:1:1234",
"OK", true);
// The GDB layer doesn't care that the number of tags !=
// the length of the write.
check_Qmemtags(client, server, 0x4321, 0x20, 9, std::vector<uint8_t>{},
"QMemTags:4321,20:9:", "OK", true);
check_Qmemtags(client, server, 0x8877, 0x123, 0x34,
std::vector<uint8_t>{0x55, 0x66, 0x77},
"QMemTags:8877,123:34:556677", "E01", false);
// Type is a signed integer but is packed as its raw bytes,
// instead of having a +/-.
check_Qmemtags(client, server, 0x456789, 0, -1, std::vector<uint8_t>{0x99},
"QMemTags:456789,0:ffffffff:99", "E03", false);
check_Qmemtags(client, server, 0x456789, 0,
std::numeric_limits<int32_t>::max(),
std::vector<uint8_t>{0x99}, "QMemTags:456789,0:7fffffff:99",
"E03", false);
check_Qmemtags(client, server, 0x456789, 0,
std::numeric_limits<int32_t>::min(),
std::vector<uint8_t>{0x99}, "QMemTags:456789,0:80000000:99",
"E03", false);
}