10b57cec5SDimitry Andric //===-- lldb-platform.cpp ---------------------------------------*- C++ -*-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 9fe6060f1SDimitry Andric #include <cerrno> 100b57cec5SDimitry Andric #if defined(__APPLE__) 110b57cec5SDimitry Andric #include <netinet/in.h> 120b57cec5SDimitry Andric #endif 13fe6060f1SDimitry Andric #include <csignal> 14fe6060f1SDimitry Andric #include <cstdint> 15fe6060f1SDimitry Andric #include <cstdio> 16fe6060f1SDimitry Andric #include <cstdlib> 17fe6060f1SDimitry Andric #include <cstring> 189dba64beSDimitry Andric #if !defined(_WIN32) 190b57cec5SDimitry Andric #include <sys/wait.h> 209dba64beSDimitry Andric #endif 210b57cec5SDimitry Andric #include <fstream> 22bdd1243dSDimitry Andric #include <optional> 230b57cec5SDimitry Andric 240b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 25fe6060f1SDimitry Andric #include "llvm/Support/WithColor.h" 269dba64beSDimitry Andric #include "llvm/Support/raw_ostream.h" 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric #include "Acceptor.h" 290b57cec5SDimitry Andric #include "LLDBServerUtilities.h" 300b57cec5SDimitry Andric #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h" 310b57cec5SDimitry Andric #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" 320b57cec5SDimitry Andric #include "lldb/Host/ConnectionFileDescriptor.h" 330b57cec5SDimitry Andric #include "lldb/Host/HostGetOpt.h" 340b57cec5SDimitry Andric #include "lldb/Host/OptionParser.h" 350b57cec5SDimitry Andric #include "lldb/Host/common/TCPSocket.h" 360b57cec5SDimitry Andric #include "lldb/Utility/FileSpec.h" 370b57cec5SDimitry Andric #include "lldb/Utility/Status.h" 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric using namespace lldb; 400b57cec5SDimitry Andric using namespace lldb_private; 410b57cec5SDimitry Andric using namespace lldb_private::lldb_server; 420b57cec5SDimitry Andric using namespace lldb_private::process_gdb_remote; 430b57cec5SDimitry Andric using namespace llvm; 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric // option descriptors for getopt_long_only() 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric static int g_debug = 0; 480b57cec5SDimitry Andric static int g_verbose = 0; 490b57cec5SDimitry Andric static int g_server = 0; 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric static struct option g_long_options[] = { 520b57cec5SDimitry Andric {"debug", no_argument, &g_debug, 1}, 530b57cec5SDimitry Andric {"verbose", no_argument, &g_verbose, 1}, 540b57cec5SDimitry Andric {"log-file", required_argument, nullptr, 'l'}, 550b57cec5SDimitry Andric {"log-channels", required_argument, nullptr, 'c'}, 560b57cec5SDimitry Andric {"listen", required_argument, nullptr, 'L'}, 570b57cec5SDimitry Andric {"port-offset", required_argument, nullptr, 'p'}, 580b57cec5SDimitry Andric {"gdbserver-port", required_argument, nullptr, 'P'}, 590b57cec5SDimitry Andric {"min-gdbserver-port", required_argument, nullptr, 'm'}, 600b57cec5SDimitry Andric {"max-gdbserver-port", required_argument, nullptr, 'M'}, 610b57cec5SDimitry Andric {"socket-file", required_argument, nullptr, 'f'}, 620b57cec5SDimitry Andric {"server", no_argument, &g_server, 1}, 630b57cec5SDimitry Andric {nullptr, 0, nullptr, 0}}; 640b57cec5SDimitry Andric 650b57cec5SDimitry Andric #if defined(__APPLE__) 660b57cec5SDimitry Andric #define LOW_PORT (IPPORT_RESERVED) 670b57cec5SDimitry Andric #define HIGH_PORT (IPPORT_HIFIRSTAUTO) 680b57cec5SDimitry Andric #else 690b57cec5SDimitry Andric #define LOW_PORT (1024u) 700b57cec5SDimitry Andric #define HIGH_PORT (49151u) 710b57cec5SDimitry Andric #endif 720b57cec5SDimitry Andric 739dba64beSDimitry Andric #if !defined(_WIN32) 740b57cec5SDimitry Andric // Watch for signals 750b57cec5SDimitry Andric static void signal_handler(int signo) { 760b57cec5SDimitry Andric switch (signo) { 770b57cec5SDimitry Andric case SIGHUP: 780b57cec5SDimitry Andric // Use SIGINT first, if that does not work, use SIGHUP as a last resort. 790b57cec5SDimitry Andric // And we should not call exit() here because it results in the global 8081ad6265SDimitry Andric // destructors to be invoked and wreaking havoc on the threads still 8181ad6265SDimitry Andric // running. 8281ad6265SDimitry Andric llvm::errs() << "SIGHUP received, exiting lldb-server...\n"; 830b57cec5SDimitry Andric abort(); 840b57cec5SDimitry Andric break; 850b57cec5SDimitry Andric } 860b57cec5SDimitry Andric } 879dba64beSDimitry Andric #endif 880b57cec5SDimitry Andric 890b57cec5SDimitry Andric static void display_usage(const char *progname, const char *subcommand) { 900b57cec5SDimitry Andric fprintf(stderr, "Usage:\n %s %s [--log-file log-file-name] [--log-channels " 910b57cec5SDimitry Andric "log-channel-list] [--port-file port-file-path] --server " 920b57cec5SDimitry Andric "--listen port\n", 930b57cec5SDimitry Andric progname, subcommand); 940b57cec5SDimitry Andric exit(0); 950b57cec5SDimitry Andric } 960b57cec5SDimitry Andric 970b57cec5SDimitry Andric static Status save_socket_id_to_file(const std::string &socket_id, 980b57cec5SDimitry Andric const FileSpec &file_spec) { 995ffd83dbSDimitry Andric FileSpec temp_file_spec(file_spec.GetDirectory().GetStringRef()); 1000b57cec5SDimitry Andric Status error(llvm::sys::fs::create_directory(temp_file_spec.GetPath())); 1010b57cec5SDimitry Andric if (error.Fail()) 1020b57cec5SDimitry Andric return Status("Failed to create directory %s: %s", 103bdd1243dSDimitry Andric temp_file_spec.GetPath().c_str(), error.AsCString()); 1040b57cec5SDimitry Andric 1059dba64beSDimitry Andric Status status; 10606c3fb27SDimitry Andric if (auto Err = llvm::writeToOutput(file_spec.GetPath(), 10706c3fb27SDimitry Andric [&socket_id](llvm::raw_ostream &OS) { 10806c3fb27SDimitry Andric OS << socket_id; 10906c3fb27SDimitry Andric return llvm::Error::success(); 11006c3fb27SDimitry Andric })) 11106c3fb27SDimitry Andric return Status("Failed to atomically write file %s: %s", 1129dba64beSDimitry Andric file_spec.GetPath().c_str(), 11306c3fb27SDimitry Andric llvm::toString(std::move(Err)).c_str()); 1149dba64beSDimitry Andric return status; 1150b57cec5SDimitry Andric } 1160b57cec5SDimitry Andric 1170b57cec5SDimitry Andric // main 1180b57cec5SDimitry Andric int main_platform(int argc, char *argv[]) { 1190b57cec5SDimitry Andric const char *progname = argv[0]; 1200b57cec5SDimitry Andric const char *subcommand = argv[1]; 1210b57cec5SDimitry Andric argc--; 1220b57cec5SDimitry Andric argv++; 1239dba64beSDimitry Andric #if !defined(_WIN32) 1240b57cec5SDimitry Andric signal(SIGPIPE, SIG_IGN); 1250b57cec5SDimitry Andric signal(SIGHUP, signal_handler); 1269dba64beSDimitry Andric #endif 1270b57cec5SDimitry Andric int long_option_index = 0; 1280b57cec5SDimitry Andric Status error; 1290b57cec5SDimitry Andric std::string listen_host_port; 1300b57cec5SDimitry Andric int ch; 1310b57cec5SDimitry Andric 1320b57cec5SDimitry Andric std::string log_file; 1330b57cec5SDimitry Andric StringRef 1340b57cec5SDimitry Andric log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" 1350b57cec5SDimitry Andric 1360b57cec5SDimitry Andric GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap; 1370b57cec5SDimitry Andric int min_gdbserver_port = 0; 1380b57cec5SDimitry Andric int max_gdbserver_port = 0; 1390b57cec5SDimitry Andric uint16_t port_offset = 0; 1400b57cec5SDimitry Andric 1410b57cec5SDimitry Andric FileSpec socket_file; 1420b57cec5SDimitry Andric bool show_usage = false; 1430b57cec5SDimitry Andric int option_error = 0; 1440b57cec5SDimitry Andric int socket_error = -1; 1450b57cec5SDimitry Andric 1460b57cec5SDimitry Andric std::string short_options(OptionParser::GetShortOptionString(g_long_options)); 1470b57cec5SDimitry Andric 1480b57cec5SDimitry Andric #if __GLIBC__ 1490b57cec5SDimitry Andric optind = 0; 1500b57cec5SDimitry Andric #else 1510b57cec5SDimitry Andric optreset = 1; 1520b57cec5SDimitry Andric optind = 1; 1530b57cec5SDimitry Andric #endif 1540b57cec5SDimitry Andric 1550b57cec5SDimitry Andric while ((ch = getopt_long_only(argc, argv, short_options.c_str(), 1560b57cec5SDimitry Andric g_long_options, &long_option_index)) != -1) { 1570b57cec5SDimitry Andric switch (ch) { 1580b57cec5SDimitry Andric case 0: // Any optional that auto set themselves will return 0 1590b57cec5SDimitry Andric break; 1600b57cec5SDimitry Andric 1610b57cec5SDimitry Andric case 'L': 1620b57cec5SDimitry Andric listen_host_port.append(optarg); 1630b57cec5SDimitry Andric break; 1640b57cec5SDimitry Andric 1650b57cec5SDimitry Andric case 'l': // Set Log File 1660b57cec5SDimitry Andric if (optarg && optarg[0]) 1670b57cec5SDimitry Andric log_file.assign(optarg); 1680b57cec5SDimitry Andric break; 1690b57cec5SDimitry Andric 1700b57cec5SDimitry Andric case 'c': // Log Channels 1710b57cec5SDimitry Andric if (optarg && optarg[0]) 1720b57cec5SDimitry Andric log_channels = StringRef(optarg); 1730b57cec5SDimitry Andric break; 1740b57cec5SDimitry Andric 1750b57cec5SDimitry Andric case 'f': // Socket file 1760b57cec5SDimitry Andric if (optarg && optarg[0]) 1770b57cec5SDimitry Andric socket_file.SetFile(optarg, FileSpec::Style::native); 1780b57cec5SDimitry Andric break; 1790b57cec5SDimitry Andric 1800b57cec5SDimitry Andric case 'p': { 1810b57cec5SDimitry Andric if (!llvm::to_integer(optarg, port_offset)) { 182fe6060f1SDimitry Andric WithColor::error() << "invalid port offset string " << optarg << "\n"; 1830b57cec5SDimitry Andric option_error = 4; 1840b57cec5SDimitry Andric break; 1850b57cec5SDimitry Andric } 1860b57cec5SDimitry Andric if (port_offset < LOW_PORT || port_offset > HIGH_PORT) { 187fe6060f1SDimitry Andric WithColor::error() << llvm::formatv( 188fe6060f1SDimitry Andric "port offset {0} is not in the " 1890b57cec5SDimitry Andric "valid user port range of {1} - {2}\n", 1900b57cec5SDimitry Andric port_offset, LOW_PORT, HIGH_PORT); 1910b57cec5SDimitry Andric option_error = 5; 1920b57cec5SDimitry Andric } 1930b57cec5SDimitry Andric } break; 1940b57cec5SDimitry Andric 1950b57cec5SDimitry Andric case 'P': 1960b57cec5SDimitry Andric case 'm': 1970b57cec5SDimitry Andric case 'M': { 1980b57cec5SDimitry Andric uint16_t portnum; 1990b57cec5SDimitry Andric if (!llvm::to_integer(optarg, portnum)) { 200fe6060f1SDimitry Andric WithColor::error() << "invalid port number string " << optarg << "\n"; 2010b57cec5SDimitry Andric option_error = 2; 2020b57cec5SDimitry Andric break; 2030b57cec5SDimitry Andric } 2040b57cec5SDimitry Andric if (portnum < LOW_PORT || portnum > HIGH_PORT) { 205fe6060f1SDimitry Andric WithColor::error() << llvm::formatv( 206fe6060f1SDimitry Andric "port number {0} is not in the " 2070b57cec5SDimitry Andric "valid user port range of {1} - {2}\n", 2080b57cec5SDimitry Andric portnum, LOW_PORT, HIGH_PORT); 2090b57cec5SDimitry Andric option_error = 1; 2100b57cec5SDimitry Andric break; 2110b57cec5SDimitry Andric } 2120b57cec5SDimitry Andric if (ch == 'P') 213e8d8bef9SDimitry Andric gdbserver_portmap.AllowPort(portnum); 2140b57cec5SDimitry Andric else if (ch == 'm') 2150b57cec5SDimitry Andric min_gdbserver_port = portnum; 2160b57cec5SDimitry Andric else 2170b57cec5SDimitry Andric max_gdbserver_port = portnum; 2180b57cec5SDimitry Andric } break; 2190b57cec5SDimitry Andric 2200b57cec5SDimitry Andric case 'h': /* fall-through is intentional */ 2210b57cec5SDimitry Andric case '?': 2220b57cec5SDimitry Andric show_usage = true; 2230b57cec5SDimitry Andric break; 2240b57cec5SDimitry Andric } 2250b57cec5SDimitry Andric } 2260b57cec5SDimitry Andric 2270b57cec5SDimitry Andric if (!LLDBServerUtilities::SetupLogging(log_file, log_channels, 0)) 2280b57cec5SDimitry Andric return -1; 2290b57cec5SDimitry Andric 2300b57cec5SDimitry Andric // Make a port map for a port range that was specified. 2310b57cec5SDimitry Andric if (min_gdbserver_port && min_gdbserver_port < max_gdbserver_port) { 232e8d8bef9SDimitry Andric gdbserver_portmap = GDBRemoteCommunicationServerPlatform::PortMap( 233e8d8bef9SDimitry Andric min_gdbserver_port, max_gdbserver_port); 2340b57cec5SDimitry Andric } else if (min_gdbserver_port || max_gdbserver_port) { 235fe6060f1SDimitry Andric WithColor::error() << llvm::formatv( 236fe6060f1SDimitry Andric "--min-gdbserver-port ({0}) is not lower than " 237fe6060f1SDimitry Andric "--max-gdbserver-port ({1})\n", 2380b57cec5SDimitry Andric min_gdbserver_port, max_gdbserver_port); 2390b57cec5SDimitry Andric option_error = 3; 2400b57cec5SDimitry Andric } 2410b57cec5SDimitry Andric 2420b57cec5SDimitry Andric // Print usage and exit if no listening port is specified. 2430b57cec5SDimitry Andric if (listen_host_port.empty()) 2440b57cec5SDimitry Andric show_usage = true; 2450b57cec5SDimitry Andric 2460b57cec5SDimitry Andric if (show_usage || option_error) { 2470b57cec5SDimitry Andric display_usage(progname, subcommand); 2480b57cec5SDimitry Andric exit(option_error); 2490b57cec5SDimitry Andric } 2500b57cec5SDimitry Andric 2510b57cec5SDimitry Andric // Skip any options we consumed with getopt_long_only. 2520b57cec5SDimitry Andric argc -= optind; 2530b57cec5SDimitry Andric argv += optind; 2540b57cec5SDimitry Andric lldb_private::Args inferior_arguments; 2550b57cec5SDimitry Andric inferior_arguments.SetArguments(argc, const_cast<const char **>(argv)); 2560b57cec5SDimitry Andric 2570b57cec5SDimitry Andric const bool children_inherit_listen_socket = false; 2580b57cec5SDimitry Andric // the test suite makes many connections in parallel, let's not miss any. 2590b57cec5SDimitry Andric // The highest this should get reasonably is a function of the number 2600b57cec5SDimitry Andric // of target CPUs. For now, let's just use 100. 2610b57cec5SDimitry Andric const int backlog = 100; 2620b57cec5SDimitry Andric 2630b57cec5SDimitry Andric std::unique_ptr<Acceptor> acceptor_up(Acceptor::Create( 2640b57cec5SDimitry Andric listen_host_port, children_inherit_listen_socket, error)); 2650b57cec5SDimitry Andric if (error.Fail()) { 2660b57cec5SDimitry Andric fprintf(stderr, "failed to create acceptor: %s", error.AsCString()); 2670b57cec5SDimitry Andric exit(socket_error); 2680b57cec5SDimitry Andric } 2690b57cec5SDimitry Andric 2700b57cec5SDimitry Andric error = acceptor_up->Listen(backlog); 2710b57cec5SDimitry Andric if (error.Fail()) { 2720b57cec5SDimitry Andric printf("failed to listen: %s\n", error.AsCString()); 2730b57cec5SDimitry Andric exit(socket_error); 2740b57cec5SDimitry Andric } 2750b57cec5SDimitry Andric if (socket_file) { 2760b57cec5SDimitry Andric error = 2770b57cec5SDimitry Andric save_socket_id_to_file(acceptor_up->GetLocalSocketId(), socket_file); 2780b57cec5SDimitry Andric if (error.Fail()) { 2790b57cec5SDimitry Andric fprintf(stderr, "failed to write socket id to %s: %s\n", 2800b57cec5SDimitry Andric socket_file.GetPath().c_str(), error.AsCString()); 2810b57cec5SDimitry Andric return 1; 2820b57cec5SDimitry Andric } 2830b57cec5SDimitry Andric } 2840b57cec5SDimitry Andric 2850b57cec5SDimitry Andric GDBRemoteCommunicationServerPlatform platform( 2860b57cec5SDimitry Andric acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme()); 2870b57cec5SDimitry Andric if (port_offset > 0) 2880b57cec5SDimitry Andric platform.SetPortOffset(port_offset); 2890b57cec5SDimitry Andric 290*0fca6ea1SDimitry Andric do { 2910b57cec5SDimitry Andric const bool children_inherit_accept_socket = true; 2920b57cec5SDimitry Andric Connection *conn = nullptr; 2930b57cec5SDimitry Andric error = acceptor_up->Accept(children_inherit_accept_socket, conn); 2940b57cec5SDimitry Andric if (error.Fail()) { 295fe6060f1SDimitry Andric WithColor::error() << error.AsCString() << '\n'; 2960b57cec5SDimitry Andric exit(socket_error); 2970b57cec5SDimitry Andric } 2980b57cec5SDimitry Andric printf("Connection established.\n"); 299*0fca6ea1SDimitry Andric 3000b57cec5SDimitry Andric if (g_server) { 3010b57cec5SDimitry Andric // Collect child zombie processes. 3029dba64beSDimitry Andric #if !defined(_WIN32) 303*0fca6ea1SDimitry Andric ::pid_t waitResult; 304*0fca6ea1SDimitry Andric while ((waitResult = waitpid(-1, nullptr, WNOHANG)) > 0) { 305*0fca6ea1SDimitry Andric // waitResult is the child pid 306*0fca6ea1SDimitry Andric gdbserver_portmap.FreePortForProcess(waitResult); 307*0fca6ea1SDimitry Andric } 3089dba64beSDimitry Andric #endif 309*0fca6ea1SDimitry Andric // TODO: Clean up portmap for Windows when children die 310*0fca6ea1SDimitry Andric // See https://github.com/llvm/llvm-project/issues/90923 311*0fca6ea1SDimitry Andric 312*0fca6ea1SDimitry Andric // After collecting zombie ports, get the next available 313*0fca6ea1SDimitry Andric GDBRemoteCommunicationServerPlatform::PortMap portmap_for_child; 314*0fca6ea1SDimitry Andric llvm::Expected<uint16_t> available_port = 315*0fca6ea1SDimitry Andric gdbserver_portmap.GetNextAvailablePort(); 316*0fca6ea1SDimitry Andric if (available_port) { 317*0fca6ea1SDimitry Andric // GetNextAvailablePort() may return 0 if gdbserver_portmap is empty. 318*0fca6ea1SDimitry Andric if (*available_port) 319*0fca6ea1SDimitry Andric portmap_for_child.AllowPort(*available_port); 320*0fca6ea1SDimitry Andric } else { 321*0fca6ea1SDimitry Andric llvm::consumeError(available_port.takeError()); 322*0fca6ea1SDimitry Andric fprintf(stderr, 323*0fca6ea1SDimitry Andric "no available gdbserver port for connection - dropping...\n"); 324*0fca6ea1SDimitry Andric delete conn; 325*0fca6ea1SDimitry Andric continue; 326*0fca6ea1SDimitry Andric } 327*0fca6ea1SDimitry Andric platform.SetPortMap(std::move(portmap_for_child)); 328*0fca6ea1SDimitry Andric 329*0fca6ea1SDimitry Andric auto childPid = fork(); 330*0fca6ea1SDimitry Andric if (childPid) { 331*0fca6ea1SDimitry Andric gdbserver_portmap.AssociatePortWithProcess(*available_port, childPid); 3320b57cec5SDimitry Andric // Parent doesn't need a connection to the lldb client 3330b57cec5SDimitry Andric delete conn; 3340b57cec5SDimitry Andric 3350b57cec5SDimitry Andric // Parent will continue to listen for new connections. 3360b57cec5SDimitry Andric continue; 3370b57cec5SDimitry Andric } else { 3380b57cec5SDimitry Andric // Child process will handle the connection and exit. 3390b57cec5SDimitry Andric g_server = 0; 3400b57cec5SDimitry Andric // Listening socket is owned by parent process. 3410b57cec5SDimitry Andric acceptor_up.release(); 3420b57cec5SDimitry Andric } 3430b57cec5SDimitry Andric } else { 3440b57cec5SDimitry Andric // If not running as a server, this process will not accept 3450b57cec5SDimitry Andric // connections while a connection is active. 3460b57cec5SDimitry Andric acceptor_up.reset(); 347*0fca6ea1SDimitry Andric 348*0fca6ea1SDimitry Andric // When not running in server mode, use all available ports 349*0fca6ea1SDimitry Andric platform.SetPortMap(std::move(gdbserver_portmap)); 3500b57cec5SDimitry Andric } 351*0fca6ea1SDimitry Andric 3525ffd83dbSDimitry Andric platform.SetConnection(std::unique_ptr<Connection>(conn)); 3530b57cec5SDimitry Andric 3540b57cec5SDimitry Andric if (platform.IsConnected()) { 3550b57cec5SDimitry Andric if (inferior_arguments.GetArgumentCount() > 0) { 3560b57cec5SDimitry Andric lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; 357*0fca6ea1SDimitry Andric std::optional<uint16_t> port; 3580b57cec5SDimitry Andric std::string socket_name; 3590b57cec5SDimitry Andric Status error = platform.LaunchGDBServer(inferior_arguments, 3600b57cec5SDimitry Andric "", // hostname 3610b57cec5SDimitry Andric pid, port, socket_name); 3620b57cec5SDimitry Andric if (error.Success()) 363e8d8bef9SDimitry Andric platform.SetPendingGdbServer(pid, *port, socket_name); 3640b57cec5SDimitry Andric else 3650b57cec5SDimitry Andric fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString()); 3660b57cec5SDimitry Andric } 3670b57cec5SDimitry Andric 3680b57cec5SDimitry Andric bool interrupt = false; 3690b57cec5SDimitry Andric bool done = false; 3700b57cec5SDimitry Andric while (!interrupt && !done) { 371bdd1243dSDimitry Andric if (platform.GetPacketAndSendResponse(std::nullopt, error, interrupt, 3720b57cec5SDimitry Andric done) != 3730b57cec5SDimitry Andric GDBRemoteCommunication::PacketResult::Success) 3740b57cec5SDimitry Andric break; 3750b57cec5SDimitry Andric } 3760b57cec5SDimitry Andric 3774824e7fdSDimitry Andric if (error.Fail()) 378fe6060f1SDimitry Andric WithColor::error() << error.AsCString() << '\n'; 3790b57cec5SDimitry Andric } 3800b57cec5SDimitry Andric } while (g_server); 3810b57cec5SDimitry Andric 3820b57cec5SDimitry Andric fprintf(stderr, "lldb-server exiting...\n"); 3830b57cec5SDimitry Andric 3840b57cec5SDimitry Andric return 0; 3850b57cec5SDimitry Andric } 386