diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/unittests/tools/lldb-server/tests/TestClient.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/unittests/tools/lldb-server/tests/TestClient.cpp')
-rw-r--r-- | gnu/llvm/lldb/unittests/tools/lldb-server/tests/TestClient.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/unittests/tools/lldb-server/tests/TestClient.cpp b/gnu/llvm/lldb/unittests/tools/lldb-server/tests/TestClient.cpp new file mode 100644 index 00000000000..7e51a3879ab --- /dev/null +++ b/gnu/llvm/lldb/unittests/tools/lldb-server/tests/TestClient.cpp @@ -0,0 +1,269 @@ +//===-- TestClient.cpp ------------------------------------------*- C++ -*-===// +// +// 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 "TestClient.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" +#include "lldb/Utility/Args.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Path.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" +#include <cstdlib> +#include <future> +#include <sstream> +#include <string> + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; +using namespace llgs_tests; + +#ifdef SendMessage +#undef SendMessage +#endif + +TestClient::TestClient(std::unique_ptr<Connection> Conn) { + SetConnection(Conn.release()); + SetPacketTimeout(std::chrono::seconds(10)); +} + +TestClient::~TestClient() { + if (!IsConnected()) + return; + + EXPECT_THAT_ERROR(SendMessage("k"), Succeeded()); +} + +Error TestClient::initializeConnection() { + if (SendAck() == 0) + return make_error<StringError>("Sending initial ACK failed.", + inconvertibleErrorCode()); + + if (Error E = SendMessage("QStartNoAckMode")) + return E; + + m_send_acks = false; + return Error::success(); +} + +Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log) { + return launch(Log, {}); +} + +Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log, ArrayRef<StringRef> InferiorArgs) { + return launchCustom(Log, {}, InferiorArgs); +} + +Expected<std::unique_ptr<TestClient>> TestClient::launchCustom(StringRef Log, ArrayRef<StringRef> ServerArgs, ArrayRef<StringRef> InferiorArgs) { + const ArchSpec &arch_spec = HostInfo::GetArchitecture(); + Args args; + args.AppendArgument(LLDB_SERVER); + if (IsLldbServer()) + args.AppendArgument("gdbserver"); + args.AppendArgument("--reverse-connect"); + + if (!Log.empty()) { + args.AppendArgument(("--log-file=" + Log).str()); + if (IsLldbServer()) + args.AppendArgument("--log-channels=gdb-remote packets"); + else + args.AppendArgument("--log-flags=0x800000"); + } + + Status status; + TCPSocket listen_socket(true, false); + status = listen_socket.Listen("127.0.0.1:0", 5); + if (status.Fail()) + return status.ToError(); + + args.AppendArgument( + ("127.0.0.1:" + Twine(listen_socket.GetLocalPortNumber())).str()); + + for (StringRef arg : ServerArgs) + args.AppendArgument(arg); + + if (!InferiorArgs.empty()) { + args.AppendArgument("--"); + for (StringRef arg : InferiorArgs) + args.AppendArgument(arg); + } + + ProcessLaunchInfo Info; + Info.SetArchitecture(arch_spec); + Info.SetArguments(args, true); + Info.GetEnvironment() = Host::GetEnvironment(); + // TODO: Use this callback to detect botched launches. If lldb-server does not + // start, we can print a nice error message here instead of hanging in + // Accept(). + Info.SetMonitorProcessCallback(&ProcessLaunchInfo::NoOpMonitorCallback, + false); + + status = Host::LaunchProcess(Info); + if (status.Fail()) + return status.ToError(); + + Socket *accept_socket; + listen_socket.Accept(accept_socket); + auto Conn = std::make_unique<ConnectionFileDescriptor>(accept_socket); + auto Client = std::unique_ptr<TestClient>(new TestClient(std::move(Conn))); + + if (Error E = Client->initializeConnection()) + return std::move(E); + + if (!InferiorArgs.empty()) { + if (Error E = Client->queryProcess()) + return std::move(E); + } + + return std::move(Client); +} + +Error TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) { + if (SendEnvironment(Host::GetEnvironment()) != 0) { + return make_error<StringError>("Failed to set launch environment", + inconvertibleErrorCode()); + } + 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 (Error E = SendMessage(command.str())) + return E; + if (Error E = SendMessage("qLaunchSuccess")) + return E; + if (Error E = queryProcess()) + return E; + return Error::success(); +} + +Error TestClient::ListThreadsInStopReply() { + return SendMessage("QListThreadsInStopReply"); +} + +Error TestClient::SetBreakpoint(unsigned long address) { + return SendMessage(formatv("Z0,{0:x-},1", address).str()); +} + +Error TestClient::ContinueAll() { return Continue("vCont;c"); } + +Error TestClient::ContinueThread(unsigned long thread_id) { + return Continue(formatv("vCont;c:{0:x-}", thread_id).str()); +} + +const llgs_tests::ProcessInfo &TestClient::GetProcessInfo() { + return *m_process_info; +} + +Expected<JThreadsInfo> TestClient::GetJThreadsInfo() { + return SendMessage<JThreadsInfo>("jThreadsInfo", m_register_infos); +} + +const StopReply &TestClient::GetLatestStopReply() { + assert(m_stop_reply); + return *m_stop_reply; +} + +Error TestClient::SendMessage(StringRef message) { + std::string dummy_string; + return SendMessage(message, dummy_string); +} + +Error TestClient::SendMessage(StringRef message, std::string &response_string) { + if (Error E = SendMessage(message, response_string, PacketResult::Success)) + return E; + StringExtractorGDBRemote Extractor(response_string); + if (Extractor.IsErrorResponse()) + return Extractor.GetStatus().ToError(); + return Error::success(); +} + +Error 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) + return make_error<StringError>( + formatv("Error sending message `{0}`: {1}", message, result).str(), + inconvertibleErrorCode()); + + return Error::success(); +} + +unsigned int TestClient::GetPcRegisterId() { + assert(m_pc_register != LLDB_INVALID_REGNUM); + return m_pc_register; +} + +Error TestClient::qProcessInfo() { + m_process_info = None; + auto InfoOr = SendMessage<ProcessInfo>("qProcessInfo"); + if (!InfoOr) + return InfoOr.takeError(); + m_process_info = std::move(*InfoOr); + return Error::success(); +} + +Error TestClient::qRegisterInfos() { + for (unsigned int Reg = 0;; ++Reg) { + std::string Message = formatv("qRegisterInfo{0:x-}", Reg).str(); + Expected<RegisterInfo> InfoOr = SendMessage<RegisterInfoParser>(Message); + if (!InfoOr) { + consumeError(InfoOr.takeError()); + break; + } + m_register_infos.emplace_back(std::move(*InfoOr)); + if (m_register_infos[Reg].kinds[eRegisterKindGeneric] == + LLDB_REGNUM_GENERIC_PC) + m_pc_register = Reg; + } + if (m_pc_register == LLDB_INVALID_REGNUM) + return make_parsing_error("qRegisterInfo: generic"); + return Error::success(); +} + +Error TestClient::queryProcess() { + if (Error E = qProcessInfo()) + return E; + if (Error E = qRegisterInfos()) + return E; + return Error::success(); +} + +Error TestClient::Continue(StringRef message) { + assert(m_process_info.hasValue()); + + auto StopReplyOr = SendMessage<StopReply>( + message, m_process_info->GetEndian(), m_register_infos); + if (!StopReplyOr) + return StopReplyOr.takeError(); + + m_stop_reply = std::move(*StopReplyOr); + if (!isa<StopReplyStop>(m_stop_reply)) { + StringExtractorGDBRemote R; + PacketResult result = ReadPacket(R, GetPacketTimeout(), false); + if (result != PacketResult::ErrorDisconnected) { + return make_error<StringError>( + formatv("Expected connection close after sending {0}. Got {1}/{2} " + "instead.", + message, result, R.GetStringRef()) + .str(), + inconvertibleErrorCode()); + } + } + return Error::success(); +} |