Index: lldb/include/lldb/Host/posix/ConnectionFileDescriptorPosix.h =================================================================== --- lldb/include/lldb/Host/posix/ConnectionFileDescriptorPosix.h +++ lldb/include/lldb/Host/posix/ConnectionFileDescriptorPosix.h @@ -28,6 +28,9 @@ class ConnectionFileDescriptor : public Connection { public: + typedef llvm::function_ref + socket_id_callback_type; + ConnectionFileDescriptor(bool child_processes_inherit = false); ConnectionFileDescriptor(int fd, bool owns_fd); @@ -38,7 +41,12 @@ 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 @@ 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, + socket_id_callback_type socket_id_callback, Status *error_ptr); - lldb::ConnectionStatus ConnectUDP(llvm::StringRef args, Status *error_ptr); + lldb::ConnectionStatus + NamedSocketConnect(llvm::StringRef socket_name, + socket_id_callback_type socket_id_callback, + Status *error_ptr); - lldb::ConnectionStatus NamedSocketConnect(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 NamedSocketAccept(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 UnixAbstractSocketConnect(llvm::StringRef socket_name, - Status *error_ptr); + lldb::ConnectionStatus + UnixAbstractSocketConnect(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 ConnectFD(llvm::StringRef args, + socket_id_callback_type socket_id_callback, + Status *error_ptr); - lldb::ConnectionStatus ConnectFile(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, - 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 @@ std::atomic 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; Index: lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp =================================================================== --- lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -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(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(fd, File::eOpenOptionReadWrite, owns_fd); @@ -77,7 +77,7 @@ 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 @@ 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 guard(m_mutex); Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); LLDB_LOGF(log, "%p ConnectionFileDescriptor::Connect (url = '%s')", @@ -143,10 +150,12 @@ if (!path.empty()) { auto method = llvm::StringSwitch(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 @@ .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 @@ // 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 @@ 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 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 @@ 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 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 @@ 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> listening_socket = Socket::TcpListen(s, m_child_processes_inherit, &m_port_predicate); if (!listening_socket) { @@ -586,6 +635,8 @@ return eConnectionStatusError; } + uint16_t port = listening_socket.get()->GetLocalPortNumber(); + socket_id_callback(port != 0 ? std::to_string(port) : ""); Socket *accepted_socket; Status error = listening_socket.get()->Accept(accepted_socket); @@ -598,8 +649,10 @@ 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 @@ 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> socket = @@ -637,8 +692,10 @@ 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 @@ 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 @@ -729,16 +787,15 @@ ::fcntl(fd, F_SETFL, flags); } } - m_io_sp = - std::make_shared(fd, File::eOpenOptionReadWrite, true); + m_io_sp = std::make_shared(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... Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -289,6 +289,9 @@ operator=(const GDBRemoteCommunicationServerLLGS &) = delete; }; +llvm::Expected LLGSArgToURL(llvm::StringRef url_arg, + bool reverse_connect); + } // namespace process_gdb_remote } // namespace lldb_private Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -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,47 @@ assert(!bool(flags & ~m_process_factory.GetSupportedExtensions())); process.SetEnabledExtensions(flags); } + +llvm::Expected +lldb_private::process_gdb_remote::LLGSArgToURL(llvm::StringRef url_arg, + bool reverse_connect) { + // Try parsing the argument as URL. + if (llvm::Optional 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(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}; + + // In reverse connect mode, port must be specified. + if (reverse_connect) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "port number must be specified on when using reverse connect"); + + // If none of the above applied, interpret the argument as UNIX socket path. + return {"unix-accept://" + url_arg.str()}; +} Index: lldb/test/API/tools/lldb-server/TestPtyServer.py =================================================================== --- /dev/null +++ lldb/test/API/tools/lldb-server/TestPtyServer.py @@ -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"))) Index: lldb/tools/lldb-server/lldb-gdbserver.cpp =================================================================== --- lldb/tools/lldb-server/lldb-gdbserver.cpp +++ lldb/tools/lldb-server/lldb-gdbserver.cpp @@ -17,7 +17,6 @@ #include #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 @@ } } -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(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,74 @@ Status error; std::unique_ptr 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 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 conn_fd_up{ + new ConnectionFileDescriptor}; + auto connection_result = conn_fd_up->Connect( + url, + [named_pipe_path, unnamed_pipe](llvm::StringRef socket_id) { + 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]) { + 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); + } + } else { + llvm::errs() << "unable to get the socket id for the listening " + "connection\n"; + } + }, + &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(connection_result)); + url, static_cast(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(connection_result)); - exit(-1); - } - } else { - std::unique_ptr 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()) { Index: lldb/unittests/Process/gdb-remote/CMakeLists.txt =================================================================== --- lldb/unittests/Process/gdb-remote/CMakeLists.txt +++ lldb/unittests/Process/gdb-remote/CMakeLists.txt @@ -1,6 +1,7 @@ add_lldb_unittest(ProcessGdbRemoteTests GDBRemoteClientBaseTest.cpp GDBRemoteCommunicationClientTest.cpp + GDBRemoteCommunicationServerLLGSTest.cpp GDBRemoteCommunicationServerTest.cpp GDBRemoteCommunicationTest.cpp GDBRemoteTestUtils.cpp Index: lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerLLGSTest.cpp =================================================================== --- /dev/null +++ lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerLLGSTest.cpp @@ -0,0 +1,75 @@ +//===-- 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 "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h" + +#include "llvm/Testing/Support/Error.h" + +using namespace lldb_private::process_gdb_remote; + +TEST(GDBRemoteCommunicationServerLLGSTest, LLGSArgToURL) { + // LLGS new-style URLs should be passed through (indepenently of + // --reverse-connect) + EXPECT_THAT_EXPECTED(LLGSArgToURL("listen://127.0.0.1:1234", false), + llvm::HasValue("listen://127.0.0.1:1234")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("listen://127.0.0.1:1234", true), + llvm::HasValue("listen://127.0.0.1:1234")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("connect://127.0.0.1:1234", false), + llvm::HasValue("connect://127.0.0.1:1234")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("connect://127.0.0.1:1234", true), + llvm::HasValue("connect://127.0.0.1:1234")); + + // LLGS legacy listen URLs should be converted if !reverse_connect + EXPECT_THAT_EXPECTED(LLGSArgToURL("tcp://127.0.0.1:1234", false), + llvm::HasValue("listen://127.0.0.1:1234")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("unix:///tmp/foo", false), + llvm::HasValue("unix-accept:///tmp/foo")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("unix-abstract://foo", false), + llvm::HasValue("unix-abstract-accept://foo")); + + // LLGS listen host:port pairs should be converted to listen:// + EXPECT_THAT_EXPECTED(LLGSArgToURL("127.0.0.1:1234", false), + llvm::HasValue("listen://127.0.0.1:1234")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("[::1]:1234", false), + llvm::HasValue("listen://[::1]:1234")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("[[::1]:1234]", false), + llvm::HasValue("listen://[[::1]:1234]")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("localhost:1234", false), + llvm::HasValue("listen://localhost:1234")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("*:1234", false), + llvm::HasValue("listen://*:1234")); + + // LLGS listen :port special-case should be converted to listen:// + EXPECT_THAT_EXPECTED(LLGSArgToURL(":1234", false), + llvm::HasValue("listen://localhost:1234")); + + // LLGS listen UNIX sockets should be converted to unix-accept:// + EXPECT_THAT_EXPECTED(LLGSArgToURL("/tmp/foo", false), + llvm::HasValue("unix-accept:///tmp/foo")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("127.0.0.1", false), + llvm::HasValue("unix-accept://127.0.0.1")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("[::1]", false), + llvm::HasValue("unix-accept://[::1]")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("localhost", false), + llvm::HasValue("unix-accept://localhost")); + EXPECT_THAT_EXPECTED(LLGSArgToURL(":frobnicate", false), + llvm::HasValue("unix-accept://:frobnicate")); + + // LLGS reverse connect host:port pairs should be converted to listen:// + EXPECT_THAT_EXPECTED(LLGSArgToURL("127.0.0.1:1234", true), + llvm::HasValue("connect://127.0.0.1:1234")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("[::1]:1234", true), + llvm::HasValue("connect://[::1]:1234")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("[[::1]:1234]", true), + llvm::HasValue("connect://[[::1]:1234]")); + EXPECT_THAT_EXPECTED(LLGSArgToURL("localhost:1234", true), + llvm::HasValue("connect://localhost:1234")); +} +