292 lines · cpp
1//===-- TestClient.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 9#include "TestClient.h"10#include "TestingSupport/Host/SocketTestUtilities.h"11#include "lldb/Host/HostInfo.h"12#include "lldb/Host/common/TCPSocket.h"13#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"14#include "lldb/Utility/Args.h"15#include "llvm/ADT/StringExtras.h"16#include "llvm/Support/Path.h"17#include "llvm/Testing/Support/Error.h"18#include "gtest/gtest.h"19#include <cstdlib>20#include <future>21#include <sstream>22#include <string>23 24using namespace lldb;25using namespace lldb_private;26using namespace llvm;27using namespace llgs_tests;28 29static std::chrono::seconds GetDefaultTimeout() {30 return std::chrono::seconds{10};31}32 33TestClient::TestClient(std::unique_ptr<Connection> Conn) {34 SetConnection(std::move(Conn));35 SetPacketTimeout(GetDefaultTimeout());36}37 38TestClient::~TestClient() {39 if (!IsConnected())40 return;41 42 EXPECT_THAT_ERROR(SendMessage("k"), Succeeded());43}44 45Error TestClient::initializeConnection() {46 if (SendAck() == 0)47 return make_error<StringError>("Sending initial ACK failed.",48 inconvertibleErrorCode());49 50 if (Error E = SendMessage("QStartNoAckMode"))51 return E;52 53 m_send_acks = false;54 return Error::success();55}56 57Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log) {58 return launch(Log, {});59}60 61Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log, ArrayRef<StringRef> InferiorArgs) {62 return launchCustom(Log, false, {}, InferiorArgs);63}64 65Expected<std::unique_ptr<TestClient>>66TestClient::launchCustom(StringRef Log, bool disable_stdio,67 ArrayRef<StringRef> ServerArgs,68 ArrayRef<StringRef> InferiorArgs) {69 const ArchSpec &arch_spec = HostInfo::GetArchitecture();70 Args args;71 args.AppendArgument(LLDB_SERVER);72 if (IsLldbServer())73 args.AppendArgument("gdbserver");74 args.AppendArgument("--reverse-connect");75 76 if (!Log.empty()) {77 args.AppendArgument(("--log-file=" + Log).str());78 if (IsLldbServer())79 args.AppendArgument("--log-channels=gdb-remote packets");80 else81 args.AppendArgument("--log-flags=0x800000");82 }83 84 auto LocalhostIPOrErr = GetLocalhostIP();85 if (!LocalhostIPOrErr)86 return LocalhostIPOrErr.takeError();87 const std::string &LocalhostIP = *LocalhostIPOrErr;88 89 Status status;90 TCPSocket listen_socket(true);91 status = listen_socket.Listen(LocalhostIP + ":0", 5);92 if (status.Fail())93 return status.ToError();94 95 args.AppendArgument(96 formatv("{0}:{1}", LocalhostIP, listen_socket.GetLocalPortNumber())97 .str());98 99 for (StringRef arg : ServerArgs)100 args.AppendArgument(arg);101 102 if (!InferiorArgs.empty()) {103 args.AppendArgument("--");104 for (StringRef arg : InferiorArgs)105 args.AppendArgument(arg);106 }107 108 ProcessLaunchInfo Info;109 Info.SetArchitecture(arch_spec);110 Info.SetArguments(args, true);111 Info.GetEnvironment() = Host::GetEnvironment();112 // TODO: Use this callback to detect botched launches. If lldb-server does not113 // start, we can print a nice error message here instead of hanging in114 // Accept().115 Info.SetMonitorProcessCallback(&ProcessLaunchInfo::NoOpMonitorCallback);116 117 if (disable_stdio)118 Info.GetFlags().Set(lldb::eLaunchFlagDisableSTDIO);119 status = Host::LaunchProcess(Info);120 if (status.Fail())121 return status.ToError();122 123 Socket *accept_socket;124 if (llvm::Error E =125 listen_socket.Accept(2 * GetDefaultTimeout(), accept_socket)126 .takeError())127 return E;128 auto Conn = std::make_unique<ConnectionFileDescriptor>(129 std::unique_ptr<Socket>(accept_socket));130 auto Client = std::unique_ptr<TestClient>(new TestClient(std::move(Conn)));131 132 if (Error E = Client->initializeConnection())133 return std::move(E);134 135 if (!InferiorArgs.empty()) {136 if (Error E = Client->queryProcess())137 return std::move(E);138 }139 140 return std::move(Client);141}142 143Error TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) {144 if (SendEnvironment(Host::GetEnvironment()) != 0) {145 return make_error<StringError>("Failed to set launch environment",146 inconvertibleErrorCode());147 }148 std::stringstream command;149 command << "A";150 for (size_t i = 0; i < inferior_args.size(); i++) {151 if (i > 0)152 command << ',';153 std::string hex_encoded = toHex(inferior_args[i]);154 command << hex_encoded.size() << ',' << i << ',' << hex_encoded;155 }156 157 if (Error E = SendMessage(command.str()))158 return E;159 if (Error E = SendMessage("qLaunchSuccess"))160 return E;161 if (Error E = queryProcess())162 return E;163 return Error::success();164}165 166Error TestClient::ListThreadsInStopReply() {167 return SendMessage("QListThreadsInStopReply");168}169 170Error TestClient::SetBreakpoint(unsigned long address) {171 return SendMessage(formatv("Z0,{0:x-},1", address).str());172}173 174Error TestClient::ContinueAll() { return Continue("vCont;c"); }175 176Error TestClient::ContinueThread(unsigned long thread_id) {177 return Continue(formatv("vCont;c:{0:x-}", thread_id).str());178}179 180const llgs_tests::ProcessInfo &TestClient::GetProcessInfo() {181 return *m_process_info;182}183 184Expected<JThreadsInfo> TestClient::GetJThreadsInfo() {185 return SendMessage<JThreadsInfo>("jThreadsInfo", m_register_infos);186}187 188const StopReply &TestClient::GetLatestStopReply() {189 assert(m_stop_reply);190 return *m_stop_reply;191}192 193Error TestClient::SendMessage(StringRef message) {194 std::string dummy_string;195 return SendMessage(message, dummy_string);196}197 198Error TestClient::SendMessage(StringRef message, std::string &response_string) {199 if (Error E = SendMessage(message, response_string, PacketResult::Success))200 return E;201 StringExtractorGDBRemote Extractor(response_string);202 if (Extractor.IsErrorResponse())203 return Extractor.GetStatus().ToError();204 return Error::success();205}206 207Error TestClient::SendMessage(StringRef message, std::string &response_string,208 PacketResult expected_result) {209 StringExtractorGDBRemote response;210 GTEST_LOG_(INFO) << "Send Packet: " << message.str();211 PacketResult result = SendPacketAndWaitForResponse(message, response);212 response.GetEscapedBinaryData(response_string);213 GTEST_LOG_(INFO) << "Read Packet: " << response_string;214 if (result != expected_result)215 return make_error<StringError>(216 formatv("Error sending message `{0}`: {1}", message, result).str(),217 inconvertibleErrorCode());218 219 return Error::success();220}221 222unsigned int TestClient::GetPcRegisterId() {223 assert(m_pc_register != LLDB_INVALID_REGNUM);224 return m_pc_register;225}226 227Error TestClient::qProcessInfo() {228 m_process_info = std::nullopt;229 auto InfoOr = SendMessage<ProcessInfo>("qProcessInfo");230 if (!InfoOr)231 return InfoOr.takeError();232 m_process_info = std::move(*InfoOr);233 return Error::success();234}235 236Error TestClient::qRegisterInfos() {237 uint32_t reg_offset = 0;238 for (unsigned int Reg = 0;; ++Reg) {239 std::string Message = formatv("qRegisterInfo{0:x-}", Reg).str();240 Expected<RegisterInfo> InfoOr = SendMessage<RegisterInfoParser>(Message);241 if (!InfoOr) {242 consumeError(InfoOr.takeError());243 break;244 }245 m_register_infos.emplace_back(std::move(*InfoOr));246 247 if (m_register_infos[Reg].byte_offset == LLDB_INVALID_INDEX32)248 m_register_infos[Reg].byte_offset = reg_offset;249 250 reg_offset =251 m_register_infos[Reg].byte_offset + m_register_infos[Reg].byte_size;252 if (m_register_infos[Reg].kinds[eRegisterKindGeneric] ==253 LLDB_REGNUM_GENERIC_PC)254 m_pc_register = Reg;255 }256 if (m_pc_register == LLDB_INVALID_REGNUM)257 return make_parsing_error("qRegisterInfo: generic");258 return Error::success();259}260 261Error TestClient::queryProcess() {262 if (Error E = qProcessInfo())263 return E;264 if (Error E = qRegisterInfos())265 return E;266 return Error::success();267}268 269Error TestClient::Continue(StringRef message) {270 assert(m_process_info);271 272 auto StopReplyOr = SendMessage<StopReply>(273 message, m_process_info->GetEndian(), m_register_infos);274 if (!StopReplyOr)275 return StopReplyOr.takeError();276 277 m_stop_reply = std::move(*StopReplyOr);278 if (!isa<StopReplyStop>(m_stop_reply)) {279 StringExtractorGDBRemote R;280 PacketResult result = ReadPacket(R, GetPacketTimeout(), false);281 if (result != PacketResult::ErrorDisconnected) {282 return make_error<StringError>(283 formatv("Expected connection close after sending {0}. Got {1}/{2} "284 "instead.",285 message, result, R.GetStringRef())286 .str(),287 inconvertibleErrorCode());288 }289 }290 return Error::success();291}292