diff options
Diffstat (limited to 'unittests/tools/lldb-server/tests/TestClient.cpp')
| -rw-r--r-- | unittests/tools/lldb-server/tests/TestClient.cpp | 287 | 
1 files changed, 287 insertions, 0 deletions
| diff --git a/unittests/tools/lldb-server/tests/TestClient.cpp b/unittests/tools/lldb-server/tests/TestClient.cpp new file mode 100644 index 000000000000..540d7979055c --- /dev/null +++ b/unittests/tools/lldb-server/tests/TestClient.cpp @@ -0,0 +1,287 @@ +//===-- TestClient.cpp ------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TestClient.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" +#include "lldb/Host/posix/ProcessLauncherPosix.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "llvm/ADT/StringExtras.h" +#include "gtest/gtest.h" +#include <cstdlib> +#include <future> +#include <sstream> +#include <string> + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +namespace llgs_tests { +void TestClient::Initialize() { HostInfo::Initialize(); } + +TestClient::TestClient(const std::string &test_name, +                       const std::string &test_case_name) +    : m_test_name(test_name), m_test_case_name(test_case_name), +      m_pc_register(UINT_MAX) {} + +TestClient::~TestClient() {} + +bool TestClient::StartDebugger() { +  const ArchSpec &arch_spec = HostInfo::GetArchitecture(); +  Args args; +  args.AppendArgument(LLDB_SERVER); +  args.AppendArgument("gdbserver"); +  args.AppendArgument("--log-channels=gdb-remote packets"); +  args.AppendArgument("--reverse-connect"); +  std::string log_file_name = GenerateLogFileName(arch_spec); +  if (log_file_name.size()) { +    args.AppendArgument("--log-file=" + log_file_name); +  } + +  Status error; +  TCPSocket listen_socket(true, false); +  error = listen_socket.Listen("127.0.0.1:0", 5); +  if (error.Fail()) { +    GTEST_LOG_(ERROR) << "Unable to open listen socket."; +    return false; +  } + +  char connect_remote_address[64]; +  snprintf(connect_remote_address, sizeof(connect_remote_address), +           "localhost:%u", listen_socket.GetLocalPortNumber()); + +  args.AppendArgument(connect_remote_address); + +  m_server_process_info.SetArchitecture(arch_spec); +  m_server_process_info.SetArguments(args, true); +  Status status = Host::LaunchProcess(m_server_process_info); +  if (status.Fail()) { +    GTEST_LOG_(ERROR) +        << formatv("Failure to launch lldb server: {0}.", status).str(); +    return false; +  } + +  char connect_remote_uri[64]; +  snprintf(connect_remote_uri, sizeof(connect_remote_uri), "connect://%s", +           connect_remote_address); +  Socket *accept_socket; +  listen_socket.Accept(accept_socket); +  SetConnection(new ConnectionFileDescriptor(accept_socket)); + +  SendAck(); // Send this as a handshake. +  return true; +} + +bool TestClient::StopDebugger() { +  std::string response; +  return SendMessage("k", response, PacketResult::ErrorDisconnected); +} + +bool TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) { +  std::stringstream command; +  command << "A"; +  for (size_t i = 0; i < inferior_args.size(); i++) { +    if (i > 0) +      command << ','; +    std::string hex_encoded = toHex(inferior_args[i]); +    command << hex_encoded.size() << ',' << i << ',' << hex_encoded; +  } + +  if (!SendMessage(command.str())) +    return false; +  if (!SendMessage("qLaunchSuccess")) +    return false; +  std::string response; +  if (!SendMessage("qProcessInfo", response)) +    return false; +  auto create_or_error = ProcessInfo::Create(response); +  if (auto create_error = create_or_error.takeError()) { +    GTEST_LOG_(ERROR) << toString(std::move(create_error)); +    return false; +  } + +  m_process_info = *create_or_error; +  return true; +} + +bool TestClient::ListThreadsInStopReply() { +  return SendMessage("QListThreadsInStopReply"); +} + +bool TestClient::SetBreakpoint(unsigned long address) { +  std::stringstream command; +  command << "Z0," << std::hex << address << ",1"; +  return SendMessage(command.str()); +} + +bool TestClient::ContinueAll() { return Continue("vCont;c"); } + +bool TestClient::ContinueThread(unsigned long thread_id) { +  return Continue(formatv("vCont;c:{0:x-}", thread_id).str()); +} + +const ProcessInfo &TestClient::GetProcessInfo() { return *m_process_info; } + +Optional<JThreadsInfo> TestClient::GetJThreadsInfo() { +  std::string response; +  if (!SendMessage("jThreadsInfo", response)) +    return llvm::None; +  auto creation = JThreadsInfo::Create(response, m_process_info->GetEndian()); +  if (auto create_error = creation.takeError()) { +    GTEST_LOG_(ERROR) << toString(std::move(create_error)); +    return llvm::None; +  } + +  return std::move(*creation); +} + +const StopReply &TestClient::GetLatestStopReply() { +  return m_stop_reply.getValue(); +} + +bool TestClient::SendMessage(StringRef message) { +  std::string dummy_string; +  return SendMessage(message, dummy_string); +} + +bool TestClient::SendMessage(StringRef message, std::string &response_string) { +  if (!SendMessage(message, response_string, PacketResult::Success)) +    return false; +  else if (response_string[0] == 'E') { +    GTEST_LOG_(ERROR) << "Error " << response_string +                      << " while sending message: " << message.str(); +    return false; +  } + +  return true; +} + +bool TestClient::SendMessage(StringRef message, std::string &response_string, +                             PacketResult expected_result) { +  StringExtractorGDBRemote response; +  GTEST_LOG_(INFO) << "Send Packet: " << message.str(); +  PacketResult result = SendPacketAndWaitForResponse(message, response, false); +  response.GetEscapedBinaryData(response_string); +  GTEST_LOG_(INFO) << "Read Packet: " << response_string; +  if (result != expected_result) { +    GTEST_LOG_(ERROR) << FormatFailedResult(message, result); +    return false; +  } + +  return true; +} + +unsigned int TestClient::GetPcRegisterId() { +  if (m_pc_register != UINT_MAX) +    return m_pc_register; + +  for (unsigned int register_id = 0;; register_id++) { +    std::string message = formatv("qRegisterInfo{0:x-}", register_id).str(); +    std::string response; +    if (!SendMessage(message, response)) { +      GTEST_LOG_(ERROR) << "Unable to query register ID for PC register."; +      return UINT_MAX; +    } + +    auto elements_or_error = SplitPairList("GetPcRegisterId", response); +    if (auto split_error = elements_or_error.takeError()) { +      GTEST_LOG_(ERROR) << "GetPcRegisterId: Error splitting response: " +                        << response; +      return UINT_MAX; +    } + +    auto elements = *elements_or_error; +    if (elements["alt-name"] == "pc" || elements["generic"] == "pc") { +      m_pc_register = register_id; +      break; +    } +  } + +  return m_pc_register; +} + +bool TestClient::Continue(StringRef message) { +  if (!m_process_info.hasValue()) { +    GTEST_LOG_(ERROR) << "Continue() called before m_process_info initialized."; +    return false; +  } + +  std::string response; +  if (!SendMessage(message, response)) +    return false; +  auto creation = StopReply::Create(response, m_process_info->GetEndian()); +  if (auto create_error = creation.takeError()) { +    GTEST_LOG_(ERROR) << toString(std::move(create_error)); +    return false; +  } + +  m_stop_reply = std::move(*creation); +  return true; +} + +std::string TestClient::GenerateLogFileName(const ArchSpec &arch) const { +  char *log_directory = getenv("LOG_FILE_DIRECTORY"); +  if (!log_directory) +    return ""; + +  if (!llvm::sys::fs::is_directory(log_directory)) { +    GTEST_LOG_(WARNING) << "Cannot access log directory: " << log_directory; +    return ""; +  } + +  std::string log_file_name; +  raw_string_ostream log_file(log_file_name); +  log_file << log_directory << "/lldb-" << m_test_case_name << '-' +           << m_test_name << '-' << arch.GetArchitectureName() << ".log"; +  return log_file.str(); +} + +std::string TestClient::FormatFailedResult(const std::string &message, +                                           PacketResult result) { +  std::string formatted_error; +  raw_string_ostream error_stream(formatted_error); +  error_stream << "Failure sending message: " << message << " Result: "; + +  switch (result) { +  case PacketResult::ErrorSendFailed: +    error_stream << "ErrorSendFailed"; +    break; +  case PacketResult::ErrorSendAck: +    error_stream << "ErrorSendAck"; +    break; +  case PacketResult::ErrorReplyFailed: +    error_stream << "ErrorReplyFailed"; +    break; +  case PacketResult::ErrorReplyTimeout: +    error_stream << "ErrorReplyTimeout"; +    break; +  case PacketResult::ErrorReplyInvalid: +    error_stream << "ErrorReplyInvalid"; +    break; +  case PacketResult::ErrorReplyAck: +    error_stream << "ErrorReplyAck"; +    break; +  case PacketResult::ErrorDisconnected: +    error_stream << "ErrorDisconnected"; +    break; +  case PacketResult::ErrorNoSequenceLock: +    error_stream << "ErrorNoSequenceLock"; +    break; +  default: +    error_stream << "Unknown Error"; +  } + +  error_stream.str(); +  return formatted_error; +} +} // namespace llgs_tests | 
