brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.7 KiB · f3510ca Raw
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