[lldb] [lldb-gdbserver] Unify listen/connect code to use ConnectionFileDescriptor

Unify the listen and connect code inside lldb-server to use
ConnectionFileDescriptor uniformly rather than a mix of it and Acceptor.
This involves:

- adding a function to map legacy values of host:port parameter
  (including legacy server URLs) into CFD-style URLs

- adding a callback to return "local socket id" (i.e. UNIX socket path
  or TCP port number) between listen() and accept() calls in CFD

- adding a "unix-abstract-accept" scheme to CFD

As an additional advantage, this permits lldb-server to accept any URL
known to CFD including the new serial:// scheme.  Effectively,
lldb-server can now listen on the serial port.  Tests for connecting
over a pty are added to test that.

Differential Revision: https://reviews.llvm.org/D111964
This commit is contained in:
Michał Górny 2021-10-22 17:42:23 +02:00
parent 93c7ed8c3f
commit 58d28b931f
8 changed files with 371 additions and 158 deletions

View File

@ -28,6 +28,9 @@ class SocketAddress;
class ConnectionFileDescriptor : public Connection {
public:
typedef llvm::function_ref<void(llvm::StringRef local_socket_id)>
socket_id_callback_type;
ConnectionFileDescriptor(bool child_processes_inherit = false);
ConnectionFileDescriptor(int fd, bool owns_fd);
@ -38,7 +41,12 @@ public:
bool IsConnected() const override;
lldb::ConnectionStatus Connect(llvm::StringRef s, Status *error_ptr) override;
lldb::ConnectionStatus Connect(llvm::StringRef url,
Status *error_ptr) override;
lldb::ConnectionStatus Connect(llvm::StringRef url,
socket_id_callback_type socket_id_callback,
Status *error_ptr);
lldb::ConnectionStatus Disconnect(Status *error_ptr) override;
@ -67,29 +75,51 @@ protected:
void CloseCommandPipe();
lldb::ConnectionStatus SocketListenAndAccept(llvm::StringRef host_and_port,
Status *error_ptr);
lldb::ConnectionStatus
SocketListenAndAccept(llvm::StringRef host_and_port,
socket_id_callback_type socket_id_callback,
Status *error_ptr);
lldb::ConnectionStatus ConnectTCP(llvm::StringRef host_and_port,
socket_id_callback_type socket_id_callback,
Status *error_ptr);
lldb::ConnectionStatus ConnectUDP(llvm::StringRef args, Status *error_ptr);
lldb::ConnectionStatus ConnectUDP(llvm::StringRef args,
socket_id_callback_type socket_id_callback,
Status *error_ptr);
lldb::ConnectionStatus NamedSocketConnect(llvm::StringRef socket_name,
Status *error_ptr);
lldb::ConnectionStatus
NamedSocketConnect(llvm::StringRef socket_name,
socket_id_callback_type socket_id_callback,
Status *error_ptr);
lldb::ConnectionStatus NamedSocketAccept(llvm::StringRef socket_name,
Status *error_ptr);
lldb::ConnectionStatus
NamedSocketAccept(llvm::StringRef socket_name,
socket_id_callback_type socket_id_callback,
Status *error_ptr);
lldb::ConnectionStatus UnixAbstractSocketConnect(llvm::StringRef socket_name,
Status *error_ptr);
lldb::ConnectionStatus
UnixAbstractSocketAccept(llvm::StringRef socket_name,
socket_id_callback_type socket_id_callback,
Status *error_ptr);
lldb::ConnectionStatus ConnectFD(llvm::StringRef args, Status *error_ptr);
lldb::ConnectionStatus
UnixAbstractSocketConnect(llvm::StringRef socket_name,
socket_id_callback_type socket_id_callback,
Status *error_ptr);
lldb::ConnectionStatus ConnectFile(llvm::StringRef args, Status *error_ptr);
lldb::ConnectionStatus ConnectFD(llvm::StringRef args,
socket_id_callback_type socket_id_callback,
Status *error_ptr);
lldb::ConnectionStatus ConnectSerialPort(llvm::StringRef args,
Status *error_ptr);
lldb::ConnectionStatus ConnectFile(llvm::StringRef args,
socket_id_callback_type socket_id_callback,
Status *error_ptr);
lldb::ConnectionStatus
ConnectSerialPort(llvm::StringRef args,
socket_id_callback_type socket_id_callback,
Status *error_ptr);
lldb::IOObjectSP m_io_sp;
@ -103,7 +133,6 @@ protected:
std::atomic<bool> m_shutting_down; // This marks that we are shutting down so
// if we get woken up from
// BytesAvailable to disconnect, we won't try to read again.
bool m_waiting_for_accept = false;
bool m_child_processes_inherit;
std::string m_uri;

View File

@ -13,10 +13,10 @@
#define _DARWIN_UNLIMITED_SELECT
#endif
#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
#include "lldb/Host/Config.h"
#include "lldb/Host/Socket.h"
#include "lldb/Host/SocketAddress.h"
#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
#include "lldb/Utility/SelectHelper.h"
#include "lldb/Utility/Timeout.h"
@ -62,7 +62,7 @@ ConnectionFileDescriptor::ConnectionFileDescriptor(bool child_processes_inherit)
ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd)
: Connection(), m_pipe(), m_mutex(), m_shutting_down(false),
m_waiting_for_accept(false), m_child_processes_inherit(false) {
m_child_processes_inherit(false) {
m_io_sp =
std::make_shared<NativeFile>(fd, File::eOpenOptionReadWrite, owns_fd);
@ -77,7 +77,7 @@ ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd)
ConnectionFileDescriptor::ConnectionFileDescriptor(Socket *socket)
: Connection(), m_pipe(), m_mutex(), m_shutting_down(false),
m_waiting_for_accept(false), m_child_processes_inherit(false) {
m_child_processes_inherit(false) {
InitializeSocket(socket);
}
@ -124,6 +124,13 @@ bool ConnectionFileDescriptor::IsConnected() const {
ConnectionStatus ConnectionFileDescriptor::Connect(llvm::StringRef path,
Status *error_ptr) {
return Connect(path, nullptr, error_ptr);
}
ConnectionStatus
ConnectionFileDescriptor::Connect(llvm::StringRef path,
socket_id_callback_type socket_id_callback,
Status *error_ptr) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
LLDB_LOGF(log, "%p ConnectionFileDescriptor::Connect (url = '%s')",
@ -143,10 +150,12 @@ ConnectionStatus ConnectionFileDescriptor::Connect(llvm::StringRef path,
if (!path.empty()) {
auto method =
llvm::StringSwitch<ConnectionStatus (ConnectionFileDescriptor::*)(
llvm::StringRef, Status *)>(scheme)
llvm::StringRef, socket_id_callback_type, Status *)>(scheme)
.Case("listen", &ConnectionFileDescriptor::SocketListenAndAccept)
.Cases("accept", "unix-accept",
&ConnectionFileDescriptor::NamedSocketAccept)
.Case("unix-abstract-accept",
&ConnectionFileDescriptor::UnixAbstractSocketAccept)
.Cases("connect", "tcp-connect",
&ConnectionFileDescriptor::ConnectTCP)
.Case("udp", &ConnectionFileDescriptor::ConnectUDP)
@ -161,7 +170,7 @@ ConnectionStatus ConnectionFileDescriptor::Connect(llvm::StringRef path,
.Default(nullptr);
if (method)
return (this->*method)(path, error_ptr);
return (this->*method)(path, socket_id_callback, error_ptr);
}
if (error_ptr)
@ -498,7 +507,8 @@ ConnectionFileDescriptor::BytesAvailable(const Timeout<std::micro> &timeout,
// data from that pipe:
char c;
ssize_t bytes_read = llvm::sys::RetryAfterSignal(-1, ::read, pipe_fd, &c, 1);
ssize_t bytes_read =
llvm::sys::RetryAfterSignal(-1, ::read, pipe_fd, &c, 1);
assert(bytes_read == 1);
(void)bytes_read;
switch (c) {
@ -522,24 +532,36 @@ ConnectionFileDescriptor::BytesAvailable(const Timeout<std::micro> &timeout,
return eConnectionStatusLostConnection;
}
ConnectionStatus
ConnectionFileDescriptor::NamedSocketAccept(llvm::StringRef socket_name,
Status *error_ptr) {
ConnectionStatus ConnectionFileDescriptor::NamedSocketAccept(
llvm::StringRef socket_name, socket_id_callback_type socket_id_callback,
Status *error_ptr) {
Status error;
std::unique_ptr<Socket> listen_socket = Socket::Create(
Socket::ProtocolUnixDomain, m_child_processes_inherit, error);
Socket *socket = nullptr;
Status error =
Socket::UnixDomainAccept(socket_name, m_child_processes_inherit, socket);
if (!error.Fail())
error = listen_socket->Listen(socket_name, 5);
if (!error.Fail()) {
socket_id_callback(socket_name);
error = listen_socket->Accept(socket);
}
if (!error.Fail()) {
m_io_sp.reset(socket);
m_uri.assign(socket_name.str());
return eConnectionStatusSuccess;
}
if (error_ptr)
*error_ptr = error;
m_io_sp.reset(socket);
if (error.Fail())
return eConnectionStatusError;
m_uri.assign(std::string(socket_name));
return eConnectionStatusSuccess;
return eConnectionStatusError;
}
ConnectionStatus
ConnectionFileDescriptor::NamedSocketConnect(llvm::StringRef socket_name,
Status *error_ptr) {
ConnectionStatus ConnectionFileDescriptor::NamedSocketConnect(
llvm::StringRef socket_name, socket_id_callback_type socket_id_callback,
Status *error_ptr) {
Socket *socket = nullptr;
Status error =
Socket::UnixDomainConnect(socket_name, m_child_processes_inherit, socket);
@ -552,9 +574,37 @@ ConnectionFileDescriptor::NamedSocketConnect(llvm::StringRef socket_name,
return eConnectionStatusSuccess;
}
lldb::ConnectionStatus
ConnectionFileDescriptor::UnixAbstractSocketConnect(llvm::StringRef socket_name,
Status *error_ptr) {
ConnectionStatus ConnectionFileDescriptor::UnixAbstractSocketAccept(
llvm::StringRef socket_name, socket_id_callback_type socket_id_callback,
Status *error_ptr) {
Status error;
std::unique_ptr<Socket> listen_socket = Socket::Create(
Socket::ProtocolUnixAbstract, m_child_processes_inherit, error);
Socket *socket = nullptr;
if (!error.Fail())
error = listen_socket->Listen(socket_name, 5);
if (!error.Fail())
socket_id_callback(socket_name);
if (!error.Fail())
error = listen_socket->Accept(socket);
if (!error.Fail()) {
m_io_sp.reset(socket);
m_uri.assign(socket_name.str());
return eConnectionStatusSuccess;
}
if (error_ptr)
*error_ptr = error;
return eConnectionStatusError;
}
lldb::ConnectionStatus ConnectionFileDescriptor::UnixAbstractSocketConnect(
llvm::StringRef socket_name, socket_id_callback_type socket_id_callback,
Status *error_ptr) {
Socket *socket = nullptr;
Status error = Socket::UnixAbstractConnect(socket_name,
m_child_processes_inherit, socket);
@ -567,14 +617,13 @@ ConnectionFileDescriptor::UnixAbstractSocketConnect(llvm::StringRef socket_name,
return eConnectionStatusSuccess;
}
ConnectionStatus
ConnectionFileDescriptor::SocketListenAndAccept(llvm::StringRef s,
Status *error_ptr) {
ConnectionStatus ConnectionFileDescriptor::SocketListenAndAccept(
llvm::StringRef s, socket_id_callback_type socket_id_callback,
Status *error_ptr) {
if (error_ptr)
*error_ptr = Status();
m_port_predicate.SetValue(0, eBroadcastNever);
m_waiting_for_accept = true;
llvm::Expected<std::unique_ptr<TCPSocket>> listening_socket =
Socket::TcpListen(s, m_child_processes_inherit, &m_port_predicate);
if (!listening_socket) {
@ -586,6 +635,8 @@ ConnectionFileDescriptor::SocketListenAndAccept(llvm::StringRef s,
return eConnectionStatusError;
}
uint16_t port = listening_socket.get()->GetLocalPortNumber();
socket_id_callback(std::to_string(port));
Socket *accepted_socket;
Status error = listening_socket.get()->Accept(accepted_socket);
@ -598,8 +649,10 @@ ConnectionFileDescriptor::SocketListenAndAccept(llvm::StringRef s,
return eConnectionStatusSuccess;
}
ConnectionStatus ConnectionFileDescriptor::ConnectTCP(llvm::StringRef s,
Status *error_ptr) {
ConnectionStatus
ConnectionFileDescriptor::ConnectTCP(llvm::StringRef s,
socket_id_callback_type socket_id_callback,
Status *error_ptr) {
if (error_ptr)
*error_ptr = Status();
@ -618,8 +671,10 @@ ConnectionStatus ConnectionFileDescriptor::ConnectTCP(llvm::StringRef s,
return eConnectionStatusSuccess;
}
ConnectionStatus ConnectionFileDescriptor::ConnectUDP(llvm::StringRef s,
Status *error_ptr) {
ConnectionStatus
ConnectionFileDescriptor::ConnectUDP(llvm::StringRef s,
socket_id_callback_type socket_id_callback,
Status *error_ptr) {
if (error_ptr)
*error_ptr = Status();
llvm::Expected<std::unique_ptr<UDPSocket>> socket =
@ -637,8 +692,10 @@ ConnectionStatus ConnectionFileDescriptor::ConnectUDP(llvm::StringRef s,
return eConnectionStatusSuccess;
}
ConnectionStatus ConnectionFileDescriptor::ConnectFD(llvm::StringRef s,
Status *error_ptr) {
ConnectionStatus
ConnectionFileDescriptor::ConnectFD(llvm::StringRef s,
socket_id_callback_type socket_id_callback,
Status *error_ptr) {
#if LLDB_ENABLE_POSIX
// Just passing a native file descriptor within this current process that
// is already opened (possibly from a service or other source).
@ -691,8 +748,9 @@ ConnectionStatus ConnectionFileDescriptor::ConnectFD(llvm::StringRef s,
llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX");
}
ConnectionStatus ConnectionFileDescriptor::ConnectFile(llvm::StringRef s,
Status *error_ptr) {
ConnectionStatus ConnectionFileDescriptor::ConnectFile(
llvm::StringRef s, socket_id_callback_type socket_id_callback,
Status *error_ptr) {
#if LLDB_ENABLE_POSIX
std::string addr_str = s.str();
// file:///PATH
@ -722,16 +780,15 @@ ConnectionStatus ConnectionFileDescriptor::ConnectFile(llvm::StringRef s,
llvm::sys::RetryAfterSignal(-1, ::tcsetattr, fd, TCSANOW, &options);
}
m_io_sp =
std::make_shared<NativeFile>(fd, File::eOpenOptionReadWrite, true);
m_io_sp = std::make_shared<NativeFile>(fd, File::eOpenOptionReadWrite, true);
return eConnectionStatusSuccess;
#endif // LLDB_ENABLE_POSIX
llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX");
}
ConnectionStatus
ConnectionFileDescriptor::ConnectSerialPort(llvm::StringRef s,
Status *error_ptr) {
ConnectionStatus ConnectionFileDescriptor::ConnectSerialPort(
llvm::StringRef s, socket_id_callback_type socket_id_callback,
Status *error_ptr) {
#if LLDB_ENABLE_POSIX
llvm::StringRef path, qs;
// serial:///PATH?k1=v1&k2=v2...

View File

@ -25,6 +25,7 @@
#include "lldb/Host/Host.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Host/PosixApi.h"
#include "lldb/Host/Socket.h"
#include "lldb/Host/common/NativeProcessProtocol.h"
#include "lldb/Host/common/NativeRegisterContext.h"
#include "lldb/Host/common/NativeThreadProtocol.h"
@ -39,6 +40,7 @@
#include "lldb/Utility/State.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/UnimplementedError.h"
#include "lldb/Utility/UriParser.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/ScopedPrinter.h"
@ -3888,3 +3890,42 @@ void GDBRemoteCommunicationServerLLGS::SetEnabledExtensions(
assert(!bool(flags & ~m_process_factory.GetSupportedExtensions()));
process.SetEnabledExtensions(flags);
}
std::string
lldb_private::process_gdb_remote::LLGSArgToURL(llvm::StringRef url_arg,
bool reverse_connect) {
// Try parsing the argument as URL.
if (llvm::Optional<URI> url = URI::Parse(url_arg)) {
if (reverse_connect)
return url_arg.str();
// Translate the scheme from LLGS notation to ConnectionFileDescriptor.
// If the scheme doesn't match any, pass it through to support using CFD
// schemes directly.
std::string new_url = llvm::StringSwitch<std::string>(url->scheme)
.Case("tcp", "listen")
.Case("unix", "unix-accept")
.Case("unix-abstract", "unix-abstract-accept")
.Default(url->scheme.str());
llvm::append_range(new_url, url_arg.substr(url->scheme.size()));
return new_url;
}
std::string host_port = url_arg.str();
// If host_and_port starts with ':', default the host to be "localhost" and
// expect the remainder to be the port.
if (url_arg.startswith(":"))
host_port.insert(0, "localhost");
std::string host_str;
std::string port_str;
uint16_t port;
// Try parsing the (preprocessed) argument as host:port pair.
if (!llvm::errorToBool(
Socket::DecodeHostAndPort(host_port, host_str, port_str, port)))
return (reverse_connect ? "connect://" : "listen://") + host_port;
// If none of the above applied, interpret the argument as UNIX socket path.
return (reverse_connect ? "unix-connect://" : "unix-accept://") +
url_arg.str();
}

View File

@ -289,6 +289,8 @@ private:
operator=(const GDBRemoteCommunicationServerLLGS &) = delete;
};
std::string LLGSArgToURL(llvm::StringRef url_arg, bool reverse_connect);
} // namespace process_gdb_remote
} // namespace lldb_private

View File

@ -0,0 +1,73 @@
import gdbremote_testcase
import lldbgdbserverutils
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbgdbserverutils import *
import xml.etree.ElementTree as ET
@skipIfWindows
class PtyServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase):
mydir = TestBase.compute_mydir(__file__)
def setUp(self):
super().setUp()
import pty
import tty
master, slave = pty.openpty()
tty.setraw(master)
self._master = io.FileIO(master, 'r+b')
self._slave = io.FileIO(slave, 'r+b')
def get_debug_monitor_command_line_args(self, attach_pid=None):
commandline_args = self.debug_monitor_extra_args
if attach_pid:
commandline_args += ["--attach=%d" % attach_pid]
libc = ctypes.CDLL(None)
libc.ptsname.argtypes = (ctypes.c_int,)
libc.ptsname.restype = ctypes.c_char_p
pty_path = libc.ptsname(self._master.fileno()).decode()
commandline_args += ["serial://%s" % (pty_path,)]
return commandline_args
def connect_to_debug_monitor(self, attach_pid=None):
self.reverse_connect = False
server = self.launch_debug_monitor(attach_pid=attach_pid)
self.assertIsNotNone(server)
# TODO: make it into proper abstraction
class FakeSocket:
def __init__(self, fd):
self.fd = fd
def sendall(self, frame):
self.fd.write(frame)
def recv(self, count):
return self.fd.read(count)
self.sock = FakeSocket(self._master)
self._server = Server(self.sock, server)
return server
@add_test_categories(["llgs"])
def test_pty_server(self):
self.build()
self.set_inferior_startup_launch()
self.prep_debug_monitor_and_inferior()
# target.xml transfer should trigger a large enough packet to check
# for partial write regression
self.test_sequence.add_log_lines([
"read packet: $qXfer:features:read:target.xml:0,200000#00",
{
"direction": "send",
"regex": re.compile("^\$l(.+)#[0-9a-fA-F]{2}$"),
"capture": {1: "target_xml"},
}],
True)
context = self.expect_gdbremote_sequence()
# verify that we have received a complete, non-malformed XML
self.assertIsNotNone(ET.fromstring(context.get("target_xml")))

View File

@ -17,7 +17,6 @@
#include <unistd.h>
#endif
#include "Acceptor.h"
#include "LLDBServerUtilities.h"
#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
@ -164,15 +163,14 @@ void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server,
}
}
Status writeSocketIdToPipe(Pipe &port_pipe, const std::string &socket_id) {
Status writeSocketIdToPipe(Pipe &port_pipe, llvm::StringRef socket_id) {
size_t bytes_written = 0;
// Write the port number as a C string with the NULL terminator.
return port_pipe.Write(socket_id.c_str(), socket_id.size() + 1,
bytes_written);
return port_pipe.Write(socket_id.data(), socket_id.size() + 1, bytes_written);
}
Status writeSocketIdToPipe(const char *const named_pipe_path,
const std::string &socket_id) {
llvm::StringRef socket_id) {
Pipe port_name_pipe;
// Wait for 10 seconds for pipe to be opened.
auto error = port_name_pipe.OpenAsWriterWithTimeout(named_pipe_path, false,
@ -183,7 +181,7 @@ Status writeSocketIdToPipe(const char *const named_pipe_path,
}
Status writeSocketIdToPipe(lldb::pipe_t unnamed_pipe,
const std::string &socket_id) {
llvm::StringRef socket_id) {
Pipe port_pipe{LLDB_INVALID_PIPE, unnamed_pipe};
return writeSocketIdToPipe(port_pipe, socket_id);
}
@ -197,120 +195,69 @@ void ConnectToRemote(MainLoop &mainloop,
Status error;
std::unique_ptr<Connection> connection_up;
std::string url;
if (connection_fd != -1) {
// Build the connection string.
std::string connection_url = llvm::formatv("fd://{0}", connection_fd).str();
url = llvm::formatv("fd://{0}", connection_fd).str();
// Create the connection.
#if LLDB_ENABLE_POSIX && !defined _WIN32
::fcntl(connection_fd, F_SETFD, FD_CLOEXEC);
#endif
connection_up.reset(new ConnectionFileDescriptor);
auto connection_result = connection_up->Connect(connection_url, &error);
} else if (!host_and_port.empty()) {
llvm::Expected<std::string> url_exp =
LLGSArgToURL(host_and_port, reverse_connect);
if (!url_exp) {
llvm::errs() << llvm::formatv("error: invalid host:port or URL '{0}': "
"{1}\n",
host_and_port,
llvm::toString(url_exp.takeError()));
exit(-1);
}
url = std::move(url_exp.get());
}
if (!url.empty()) {
// Create the connection or server.
std::unique_ptr<ConnectionFileDescriptor> conn_fd_up{
new ConnectionFileDescriptor};
auto connection_result = conn_fd_up->Connect(
url,
[named_pipe_path, unnamed_pipe](llvm::StringRef socket_id) {
// If we have a named pipe to write the socket id back to, do that
// now.
if (named_pipe_path && named_pipe_path[0]) {
Status error = writeSocketIdToPipe(named_pipe_path, socket_id);
if (error.Fail())
llvm::errs() << llvm::formatv(
"failed to write to the named peipe '{0}': {1}\n",
named_pipe_path, error.AsCString());
}
// If we have an unnamed pipe to write the socket id back to, do
// that now.
else if (unnamed_pipe != LLDB_INVALID_PIPE) {
Status error = writeSocketIdToPipe(unnamed_pipe, socket_id);
if (error.Fail())
llvm::errs() << llvm::formatv(
"failed to write to the unnamed pipe: {0}\n", error);
}
},
&error);
if (error.Fail()) {
llvm::errs() << llvm::formatv(
"error: failed to connect to client at '{0}': {1}\n", connection_url,
error);
"error: failed to connect to client at '{0}': {1}\n", url, error);
exit(-1);
}
if (connection_result != eConnectionStatusSuccess) {
llvm::errs() << llvm::formatv(
"error: failed to connect to client at '{0}' "
"(connection status: {1})\n",
connection_url, static_cast<int>(connection_result));
url, static_cast<int>(connection_result));
exit(-1);
}
} else if (!host_and_port.empty()) {
// Parse out host and port.
std::string final_host_and_port;
// If host_and_port starts with ':', default the host to be "localhost" and
// expect the remainder to be the port.
if (host_and_port[0] == ':')
final_host_and_port.append("localhost");
final_host_and_port.append(host_and_port.str());
if (reverse_connect) {
// llgs will connect to the gdb-remote client.
// Ensure we have a port number for the connection.
// Note: use rfind, because the host/port may look like "[::1]:12345".
uint32_t connection_portno = 0;
const std::string::size_type colon_pos = final_host_and_port.rfind(':');
if (colon_pos != std::string::npos)
llvm::to_integer(final_host_and_port.substr(colon_pos + 1),
connection_portno);
if (connection_portno == 0) {
llvm::errs() << "error: port number must be specified on when using "
"reverse connect\n";
exit(1);
}
// Build the connection string.
final_host_and_port.insert(0, "connect://");
// Create the connection.
connection_up.reset(new ConnectionFileDescriptor);
auto connection_result =
connection_up->Connect(final_host_and_port, &error);
if (error.Fail()) {
llvm::errs() << llvm::formatv(
"error: failed to connect to client at '{0}': {1}\n",
final_host_and_port, error);
exit(-1);
}
if (connection_result != eConnectionStatusSuccess) {
llvm::errs() << llvm::formatv(
"error: failed to connect to client at '{0}' "
"(connection status: {1})\n",
final_host_and_port, static_cast<int>(connection_result));
exit(-1);
}
} else {
std::unique_ptr<Acceptor> acceptor_up(
Acceptor::Create(final_host_and_port, false, error));
if (error.Fail()) {
llvm::errs() << llvm::formatv("failed to create acceptor: {0}\n",
error);
exit(1);
}
error = acceptor_up->Listen(1);
if (error.Fail()) {
llvm::errs() << llvm::formatv("failed to listen: {0}\n", error);
exit(1);
}
const std::string socket_id = acceptor_up->GetLocalSocketId();
if (!socket_id.empty()) {
// If we have a named pipe to write the socket id back to, do that now.
if (named_pipe_path && named_pipe_path[0]) {
error = writeSocketIdToPipe(named_pipe_path, socket_id);
if (error.Fail())
llvm::errs() << llvm::formatv(
"failed to write to the named peipe '{0}': {1}\n",
named_pipe_path, error.AsCString());
}
// If we have an unnamed pipe to write the socket id back to, do that
// now.
else if (unnamed_pipe != LLDB_INVALID_PIPE) {
error = writeSocketIdToPipe(unnamed_pipe, socket_id);
if (error.Fail())
llvm::errs() << llvm::formatv(
"failed to write to the unnamed pipe: {0}\n", error);
}
} else {
llvm::errs()
<< "unable to get the socket id for the listening connection\n";
}
Connection *conn = nullptr;
error = acceptor_up->Accept(false, conn);
if (error.Fail()) {
llvm::errs() << llvm::formatv("failed to accept new connection: {0}\n",
error);
exit(1);
}
connection_up.reset(conn);
}
connection_up = std::move(conn_fd_up);
}
error = gdb_server.InitializeConnection(std::move(connection_up));
if (error.Fail()) {

View File

@ -1,6 +1,7 @@
add_lldb_unittest(ProcessGdbRemoteTests
GDBRemoteClientBaseTest.cpp
GDBRemoteCommunicationClientTest.cpp
GDBRemoteCommunicationServerLLGSTest.cpp
GDBRemoteCommunicationServerTest.cpp
GDBRemoteCommunicationTest.cpp
GDBRemoteTestUtils.cpp

View File

@ -0,0 +1,63 @@
//===-- GDBRemoteCommunicationServerLLGSTest.cpp --------------------------===//
//
// 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 "gtest/gtest.h"
#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
using namespace lldb_private::process_gdb_remote;
TEST(GDBRemoteCommunicationServerLLGSTest, LLGSArgToURL) {
// LLGS new-style URLs should be passed through (indepenently of
// --reverse-connect)
EXPECT_EQ(LLGSArgToURL("listen://127.0.0.1:1234", false),
"listen://127.0.0.1:1234");
EXPECT_EQ(LLGSArgToURL("listen://127.0.0.1:1234", true),
"listen://127.0.0.1:1234");
EXPECT_EQ(LLGSArgToURL("connect://127.0.0.1:1234", false),
"connect://127.0.0.1:1234");
EXPECT_EQ(LLGSArgToURL("connect://127.0.0.1:1234", true),
"connect://127.0.0.1:1234");
// LLGS legacy listen URLs should be converted if !reverse_connect
EXPECT_EQ(LLGSArgToURL("tcp://127.0.0.1:1234", false),
"listen://127.0.0.1:1234");
EXPECT_EQ(LLGSArgToURL("unix:///tmp/foo", false), "unix-accept:///tmp/foo");
EXPECT_EQ(LLGSArgToURL("unix-abstract://foo", false),
"unix-abstract-accept://foo");
// LLGS listen host:port pairs should be converted to listen://
EXPECT_EQ(LLGSArgToURL("127.0.0.1:1234", false), "listen://127.0.0.1:1234");
EXPECT_EQ(LLGSArgToURL("[::1]:1234", false), "listen://[::1]:1234");
EXPECT_EQ(LLGSArgToURL("[[::1]:1234]", false), "listen://[[::1]:1234]");
EXPECT_EQ(LLGSArgToURL("localhost:1234", false), "listen://localhost:1234");
EXPECT_EQ(LLGSArgToURL("*:1234", false), "listen://*:1234");
// LLGS listen :port special-case should be converted to listen://
EXPECT_EQ(LLGSArgToURL(":1234", false), "listen://localhost:1234");
// LLGS listen UNIX sockets should be converted to unix-accept://
EXPECT_EQ(LLGSArgToURL("/tmp/foo", false), "unix-accept:///tmp/foo");
EXPECT_EQ(LLGSArgToURL("127.0.0.1", false), "unix-accept://127.0.0.1");
EXPECT_EQ(LLGSArgToURL("[::1]", false), "unix-accept://[::1]");
EXPECT_EQ(LLGSArgToURL("localhost", false), "unix-accept://localhost");
EXPECT_EQ(LLGSArgToURL(":frobnicate", false), "unix-accept://:frobnicate");
// LLGS reverse connect host:port pairs should be converted to connect://
EXPECT_EQ(LLGSArgToURL("127.0.0.1:1234", true), "connect://127.0.0.1:1234");
EXPECT_EQ(LLGSArgToURL("[::1]:1234", true), "connect://[::1]:1234");
EXPECT_EQ(LLGSArgToURL("[[::1]:1234]", true), "connect://[[::1]:1234]");
EXPECT_EQ(LLGSArgToURL("localhost:1234", true), "connect://localhost:1234");
// with LLGS reverse connect, anything else goes as unix-connect://
EXPECT_EQ(LLGSArgToURL("/tmp/foo", true), "unix-connect:///tmp/foo");
EXPECT_EQ(LLGSArgToURL("127.0.0.1", true), "unix-connect://127.0.0.1");
EXPECT_EQ(LLGSArgToURL("[::1]", true), "unix-connect://[::1]");
EXPECT_EQ(LLGSArgToURL("localhost", true), "unix-connect://localhost");
EXPECT_EQ(LLGSArgToURL(":frobnicate", true), "unix-connect://:frobnicate");
}