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
signal_handler(int signo)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
display_usage(const char * progname,const char * subcommand)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
save_socket_id_to_file(const std::string & socket_id,const FileSpec & file_spec)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
main_platform(int argc,char * argv[])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 do {
2860b57cec5SDimitry Andric GDBRemoteCommunicationServerPlatform platform(
2870b57cec5SDimitry Andric acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme());
2880b57cec5SDimitry Andric
2890b57cec5SDimitry Andric if (port_offset > 0)
2900b57cec5SDimitry Andric platform.SetPortOffset(port_offset);
2910b57cec5SDimitry Andric
2920b57cec5SDimitry Andric if (!gdbserver_portmap.empty()) {
2930b57cec5SDimitry Andric platform.SetPortMap(std::move(gdbserver_portmap));
2940b57cec5SDimitry Andric }
2950b57cec5SDimitry Andric
2960b57cec5SDimitry Andric const bool children_inherit_accept_socket = true;
2970b57cec5SDimitry Andric Connection *conn = nullptr;
2980b57cec5SDimitry Andric error = acceptor_up->Accept(children_inherit_accept_socket, conn);
2990b57cec5SDimitry Andric if (error.Fail()) {
300fe6060f1SDimitry Andric WithColor::error() << error.AsCString() << '\n';
3010b57cec5SDimitry Andric exit(socket_error);
3020b57cec5SDimitry Andric }
3030b57cec5SDimitry Andric printf("Connection established.\n");
3040b57cec5SDimitry Andric if (g_server) {
3050b57cec5SDimitry Andric // Collect child zombie processes.
3069dba64beSDimitry Andric #if !defined(_WIN32)
3070b57cec5SDimitry Andric while (waitpid(-1, nullptr, WNOHANG) > 0)
3080b57cec5SDimitry Andric ;
3099dba64beSDimitry Andric #endif
3100b57cec5SDimitry Andric if (fork()) {
3110b57cec5SDimitry Andric // Parent doesn't need a connection to the lldb client
3120b57cec5SDimitry Andric delete conn;
3130b57cec5SDimitry Andric
3140b57cec5SDimitry Andric // Parent will continue to listen for new connections.
3150b57cec5SDimitry Andric continue;
3160b57cec5SDimitry Andric } else {
3170b57cec5SDimitry Andric // Child process will handle the connection and exit.
3180b57cec5SDimitry Andric g_server = 0;
3190b57cec5SDimitry Andric // Listening socket is owned by parent process.
3200b57cec5SDimitry Andric acceptor_up.release();
3210b57cec5SDimitry Andric }
3220b57cec5SDimitry Andric } else {
3230b57cec5SDimitry Andric // If not running as a server, this process will not accept
3240b57cec5SDimitry Andric // connections while a connection is active.
3250b57cec5SDimitry Andric acceptor_up.reset();
3260b57cec5SDimitry Andric }
3275ffd83dbSDimitry Andric platform.SetConnection(std::unique_ptr<Connection>(conn));
3280b57cec5SDimitry Andric
3290b57cec5SDimitry Andric if (platform.IsConnected()) {
3300b57cec5SDimitry Andric if (inferior_arguments.GetArgumentCount() > 0) {
3310b57cec5SDimitry Andric lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
332bdd1243dSDimitry Andric std::optional<uint16_t> port = 0;
3330b57cec5SDimitry Andric std::string socket_name;
3340b57cec5SDimitry Andric Status error = platform.LaunchGDBServer(inferior_arguments,
3350b57cec5SDimitry Andric "", // hostname
3360b57cec5SDimitry Andric pid, port, socket_name);
3370b57cec5SDimitry Andric if (error.Success())
338e8d8bef9SDimitry Andric platform.SetPendingGdbServer(pid, *port, socket_name);
3390b57cec5SDimitry Andric else
3400b57cec5SDimitry Andric fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString());
3410b57cec5SDimitry Andric }
3420b57cec5SDimitry Andric
3430b57cec5SDimitry Andric bool interrupt = false;
3440b57cec5SDimitry Andric bool done = false;
3450b57cec5SDimitry Andric while (!interrupt && !done) {
346bdd1243dSDimitry Andric if (platform.GetPacketAndSendResponse(std::nullopt, error, interrupt,
3470b57cec5SDimitry Andric done) !=
3480b57cec5SDimitry Andric GDBRemoteCommunication::PacketResult::Success)
3490b57cec5SDimitry Andric break;
3500b57cec5SDimitry Andric }
3510b57cec5SDimitry Andric
3524824e7fdSDimitry Andric if (error.Fail())
353fe6060f1SDimitry Andric WithColor::error() << error.AsCString() << '\n';
3540b57cec5SDimitry Andric }
3550b57cec5SDimitry Andric } while (g_server);
3560b57cec5SDimitry Andric
3570b57cec5SDimitry Andric fprintf(stderr, "lldb-server exiting...\n");
3580b57cec5SDimitry Andric
3590b57cec5SDimitry Andric return 0;
3600b57cec5SDimitry Andric }
361