diff options
Diffstat (limited to 'unittests/tools/lldb-server/tests/TestClient.cpp')
-rw-r--r-- | unittests/tools/lldb-server/tests/TestClient.cpp | 291 |
1 files changed, 140 insertions, 151 deletions
diff --git a/unittests/tools/lldb-server/tests/TestClient.cpp b/unittests/tools/lldb-server/tests/TestClient.cpp index 9553ddd65fac..773466abe855 100644 --- a/unittests/tools/lldb-server/tests/TestClient.cpp +++ b/unittests/tools/lldb-server/tests/TestClient.cpp @@ -8,13 +8,14 @@ //===----------------------------------------------------------------------===// #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/Interpreter/Args.h" #include "lldb/Target/ProcessLaunchInfo.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> @@ -26,67 +27,100 @@ using namespace lldb_private; using namespace llvm; namespace llgs_tests { -void TestClient::Initialize() { HostInfo::Initialize(); } +bool TestClient::IsDebugServer() { + return sys::path::filename(LLDB_SERVER).contains("debugserver"); +} + +bool TestClient::IsLldbServer() { return !IsDebugServer(); } + +TestClient::TestClient(std::unique_ptr<Connection> Conn) { + SetConnection(Conn.release()); + + SendAck(); // Send this as a handshake. +} -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() { + if (!IsConnected()) + return; -TestClient::~TestClient() {} + std::string response; + // Debugserver (non-conformingly?) sends a reply to the k packet instead of + // simply closing the connection. + PacketResult result = + IsDebugServer() ? PacketResult::Success : PacketResult::ErrorDisconnected; + EXPECT_THAT_ERROR(SendMessage("k", response, result), Succeeded()); +} + +Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log) { + return launch(Log, {}); +} -bool TestClient::StartDebugger() { +Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log, ArrayRef<StringRef> InferiorArgs) { const ArchSpec &arch_spec = HostInfo::GetArchitecture(); Args args; args.AppendArgument(LLDB_SERVER); - args.AppendArgument("gdbserver"); - args.AppendArgument("--log-channels=gdb-remote packets"); + if (IsLldbServer()) + args.AppendArgument("gdbserver"); args.AppendArgument("--reverse-connect"); - std::string log_file_name = GenerateLogFileName(arch_spec); - if (log_file_name.size()) { - args.AppendArgument("--log-file=" + log_file_name); + + 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 error; + Status status; 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; + status = listen_socket.Listen("127.0.0.1:0", 5); + if (status.Fail()) + return status.ToError(); + + args.AppendArgument( + ("localhost:" + Twine(listen_socket.GetLocalPortNumber())).str()); + + if (!InferiorArgs.empty()) { + args.AppendArgument("--"); + for (StringRef arg : InferiorArgs) + args.AppendArgument(arg); } - char connect_remote_address[64]; - snprintf(connect_remote_address, sizeof(connect_remote_address), - "localhost:%u", listen_socket.GetLocalPortNumber()); + ProcessLaunchInfo Info; + Info.SetArchitecture(arch_spec); + Info.SetArguments(args, true); - args.AppendArgument(connect_remote_address); + StringList Env; + Host::GetEnvironment(Env); + Info.GetEnvironmentEntries() = Args(Env); - 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; - } + status = Host::LaunchProcess(Info); + if (status.Fail()) + return status.ToError(); - 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)); + auto Conn = llvm::make_unique<ConnectionFileDescriptor>(accept_socket); + auto Client = std::unique_ptr<TestClient>(new TestClient(std::move(Conn))); - SendAck(); // Send this as a handshake. - return true; -} + if (!InferiorArgs.empty()) { + if (Error E = Client->QueryProcessInfo()) + return std::move(E); + } -bool TestClient::StopDebugger() { - std::string response; - return SendMessage("k", response, PacketResult::ErrorDisconnected); + return std::move(Client); } -bool TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) { +Error TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) { + StringList env; + Host::GetEnvironment(env); + for (size_t i = 0; i < env.GetSize(); ++i) { + if (SendEnvironmentPacket(env[i].c_str()) != 0) { + return make_error<StringError>( + formatv("Failed to set environment variable: {0}", env[i]).str(), + inconvertibleErrorCode()); + } + } std::stringstream command; command << "A"; for (size_t i = 0; i < inferior_args.size(); i++) { @@ -96,36 +130,26 @@ bool TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) { 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; + if (Error E = SendMessage(command.str())) + return E; + if (Error E = SendMessage("qLaunchSuccess")) + return E; + if (Error E = QueryProcessInfo()) + return E; + return Error::success(); } -bool TestClient::ListThreadsInStopReply() { +Error TestClient::ListThreadsInStopReply() { return SendMessage("QListThreadsInStopReply"); } -bool TestClient::SetBreakpoint(unsigned long address) { - std::stringstream command; - command << "Z0," << std::hex << address << ",1"; - return SendMessage(command.str()); +Error TestClient::SetBreakpoint(unsigned long address) { + return SendMessage(formatv("Z0,{0:x-},1", address).str()); } -bool TestClient::ContinueAll() { return Continue("vCont;c"); } +Error TestClient::ContinueAll() { return Continue("vCont;c"); } -bool TestClient::ContinueThread(unsigned long thread_id) { +Error TestClient::ContinueThread(unsigned long thread_id) { return Continue(formatv("vCont;c:{0:x-}", thread_id).str()); } @@ -133,7 +157,7 @@ const ProcessInfo &TestClient::GetProcessInfo() { return *m_process_info; } Optional<JThreadsInfo> TestClient::GetJThreadsInfo() { std::string response; - if (!SendMessage("jThreadsInfo", response)) + if (SendMessage("jThreadsInfo", response)) return llvm::None; auto creation = JThreadsInfo::Create(response, m_process_info->GetEndian()); if (auto create_error = creation.takeError()) { @@ -145,39 +169,41 @@ Optional<JThreadsInfo> TestClient::GetJThreadsInfo() { } const StopReply &TestClient::GetLatestStopReply() { - return m_stop_reply.getValue(); + assert(m_stop_reply); + return *m_stop_reply; } -bool TestClient::SendMessage(StringRef message) { +Error 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; +Error TestClient::SendMessage(StringRef message, std::string &response_string) { + if (Error E = SendMessage(message, response_string, PacketResult::Success)) + return E; + if (response_string[0] == 'E') { + return make_error<StringError>( + formatv("Error `{0}` while sending message: {1}", response_string, + message) + .str(), + inconvertibleErrorCode()); } - - return true; + return Error::success(); } -bool TestClient::SendMessage(StringRef message, std::string &response_string, - PacketResult expected_result) { +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) { - GTEST_LOG_(ERROR) << FormatFailedResult(message, result); - return false; - } + if (result != expected_result) + return make_error<StringError>( + formatv("Error sending message `{0}`: {1}", message, result).str(), + inconvertibleErrorCode()); - return true; + return Error::success(); } unsigned int TestClient::GetPcRegisterId() { @@ -187,12 +213,12 @@ unsigned int TestClient::GetPcRegisterId() { 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)) { + 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); + auto elements_or_error = SplitUniquePairList("GetPcRegisterId", response); if (auto split_error = elements_or_error.takeError()) { GTEST_LOG_(ERROR) << "GetPcRegisterId: Error splitting response: " << response; @@ -209,78 +235,41 @@ unsigned int TestClient::GetPcRegisterId() { 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; - } - +llvm::Error TestClient::QueryProcessInfo() { 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; + if (Error E = SendMessage("qProcessInfo", response)) + return E; + auto create_or_error = ProcessInfo::Create(response); + if (!create_or_error) + return create_or_error.takeError(); + m_process_info = *create_or_error; + return Error::success(); } -std::string TestClient::GenerateLogFileName(const ArchSpec &arch) const { - char *log_directory = getenv("LOG_FILE_DIRECTORY"); - if (!log_directory) - return ""; +Error TestClient::Continue(StringRef message) { + assert(m_process_info.hasValue()); - 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 response; + if (Error E = SendMessage(message, response)) + return E; + auto creation = StopReply::create(response, m_process_info->GetEndian()); + if (Error E = creation.takeError()) + return E; -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"; + m_stop_reply = std::move(*creation); + 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 receiving {0}. Got {1}/{2} " + "instead.", + response, result, R.GetStringRef()) + .str(), + inconvertibleErrorCode()); + } } - - error_stream.str(); - return formatted_error; + return Error::success(); } + } // namespace llgs_tests |