summaryrefslogtreecommitdiff
path: root/unittests/tools/lldb-server/tests/TestClient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/tools/lldb-server/tests/TestClient.cpp')
-rw-r--r--unittests/tools/lldb-server/tests/TestClient.cpp291
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