https://github.com/slydiman updated https://github.com/llvm/llvm-project/pull/101283
>From 396b9a0595c04a8ec2e01c06788ddf9498ee3c4c Mon Sep 17 00:00:00 2001 From: Dmitry Vasilyev <dvassil...@accesssoftek.com> Date: Wed, 31 Jul 2024 05:41:21 +0400 Subject: [PATCH] [lldb] Updated lldb-server to spawn the child process and share socket on Windows `lldb-server --server` works on Windows now w/o multithreading. The rest functionality remains unchanged. Added also PipeWindows::WriteWithTimeout(), fixed PipeWindows::ReadWithTimeout() and missing initialization of m_read_overlapped.hEvent in the constructor PipeWindows(lldb::pipe_t read, lldb::pipe_t write). Fixes #90923, fixes #56346. This is the part 1 of the replacement of #100670. In the part 2 I plan to switch `lldb-server gdbserver` to use --fd and listen a common gdb port for all gdbserver connections. Then we can remove gdb port mapping to fix #97537. --- lldb/include/lldb/Host/windows/PipeWindows.h | 3 + lldb/source/Host/windows/PipeWindows.cpp | 128 ++++--- .../GDBRemoteCommunicationServerPlatform.cpp | 34 ++ .../GDBRemoteCommunicationServerPlatform.h | 2 +- .../tools/lldb-server/LLDBServerUtilities.cpp | 2 + lldb/tools/lldb-server/lldb-platform.cpp | 316 +++++++++++++++--- 6 files changed, 404 insertions(+), 81 deletions(-) diff --git a/lldb/include/lldb/Host/windows/PipeWindows.h b/lldb/include/lldb/Host/windows/PipeWindows.h index 4b5be28d7ae6c..c99cf52be46c1 100644 --- a/lldb/include/lldb/Host/windows/PipeWindows.h +++ b/lldb/include/lldb/Host/windows/PipeWindows.h @@ -61,6 +61,9 @@ class PipeWindows : public PipeBase { Status Delete(llvm::StringRef name) override; Status Write(const void *buf, size_t size, size_t &bytes_written) override; + Status WriteWithTimeout(const void *buf, size_t size, + const std::chrono::microseconds &timeout, + size_t &bytes_written); Status ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read) override; diff --git a/lldb/source/Host/windows/PipeWindows.cpp b/lldb/source/Host/windows/PipeWindows.cpp index c82c919607b5b..318d5abbed74d 100644 --- a/lldb/source/Host/windows/PipeWindows.cpp +++ b/lldb/source/Host/windows/PipeWindows.cpp @@ -58,7 +58,10 @@ PipeWindows::PipeWindows(pipe_t read, pipe_t write) } ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + m_read_overlapped.hEvent = ::CreateEventA(nullptr, TRUE, FALSE, nullptr); + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); + m_write_overlapped.hEvent = ::CreateEventA(nullptr, TRUE, FALSE, nullptr); } PipeWindows::~PipeWindows() { Close(); } @@ -77,6 +80,7 @@ Status PipeWindows::CreateNew(bool child_process_inherit) { m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY); ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); + m_write_overlapped.hEvent = ::CreateEventA(nullptr, TRUE, FALSE, nullptr); return Status(); } @@ -202,6 +206,7 @@ Status PipeWindows::OpenNamedPipe(llvm::StringRef name, m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY); ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); + m_write_overlapped.hEvent = ::CreateEventA(nullptr, TRUE, FALSE, nullptr); } return Status(); @@ -228,6 +233,8 @@ int PipeWindows::ReleaseWriteFileDescriptor() { return PipeWindows::kInvalidDescriptor; int result = m_write_fd; m_write_fd = PipeWindows::kInvalidDescriptor; + if (m_write_overlapped.hEvent) + ::CloseHandle(m_write_overlapped.hEvent); m_write = INVALID_HANDLE_VALUE; ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); return result; @@ -250,6 +257,9 @@ void PipeWindows::CloseWriteFileDescriptor() { if (!CanWrite()) return; + if (m_write_overlapped.hEvent) + ::CloseHandle(m_write_overlapped.hEvent); + _close(m_write_fd); m_write = INVALID_HANDLE_VALUE; m_write_fd = PipeWindows::kInvalidDescriptor; @@ -283,54 +293,94 @@ Status PipeWindows::ReadWithTimeout(void *buf, size_t size, DWORD sys_bytes_read = size; BOOL result = ::ReadFile(m_read, buf, sys_bytes_read, &sys_bytes_read, &m_read_overlapped); - if (!result && GetLastError() != ERROR_IO_PENDING) - return Status(::GetLastError(), eErrorTypeWin32); - - DWORD timeout = (duration == std::chrono::microseconds::zero()) - ? INFINITE - : duration.count() * 1000; - DWORD wait_result = ::WaitForSingleObject(m_read_overlapped.hEvent, timeout); - if (wait_result != WAIT_OBJECT_0) { - // The operation probably failed. However, if it timed out, we need to - // cancel the I/O. Between the time we returned from WaitForSingleObject - // and the time we call CancelIoEx, the operation may complete. If that - // hapens, CancelIoEx will fail and return ERROR_NOT_FOUND. If that - // happens, the original operation should be considered to have been - // successful. - bool failed = true; - DWORD failure_error = ::GetLastError(); - if (wait_result == WAIT_TIMEOUT) { - BOOL cancel_result = CancelIoEx(m_read, &m_read_overlapped); - if (!cancel_result && GetLastError() == ERROR_NOT_FOUND) - failed = false; + if (!result) { + if (GetLastError() != ERROR_IO_PENDING) + return Status(::GetLastError(), eErrorTypeWin32); + else { + DWORD timeout = (duration == std::chrono::microseconds::zero()) + ? INFINITE + : duration.count() * 1000; + DWORD wait_result = + ::WaitForSingleObject(m_read_overlapped.hEvent, timeout); + if (wait_result != WAIT_OBJECT_0) { + // The operation probably failed. However, if it timed out, we need to + // cancel the I/O. Between the time we returned from WaitForSingleObject + // and the time we call CancelIoEx, the operation may complete. If that + // hapens, CancelIoEx will fail and return ERROR_NOT_FOUND. If that + // happens, the original operation should be considered to have been + // successful. + bool failed = true; + DWORD failure_error = ::GetLastError(); + if (wait_result == WAIT_TIMEOUT) { + BOOL cancel_result = CancelIoEx(m_read, &m_read_overlapped); + if (!cancel_result && GetLastError() == ERROR_NOT_FOUND) + failed = false; + } + if (failed) + return Status(failure_error, eErrorTypeWin32); + } + + // Now we call GetOverlappedResult setting bWait to false, since we've + // already waited as long as we're willing to. + if (!GetOverlappedResult(m_read, &m_read_overlapped, &sys_bytes_read, + FALSE)) + return Status(::GetLastError(), eErrorTypeWin32); } - if (failed) - return Status(failure_error, eErrorTypeWin32); } - - // Now we call GetOverlappedResult setting bWait to false, since we've - // already waited as long as we're willing to. - if (!GetOverlappedResult(m_read, &m_read_overlapped, &sys_bytes_read, FALSE)) - return Status(::GetLastError(), eErrorTypeWin32); - bytes_read = sys_bytes_read; return Status(); } -Status PipeWindows::Write(const void *buf, size_t num_bytes, - size_t &bytes_written) { +Status PipeWindows::WriteWithTimeout(const void *buf, size_t size, + const std::chrono::microseconds &duration, + size_t &bytes_written) { if (!CanWrite()) return Status(ERROR_INVALID_HANDLE, eErrorTypeWin32); - DWORD sys_bytes_written = 0; - BOOL write_result = ::WriteFile(m_write, buf, num_bytes, &sys_bytes_written, - &m_write_overlapped); - if (!write_result && GetLastError() != ERROR_IO_PENDING) - return Status(::GetLastError(), eErrorTypeWin32); + bytes_written = 0; + DWORD sys_bytes_write = size; + BOOL result = ::WriteFile(m_write, buf, sys_bytes_write, &sys_bytes_write, + &m_write_overlapped); + if (!result) { + if (GetLastError() != ERROR_IO_PENDING) + return Status(::GetLastError(), eErrorTypeWin32); + else { + DWORD timeout = (duration == std::chrono::microseconds::zero()) + ? INFINITE + : duration.count() * 1000; + DWORD wait_result = + ::WaitForSingleObject(m_write_overlapped.hEvent, timeout); + if (wait_result != WAIT_OBJECT_0) { + // The operation probably failed. However, if it timed out, we need to + // cancel the I/O. Between the time we returned from WaitForSingleObject + // and the time we call CancelIoEx, the operation may complete. If that + // hapens, CancelIoEx will fail and return ERROR_NOT_FOUND. If that + // happens, the original operation should be considered to have been + // successful. + bool failed = true; + DWORD failure_error = ::GetLastError(); + if (wait_result == WAIT_TIMEOUT) { + BOOL cancel_result = CancelIoEx(m_write, &m_write_overlapped); + if (!cancel_result && GetLastError() == ERROR_NOT_FOUND) + failed = false; + } + if (failed) + return Status(failure_error, eErrorTypeWin32); + } + + // Now we call GetOverlappedResult setting bWait to false, since we've + // already waited as long as we're willing to. + if (!GetOverlappedResult(m_write, &m_write_overlapped, &sys_bytes_write, + FALSE)) + return Status(::GetLastError(), eErrorTypeWin32); + } + } - BOOL result = GetOverlappedResult(m_write, &m_write_overlapped, - &sys_bytes_written, TRUE); - if (!result) - return Status(::GetLastError(), eErrorTypeWin32); + bytes_written = sys_bytes_write; return Status(); } + +Status PipeWindows::Write(const void *buf, size_t size, size_t &bytes_written) { + return WriteWithTimeout(buf, size, std::chrono::microseconds::zero(), + bytes_written); +} diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp index 65f1cc12ba307..18824a6cb08ca 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp @@ -159,6 +159,40 @@ GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform( GDBRemoteCommunicationServerPlatform::~GDBRemoteCommunicationServerPlatform() = default; +void GDBRemoteCommunicationServerPlatform::Proc( + const lldb_private::Args &args) { + if (!IsConnected()) + return; + + if (args.GetArgumentCount() > 0) { + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + std::optional<uint16_t> port; + std::string socket_name; + Status error = LaunchGDBServer(args, + "", // hostname + pid, port, socket_name); + if (error.Success()) + SetPendingGdbServer(pid, *port, socket_name); + } + + bool interrupt = false; + bool done = false; + Status error; + while (!interrupt && !done) { + if (GetPacketAndSendResponse(std::nullopt, error, interrupt, done) != + GDBRemoteCommunication::PacketResult::Success) + break; + } + + if (error.Fail()) { + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOGF(log, + "GDBRemoteCommunicationServerPlatform::%s() " + "GetPacketAndSendResponse: %s", + __FUNCTION__, error.AsCString()); + } +} + Status GDBRemoteCommunicationServerPlatform::LaunchGDBServer( const lldb_private::Args &args, std::string hostname, lldb::pid_t &pid, std::optional<uint16_t> &port, std::string &socket_name) { diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h index 1853025466cf0..2f3f313c917c4 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h @@ -85,7 +85,7 @@ class GDBRemoteCommunicationServerPlatform void SetPortOffset(uint16_t port_offset); - void SetInferiorArguments(const lldb_private::Args &args); + void Proc(const lldb_private::Args &args); // Set port if you want to use a specific port number. // Otherwise port will be set to the port that was chosen for you. diff --git a/lldb/tools/lldb-server/LLDBServerUtilities.cpp b/lldb/tools/lldb-server/LLDBServerUtilities.cpp index c3a8df19e969e..5facfbf3105e9 100644 --- a/lldb/tools/lldb-server/LLDBServerUtilities.cpp +++ b/lldb/tools/lldb-server/LLDBServerUtilities.cpp @@ -27,11 +27,13 @@ class TestLogHandler : public LogHandler { : m_stream_sp(stream_sp) {} void Emit(llvm::StringRef message) override { + std::lock_guard<std::mutex> guard(m_mutex); (*m_stream_sp) << message; m_stream_sp->flush(); } private: + std::mutex m_mutex; std::shared_ptr<raw_ostream> m_stream_sp; }; diff --git a/lldb/tools/lldb-server/lldb-platform.cpp b/lldb/tools/lldb-server/lldb-platform.cpp index 7148a1d2a3094..510d569736ff9 100644 --- a/lldb/tools/lldb-server/lldb-platform.cpp +++ b/lldb/tools/lldb-server/lldb-platform.cpp @@ -22,6 +22,7 @@ #include <optional> #include "llvm/Support/FileSystem.h" +#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" @@ -32,8 +33,10 @@ #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/HostGetOpt.h" #include "lldb/Host/OptionParser.h" +#include "lldb/Host/Socket.h" #include "lldb/Host/common/TCPSocket.h" #include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Status.h" using namespace lldb; @@ -60,6 +63,9 @@ static struct option g_long_options[] = { {"max-gdbserver-port", required_argument, nullptr, 'M'}, {"socket-file", required_argument, nullptr, 'f'}, {"server", no_argument, &g_server, 1}, +#if defined(_WIN32) + {"accept", required_argument, nullptr, 'a'}, +#endif {nullptr, 0, nullptr, 0}}; #if defined(__APPLE__) @@ -114,6 +120,218 @@ static Status save_socket_id_to_file(const std::string &socket_id, return status; } +static GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap; +static std::mutex gdbserver_portmap_mutex; + +#if defined(_WIN32) +static void SpawnProcessReaped(lldb::pid_t pid, int signal, int status) { + std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex); + gdbserver_portmap.FreePortForProcess(pid); +} + +static bool SpawnProcessParent(const char *progname, Connection *conn, + uint16_t gdb_port, uint16_t port_offset, + const lldb_private::Args &args, + const std::string &log_file, + const StringRef log_channels) { + Log *log = GetLog(LLDBLog::Platform); + Pipe socket_pipe; + Status error = socket_pipe.CreateNew(true); + if (error.Fail()) { + LLDB_LOGF(log, + "lldb-platform parent: " + "cannot create pipe: %s", + error.AsCString()); + return false; + } + + ProcessLaunchInfo launch_info; + FileSpec self_spec(progname, FileSpec::Style::native); + launch_info.SetExecutableFile(self_spec, true); + Args &self_args = launch_info.GetArguments(); + self_args.AppendArgument(llvm::StringRef("platform")); + self_args.AppendArgument(llvm::StringRef("--accept")); + self_args.AppendArgument(llvm::to_string(socket_pipe.GetReadPipe())); + if (gdb_port) { + self_args.AppendArgument(llvm::StringRef("--gdbserver-port")); + self_args.AppendArgument(llvm::to_string(gdb_port)); + } + if (port_offset > 0) { + self_args.AppendArgument(llvm::StringRef("--port-offset")); + self_args.AppendArgument(llvm::to_string(port_offset)); + } + if (!log_file.empty()) { + self_args.AppendArgument(llvm::StringRef("--log-file")); + self_args.AppendArgument(log_file); + } + if (!log_channels.empty()) { + self_args.AppendArgument(llvm::StringRef("--log-channels")); + self_args.AppendArgument(log_channels); + } + if (args.GetArgumentCount() > 0) { + self_args.AppendArgument("--"); + self_args.AppendArguments(args); + } + + launch_info.SetLaunchInSeparateProcessGroup(false); + launch_info.SetMonitorProcessCallback(&SpawnProcessReaped); + + // Copy the current environment. + // WSASocket(FROM_PROTOCOL_INFO) will fail in the child process + // with the error WSAEPROVIDERFAILEDINIT if the SystemRoot is missing + // in the environment. + launch_info.GetEnvironment() = Host::GetEnvironment(); + + launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO); + + launch_info.AppendCloseFileAction(socket_pipe.GetWriteFileDescriptor()); + + // Close STDIN, STDOUT and STDERR. + launch_info.AppendCloseFileAction(STDIN_FILENO); + launch_info.AppendCloseFileAction(STDOUT_FILENO); + launch_info.AppendCloseFileAction(STDERR_FILENO); + + // Redirect STDIN, STDOUT and STDERR to "/dev/null". + launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false); + launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true); + launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true); + + std::string cmd; + self_args.GetCommandString(cmd); + + error = Host::LaunchProcess(launch_info); + if (error.Fail()) { + LLDB_LOGF(log, + "lldb-platform parent: " + "cannot launch child process for connection: %s", + error.AsCString()); + return false; + } + + lldb::pid_t childPid = launch_info.GetProcessID(); + if (childPid == LLDB_INVALID_PROCESS_ID) { + LLDB_LOGF(log, "lldb-platform parent: " + "cannot launch child process for connection: invalid pid"); + return false; + } + LLDB_LOGF(log, + "lldb-platform parent: " + "launched '%s', pid=0x%x", + cmd.c_str(), childPid); + + { + std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex); + gdbserver_portmap.AssociatePortWithProcess(gdb_port, childPid); + } + + if (socket_pipe.CanRead()) + socket_pipe.CloseReadFileDescriptor(); + if (!socket_pipe.CanWrite()) { + LLDB_LOGF(log, "lldb-platform parent: " + "cannot write to socket_pipe"); + Host::Kill(childPid, SIGTERM); + return false; + } + + const TCPSocket &socket = + static_cast<const TCPSocket &>(*conn->GetReadObject()); + NativeSocket nativeSocket = socket.GetNativeSocket(); + + WSAPROTOCOL_INFO protocol_info; + if (::WSADuplicateSocket(nativeSocket, childPid, &protocol_info) == + SOCKET_ERROR) { + LLDB_LOGF(log, + "lldb-platform parent: " + "WSADuplicateSocket() failed, error: %d", + ::WSAGetLastError()); + Host::Kill(childPid, SIGTERM); + return false; + } + + size_t num_bytes; + error = socket_pipe.WriteWithTimeout(&protocol_info, sizeof(protocol_info), + std::chrono::seconds(2), num_bytes); + if (error.Fail()) { + LLDB_LOGF(log, + "lldb-platform parent: " + "socket_pipe.WriteWithTimeout(WSAPROTOCOL_INFO) failed: %s", + error.AsCString()); + Host::Kill(childPid, SIGTERM); + return false; + } + if (num_bytes != sizeof(protocol_info)) { + LLDB_LOGF(log, + "lldb-platform parent: " + "socket_pipe.WriteWithTimeout(WSAPROTOCOL_INFO) failed: %d bytes", + num_bytes); + Host::Kill(childPid, SIGTERM); + return false; + } + + socket_pipe.Close(); + + return true; +} + +static bool SpawnProcessChild(pipe_t accept_fd, + const std::string &listen_host_port, + uint16_t port_offset, + const lldb_private::Args &args) { + if (accept_fd == INVALID_HANDLE_VALUE) + return false; + + Log *log = GetLog(LLDBLog::Platform); + if (!listen_host_port.empty()) { + LLDB_LOGF(log, "lldb-platform child: " + "ambiguous parameters --listen and --accept"); + exit(SOCKET_ERROR); + } + + Pipe socket_pipe(accept_fd, LLDB_INVALID_PIPE); + + WSAPROTOCOL_INFO protocol_info; + size_t num_bytes; + Status error = + socket_pipe.ReadWithTimeout(&protocol_info, sizeof(protocol_info), + std::chrono::seconds(2), num_bytes); + if (error.Fail()) { + LLDB_LOGF(log, + "lldb-platform child: " + "socket_pipe(0x%x).ReadWithTimeout(WSAPROTOCOL_INFO) failed: %s", + accept_fd, error.AsCString()); + exit(SOCKET_ERROR); + } + if (num_bytes != sizeof(protocol_info)) { + LLDB_LOGF(log, + "lldb-platform child: " + "socket_pipe(0x%x).ReadWithTimeout(WSAPROTOCOL_INFO) failed: " + "%d bytes", + num_bytes); + exit(SOCKET_ERROR); + } + + NativeSocket socket = ::WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, &protocol_info, 0, 0); + if (socket == INVALID_SOCKET) { + LLDB_LOGF(log, + "lldb-platform child: " + "WSASocket(FROM_PROTOCOL_INFO) failed: error %d", + ::WSAGetLastError()); + exit(SOCKET_ERROR); + } + + Connection *conn = + new ConnectionFileDescriptor(new TCPSocket(socket, true, false)); + GDBRemoteCommunicationServerPlatform platform(Socket::ProtocolTcp, "tcp"); + if (port_offset > 0) + platform.SetPortOffset(port_offset); + platform.SetPortMap(std::move(gdbserver_portmap)); + platform.SetConnection(std::unique_ptr<Connection>(conn)); + platform.Proc(args); + return true; +} +#endif + // main int main_platform(int argc, char *argv[]) { const char *progname = argv[0]; @@ -123,6 +341,8 @@ int main_platform(int argc, char *argv[]) { #if !defined(_WIN32) signal(SIGPIPE, SIG_IGN); signal(SIGHUP, signal_handler); +#else + pipe_t accept_fd = INVALID_HANDLE_VALUE; #endif int long_option_index = 0; Status error; @@ -133,7 +353,6 @@ int main_platform(int argc, char *argv[]) { StringRef log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" - GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap; int min_gdbserver_port = 0; int max_gdbserver_port = 0; uint16_t port_offset = 0; @@ -217,6 +436,17 @@ int main_platform(int argc, char *argv[]) { max_gdbserver_port = portnum; } break; +#if defined(_WIN32) + case 'a': { + uint64_t fd; + if (!llvm::to_integer(optarg, fd)) { + WithColor::error() << "invalid accept fd " << optarg << "\n"; + option_error = 6; + } else + accept_fd = (pipe_t)fd; + } break; +#endif + case 'h': /* fall-through is intentional */ case '?': show_usage = true; @@ -240,7 +470,11 @@ int main_platform(int argc, char *argv[]) { } // Print usage and exit if no listening port is specified. +#if !defined(_WIN32) if (listen_host_port.empty()) +#else + if (listen_host_port.empty() && accept_fd == INVALID_HANDLE_VALUE) +#endif show_usage = true; if (show_usage || option_error) { @@ -254,6 +488,13 @@ int main_platform(int argc, char *argv[]) { lldb_private::Args inferior_arguments; inferior_arguments.SetArguments(argc, const_cast<const char **>(argv)); +#if defined(_WIN32) + // Child process will handle the connection and exit. + if (SpawnProcessChild(accept_fd, listen_host_port, port_offset, + inferior_arguments)) + return 0; +#endif + 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 @@ -303,32 +544,39 @@ int main_platform(int argc, char *argv[]) { ::pid_t waitResult; while ((waitResult = waitpid(-1, nullptr, WNOHANG)) > 0) { // waitResult is the child pid + std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex); gdbserver_portmap.FreePortForProcess(waitResult); } #endif - // TODO: Clean up portmap for Windows when children die - // See https://github.com/llvm/llvm-project/issues/90923 - // After collecting zombie ports, get the next available - GDBRemoteCommunicationServerPlatform::PortMap portmap_for_child; - llvm::Expected<uint16_t> available_port = - gdbserver_portmap.GetNextAvailablePort(); - if (available_port) { - // GetNextAvailablePort() may return 0 if gdbserver_portmap is empty. - if (*available_port) - portmap_for_child.AllowPort(*available_port); - } else { - llvm::consumeError(available_port.takeError()); + 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"); delete conn; continue; } +#if !defined(_WIN32) + GDBRemoteCommunicationServerPlatform::PortMap portmap_for_child; + // GetNextAvailablePort() may return 0 if gdbserver_portmap is empty. + if (*available_port) + portmap_for_child.AllowPort(*available_port); platform.SetPortMap(std::move(portmap_for_child)); auto childPid = fork(); if (childPid) { - gdbserver_portmap.AssociatePortWithProcess(*available_port, childPid); + { + std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex); + gdbserver_portmap.AssociatePortWithProcess(*available_port, childPid); + } // Parent doesn't need a connection to the lldb client delete conn; @@ -340,6 +588,18 @@ int main_platform(int argc, char *argv[]) { // Listening socket is owned by parent process. acceptor_up.release(); } +#else + if (!SpawnProcessParent(progname, conn, *available_port, port_offset, + inferior_arguments, log_file, log_channels)) { + std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex); + gdbserver_portmap.FreePort(*available_port); + } + // Parent doesn't need a connection to the lldb client + delete conn; + + // Parent will continue to listen for new connections. + continue; +#endif } else { // If not running as a server, this process will not accept // connections while a connection is active. @@ -350,33 +610,7 @@ int main_platform(int argc, char *argv[]) { } platform.SetConnection(std::unique_ptr<Connection>(conn)); - - if (platform.IsConnected()) { - if (inferior_arguments.GetArgumentCount() > 0) { - lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; - std::optional<uint16_t> port; - std::string socket_name; - Status error = platform.LaunchGDBServer(inferior_arguments, - "", // hostname - pid, port, socket_name); - if (error.Success()) - platform.SetPendingGdbServer(pid, *port, socket_name); - else - fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString()); - } - - bool interrupt = false; - bool done = false; - while (!interrupt && !done) { - if (platform.GetPacketAndSendResponse(std::nullopt, error, interrupt, - done) != - GDBRemoteCommunication::PacketResult::Success) - break; - } - - if (error.Fail()) - WithColor::error() << error.AsCString() << '\n'; - } + platform.Proc(inferior_arguments); } while (g_server); fprintf(stderr, "lldb-server exiting...\n"); _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits