================ @@ -393,125 +362,222 @@ int main_platform(int argc, char *argv[]) { lldb_private::Args inferior_arguments; inferior_arguments.SetArguments(argc, const_cast<const char **>(argv)); + Socket::SocketProtocol protocol = Socket::ProtocolUnixDomain; + if (fd != SharedSocket::kInvalidFD) { // Child process will handle the connection and exit. + if (gdbserver_port) + protocol = Socket::ProtocolTcp; + Log *log = GetLog(LLDBLog::Platform); - if (!listen_host_port.empty()) { - LLDB_LOGF(log, "lldb-platform child: " - "ambiguous parameters --listen and --child-platform-fd"); - return socket_error; - } - NativeSocket socket; - error = SharedSocket::GetNativeSocket(fd, socket); + NativeSocket sockfd; + error = SharedSocket::GetNativeSocket(fd, sockfd); if (error.Fail()) { LLDB_LOGF(log, "lldb-platform child: %s", error.AsCString()); return socket_error; } - GDBRemoteCommunicationServerPlatform platform(Socket::ProtocolTcp, "tcp"); - if (port_offset > 0) - platform.SetPortOffset(port_offset); - platform.SetPortMap(std::move(gdbserver_portmap)); + GDBRemoteCommunicationServerPlatform platform(protocol, gdbserver_port); + Socket *socket; + if (protocol == Socket::ProtocolTcp) + socket = new TCPSocket(sockfd, /*should_close=*/true, + /*child_processes_inherit=*/false); + else { +#if LLDB_ENABLE_POSIX + socket = new DomainSocket(sockfd, /*should_close=*/true, + /*child_processes_inherit=*/false); +#else + LLDB_LOGF(log, + "lldb-platform child: Unix domain sockets are not supported on " + "this platform."); + return socket_error; +#endif + } platform.SetConnection( - std::unique_ptr<Connection>(new ConnectionFileDescriptor( - new TCPSocket(socket, /*should_close=*/true, - /*child_processes_inherit=*/false)))); + std::unique_ptr<Connection>(new ConnectionFileDescriptor(socket))); client_handle(platform, inferior_arguments); return 0; } - const bool children_inherit_listen_socket = false; - // the test suite makes many connections in parallel, let's not miss any. - // The highest this should get reasonably is a function of the number - // of target CPUs. For now, let's just use 100. - const int backlog = 100; + if (gdbserver_port != 0 && + (gdbserver_port < LOW_PORT || gdbserver_port > HIGH_PORT)) { + WithColor::error() << llvm::formatv("Port number {0} is not in the " + "valid user port range of {1} - {2}\n", + gdbserver_port, LOW_PORT, HIGH_PORT); + return 1; + } - std::unique_ptr<Acceptor> acceptor_up(Acceptor::Create( - listen_host_port, children_inherit_listen_socket, error)); - if (error.Fail()) { - fprintf(stderr, "failed to create acceptor: %s", error.AsCString()); - exit(socket_error); + std::string hostname; + uint16_t platform_port = 0; + + // Try to match socket name as URL - e.g., tcp://localhost:5555 + if (std::optional<URI> uri = URI::Parse(listen_host_port)) { + if (!Socket::FindProtocolByScheme(uri->scheme.str().c_str(), protocol)) { + fprintf(stderr, "Unknown protocol scheme \"%s\".", + uri->scheme.str().c_str()); + return socket_error; + } + if (protocol == Socket::ProtocolTcp) { + hostname = uri->hostname; + if (uri->port) { + platform_port = *(uri->port); + } + } else + hostname = listen_host_port.substr(uri->scheme.size() + strlen("://")); + } else { + // Try to match socket name as $host:port - e.g., localhost:5555 + llvm::Expected<Socket::HostAndPort> host_port = + Socket::DecodeHostAndPort(listen_host_port); + if (!llvm::errorToBool(host_port.takeError())) { + protocol = Socket::ProtocolTcp; + hostname = host_port->hostname; + platform_port = host_port->port; + } else + hostname = listen_host_port; } - error = acceptor_up->Listen(backlog); + if (protocol == Socket::ProtocolTcp) { + if (platform_port != 0 && platform_port == gdbserver_port) { + fprintf(stderr, "The same platform and gdb ports %u.", platform_port); + return socket_error; + } + } else { + if (gdbserver_port) { + fprintf(stderr, + "--gdbserver-port %u is redundant for non-tcp protocol %s.", + gdbserver_port, Socket::FindSchemeByProtocol(protocol)); + return socket_error; + } + if (g_server) { + fprintf(stderr, + "Ambiguous parameters --server --listen %s.\n" + "The protocol must be tcp for the server mode.", + listen_host_port.c_str()); + return socket_error; + } + } + + std::unique_ptr<Socket> sock_platform = + Socket::Create(protocol, /*child_processes_inherit=*/false, error); + if (error.Fail()) { + printf("Failed to create platform socket: %s\n", error.AsCString()); + return socket_error; + } + error = sock_platform->Listen( + protocol == Socket::ProtocolTcp + ? llvm::formatv("{0}:{1}", hostname, platform_port).str() + : hostname, + backlog); if (error.Fail()) { - printf("failed to listen: %s\n", error.AsCString()); - exit(socket_error); + printf("Failed to listen platform: %s\n", error.AsCString()); + return socket_error; } + if (protocol == Socket::ProtocolTcp && platform_port == 0) + platform_port = + static_cast<TCPSocket *>(sock_platform.get())->GetLocalPortNumber(); + if (socket_file) { - error = - save_socket_id_to_file(acceptor_up->GetLocalSocketId(), socket_file); + error = save_socket_id_to_file( + protocol == Socket::ProtocolTcp + ? (platform_port ? llvm::to_string(platform_port) : "") + : hostname, + socket_file); if (error.Fail()) { fprintf(stderr, "failed to write socket id to %s: %s\n", socket_file.GetPath().c_str(), error.AsCString()); return 1; } } - GDBRemoteCommunicationServerPlatform platform( - acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme()); - if (port_offset > 0) - platform.SetPortOffset(port_offset); - - do { - const bool children_inherit_accept_socket = true; - Connection *conn = nullptr; - error = acceptor_up->Accept(children_inherit_accept_socket, conn); + lldb_private::Args gdb_inferior_arguments; + std::unique_ptr<TCPSocket> sock_gdb; + if (protocol == Socket::ProtocolTcp) { + sock_gdb = std::make_unique<TCPSocket>( + /*should_close=*/true, /*child_processes_inherit=*/false); + error = sock_gdb->Listen( + llvm::formatv("{0}:{1}", hostname, gdbserver_port).str(), backlog); if (error.Fail()) { - WithColor::error() << error.AsCString() << '\n'; - exit(socket_error); + printf("Failed to listen gdb: %s\n", error.AsCString()); + return socket_error; } - printf("Connection established.\n"); + if (gdbserver_port == 0) + gdbserver_port = sock_gdb->GetLocalPortNumber(); - if (g_server) { - std::optional<uint16_t> available_port; - { - std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex); - auto port = gdbserver_portmap.GetNextAvailablePort(); - if (port) - available_port = *port; - else - llvm::consumeError(port.takeError()); - } - if (!available_port) { - fprintf(stderr, - "no available gdbserver port for connection - dropping...\n"); - } else { - const Socket *conn_socket = - static_cast<const Socket *>(conn->GetReadObject().get()); - error = - spawn_process(progname, conn_socket, *available_port, port_offset, - inferior_arguments, log_file, log_channels); - if (error.Fail()) { - { - - std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex); - gdbserver_portmap.FreePort(*available_port); - } - LLDB_LOGF(GetLog(LLDBLog::Platform), "spawn_process failed: %s", - error.AsCString()); - WithColor::error() - << "spawn_process failed: " << error.AsCString() << "\n"; - } - } - // Parent doesn't need a connection to the lldb client - delete conn; - - // Parent will continue to listen for new connections. - continue; - } else { - // If not running as a server, this process will not accept - // connections while a connection is active. - acceptor_up.reset(); - - // When not running in server mode, use all available ports - platform.SetPortMap(std::move(gdbserver_portmap)); + gdb_inferior_arguments.AppendArguments(inferior_arguments); + } + + GDBRemoteCommunicationServerPlatform platform(protocol, gdbserver_port); + { + llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>> platform_handles = + sock_platform->Accept( + *g_main_loop, + [progname, gdbserver_port, inferior_arguments, log_file, + log_channels](std::unique_ptr<Socket> sock_up) { + // If not running as a server, this process will not accept ---------------- labath wrote:
I think that's actually great, as it allows us to synchronize with the exit of the child process and solve our global/detached thread problem -- after returning from MainLoop::Run, we just wait for a notification that the process has really exited. Something like: ``` std::promise<void> child_exited; handles = socket.Accept(loop, [&]{ ... launch_info.SetMonitorProcessCallback([&child_exited]{child_exited.set_value();}); Launch(launch_info); handles.clear(); }); loop.Run(); child_exited.get_future().get(); ``` https://github.com/llvm/llvm-project/pull/104238 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits