10b57cec5SDimitry Andric //===-- lldb-gdbserver.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>
10fe6060f1SDimitry Andric #include <cstdint>
11fe6060f1SDimitry Andric #include <cstdio>
12fe6060f1SDimitry Andric #include <cstdlib>
13fe6060f1SDimitry Andric #include <cstring>
140b57cec5SDimitry Andric
150b57cec5SDimitry Andric #ifndef _WIN32
16fe6060f1SDimitry Andric #include <csignal>
170b57cec5SDimitry Andric #include <unistd.h>
180b57cec5SDimitry Andric #endif
190b57cec5SDimitry Andric
200b57cec5SDimitry Andric #include "LLDBServerUtilities.h"
210b57cec5SDimitry Andric #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
220b57cec5SDimitry Andric #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
23480093f4SDimitry Andric #include "lldb/Host/Config.h"
240b57cec5SDimitry Andric #include "lldb/Host/ConnectionFileDescriptor.h"
250b57cec5SDimitry Andric #include "lldb/Host/FileSystem.h"
260b57cec5SDimitry Andric #include "lldb/Host/Pipe.h"
270b57cec5SDimitry Andric #include "lldb/Host/Socket.h"
280b57cec5SDimitry Andric #include "lldb/Host/common/NativeProcessProtocol.h"
290b57cec5SDimitry Andric #include "lldb/Target/Process.h"
3081ad6265SDimitry Andric #include "lldb/Utility/LLDBLog.h"
310b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
320b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
33e8d8bef9SDimitry Andric #include "llvm/Option/ArgList.h"
34e8d8bef9SDimitry Andric #include "llvm/Option/OptTable.h"
35e8d8bef9SDimitry Andric #include "llvm/Option/Option.h"
360b57cec5SDimitry Andric #include "llvm/Support/Errno.h"
3706c3fb27SDimitry Andric #include "llvm/Support/Error.h"
38e8d8bef9SDimitry Andric #include "llvm/Support/WithColor.h"
390b57cec5SDimitry Andric
400b57cec5SDimitry Andric #if defined(__linux__)
410b57cec5SDimitry Andric #include "Plugins/Process/Linux/NativeProcessLinux.h"
42e8d8bef9SDimitry Andric #elif defined(__FreeBSD__)
43d409305fSDimitry Andric #include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h"
440b57cec5SDimitry Andric #elif defined(__NetBSD__)
450b57cec5SDimitry Andric #include "Plugins/Process/NetBSD/NativeProcessNetBSD.h"
469dba64beSDimitry Andric #elif defined(_WIN32)
479dba64beSDimitry Andric #include "Plugins/Process/Windows/Common/NativeProcessWindows.h"
480b57cec5SDimitry Andric #endif
490b57cec5SDimitry Andric
500b57cec5SDimitry Andric #ifndef LLGS_PROGRAM_NAME
510b57cec5SDimitry Andric #define LLGS_PROGRAM_NAME "lldb-server"
520b57cec5SDimitry Andric #endif
530b57cec5SDimitry Andric
540b57cec5SDimitry Andric #ifndef LLGS_VERSION_STR
550b57cec5SDimitry Andric #define LLGS_VERSION_STR "local_build"
560b57cec5SDimitry Andric #endif
570b57cec5SDimitry Andric
580b57cec5SDimitry Andric using namespace llvm;
590b57cec5SDimitry Andric using namespace lldb;
600b57cec5SDimitry Andric using namespace lldb_private;
610b57cec5SDimitry Andric using namespace lldb_private::lldb_server;
620b57cec5SDimitry Andric using namespace lldb_private::process_gdb_remote;
630b57cec5SDimitry Andric
640b57cec5SDimitry Andric namespace {
650b57cec5SDimitry Andric #if defined(__linux__)
6606c3fb27SDimitry Andric typedef process_linux::NativeProcessLinux::Manager NativeProcessManager;
67e8d8bef9SDimitry Andric #elif defined(__FreeBSD__)
6806c3fb27SDimitry Andric typedef process_freebsd::NativeProcessFreeBSD::Manager NativeProcessManager;
690b57cec5SDimitry Andric #elif defined(__NetBSD__)
7006c3fb27SDimitry Andric typedef process_netbsd::NativeProcessNetBSD::Manager NativeProcessManager;
719dba64beSDimitry Andric #elif defined(_WIN32)
7206c3fb27SDimitry Andric typedef NativeProcessWindows::Manager NativeProcessManager;
730b57cec5SDimitry Andric #else
740b57cec5SDimitry Andric // Dummy implementation to make sure the code compiles
7506c3fb27SDimitry Andric class NativeProcessManager : public NativeProcessProtocol::Manager {
760b57cec5SDimitry Andric public:
7706c3fb27SDimitry Andric NativeProcessManager(MainLoop &mainloop)
7806c3fb27SDimitry Andric : NativeProcessProtocol::Manager(mainloop) {}
7906c3fb27SDimitry Andric
800b57cec5SDimitry Andric llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
810b57cec5SDimitry Andric Launch(ProcessLaunchInfo &launch_info,
8206c3fb27SDimitry Andric NativeProcessProtocol::NativeDelegate &native_delegate) override {
830b57cec5SDimitry Andric llvm_unreachable("Not implemented");
840b57cec5SDimitry Andric }
850b57cec5SDimitry Andric llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
8606c3fb27SDimitry Andric Attach(lldb::pid_t pid,
8706c3fb27SDimitry Andric NativeProcessProtocol::NativeDelegate &native_delegate) override {
880b57cec5SDimitry Andric llvm_unreachable("Not implemented");
890b57cec5SDimitry Andric }
900b57cec5SDimitry Andric };
910b57cec5SDimitry Andric #endif
920b57cec5SDimitry Andric }
930b57cec5SDimitry Andric
949dba64beSDimitry Andric #ifndef _WIN32
950b57cec5SDimitry Andric // Watch for signals
960b57cec5SDimitry Andric static int g_sighup_received_count = 0;
970b57cec5SDimitry Andric
sighup_handler(MainLoopBase & mainloop)980b57cec5SDimitry Andric static void sighup_handler(MainLoopBase &mainloop) {
990b57cec5SDimitry Andric ++g_sighup_received_count;
1000b57cec5SDimitry Andric
10181ad6265SDimitry Andric Log *log = GetLog(LLDBLog::Process);
1029dba64beSDimitry Andric LLDB_LOGF(log, "lldb-server:%s swallowing SIGHUP (receive count=%d)",
1030b57cec5SDimitry Andric __FUNCTION__, g_sighup_received_count);
1040b57cec5SDimitry Andric
1050b57cec5SDimitry Andric if (g_sighup_received_count >= 2)
1060b57cec5SDimitry Andric mainloop.RequestTermination();
1070b57cec5SDimitry Andric }
1080b57cec5SDimitry Andric #endif // #ifndef _WIN32
1090b57cec5SDimitry Andric
handle_attach_to_pid(GDBRemoteCommunicationServerLLGS & gdb_server,lldb::pid_t pid)1100b57cec5SDimitry Andric void handle_attach_to_pid(GDBRemoteCommunicationServerLLGS &gdb_server,
1110b57cec5SDimitry Andric lldb::pid_t pid) {
1120b57cec5SDimitry Andric Status error = gdb_server.AttachToProcess(pid);
1130b57cec5SDimitry Andric if (error.Fail()) {
1140b57cec5SDimitry Andric fprintf(stderr, "error: failed to attach to pid %" PRIu64 ": %s\n", pid,
1150b57cec5SDimitry Andric error.AsCString());
1160b57cec5SDimitry Andric exit(1);
1170b57cec5SDimitry Andric }
1180b57cec5SDimitry Andric }
1190b57cec5SDimitry Andric
handle_attach_to_process_name(GDBRemoteCommunicationServerLLGS & gdb_server,const std::string & process_name)1200b57cec5SDimitry Andric void handle_attach_to_process_name(GDBRemoteCommunicationServerLLGS &gdb_server,
1210b57cec5SDimitry Andric const std::string &process_name) {
1220b57cec5SDimitry Andric // FIXME implement.
1230b57cec5SDimitry Andric }
1240b57cec5SDimitry Andric
handle_attach(GDBRemoteCommunicationServerLLGS & gdb_server,const std::string & attach_target)1250b57cec5SDimitry Andric void handle_attach(GDBRemoteCommunicationServerLLGS &gdb_server,
1260b57cec5SDimitry Andric const std::string &attach_target) {
1270b57cec5SDimitry Andric assert(!attach_target.empty() && "attach_target cannot be empty");
1280b57cec5SDimitry Andric
1290b57cec5SDimitry Andric // First check if the attach_target is convertible to a long. If so, we'll use
1300b57cec5SDimitry Andric // it as a pid.
1310b57cec5SDimitry Andric char *end_p = nullptr;
1320b57cec5SDimitry Andric const long int pid = strtol(attach_target.c_str(), &end_p, 10);
1330b57cec5SDimitry Andric
1340b57cec5SDimitry Andric // We'll call it a match if the entire argument is consumed.
1350b57cec5SDimitry Andric if (end_p &&
1360b57cec5SDimitry Andric static_cast<size_t>(end_p - attach_target.c_str()) ==
1370b57cec5SDimitry Andric attach_target.size())
1380b57cec5SDimitry Andric handle_attach_to_pid(gdb_server, static_cast<lldb::pid_t>(pid));
1390b57cec5SDimitry Andric else
1400b57cec5SDimitry Andric handle_attach_to_process_name(gdb_server, attach_target);
1410b57cec5SDimitry Andric }
1420b57cec5SDimitry Andric
handle_launch(GDBRemoteCommunicationServerLLGS & gdb_server,llvm::ArrayRef<llvm::StringRef> Arguments)143e8d8bef9SDimitry Andric void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server,
144e8d8bef9SDimitry Andric llvm::ArrayRef<llvm::StringRef> Arguments) {
1450b57cec5SDimitry Andric ProcessLaunchInfo info;
1460b57cec5SDimitry Andric info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug |
1470b57cec5SDimitry Andric eLaunchFlagDisableASLR);
148e8d8bef9SDimitry Andric info.SetArguments(Args(Arguments), true);
1490b57cec5SDimitry Andric
1500b57cec5SDimitry Andric llvm::SmallString<64> cwd;
1510b57cec5SDimitry Andric if (std::error_code ec = llvm::sys::fs::current_path(cwd)) {
1520b57cec5SDimitry Andric llvm::errs() << "Error getting current directory: " << ec.message() << "\n";
1530b57cec5SDimitry Andric exit(1);
1540b57cec5SDimitry Andric }
1550b57cec5SDimitry Andric FileSpec cwd_spec(cwd);
1560b57cec5SDimitry Andric FileSystem::Instance().Resolve(cwd_spec);
1570b57cec5SDimitry Andric info.SetWorkingDirectory(cwd_spec);
1580b57cec5SDimitry Andric info.GetEnvironment() = Host::GetEnvironment();
1590b57cec5SDimitry Andric
1600b57cec5SDimitry Andric gdb_server.SetLaunchInfo(info);
1610b57cec5SDimitry Andric
1620b57cec5SDimitry Andric Status error = gdb_server.LaunchProcess();
1630b57cec5SDimitry Andric if (error.Fail()) {
1640b57cec5SDimitry Andric llvm::errs() << llvm::formatv("error: failed to launch '{0}': {1}\n",
165e8d8bef9SDimitry Andric Arguments[0], error);
1660b57cec5SDimitry Andric exit(1);
1670b57cec5SDimitry Andric }
1680b57cec5SDimitry Andric }
1690b57cec5SDimitry Andric
writeSocketIdToPipe(Pipe & port_pipe,llvm::StringRef socket_id)170349cc55cSDimitry Andric Status writeSocketIdToPipe(Pipe &port_pipe, llvm::StringRef socket_id) {
1710b57cec5SDimitry Andric size_t bytes_written = 0;
1720b57cec5SDimitry Andric // Write the port number as a C string with the NULL terminator.
173349cc55cSDimitry Andric return port_pipe.Write(socket_id.data(), socket_id.size() + 1, bytes_written);
1740b57cec5SDimitry Andric }
1750b57cec5SDimitry Andric
writeSocketIdToPipe(const char * const named_pipe_path,llvm::StringRef socket_id)1760b57cec5SDimitry Andric Status writeSocketIdToPipe(const char *const named_pipe_path,
177349cc55cSDimitry Andric llvm::StringRef socket_id) {
1780b57cec5SDimitry Andric Pipe port_name_pipe;
1790b57cec5SDimitry Andric // Wait for 10 seconds for pipe to be opened.
1800b57cec5SDimitry Andric auto error = port_name_pipe.OpenAsWriterWithTimeout(named_pipe_path, false,
1810b57cec5SDimitry Andric std::chrono::seconds{10});
1820b57cec5SDimitry Andric if (error.Fail())
1830b57cec5SDimitry Andric return error;
1840b57cec5SDimitry Andric return writeSocketIdToPipe(port_name_pipe, socket_id);
1850b57cec5SDimitry Andric }
1860b57cec5SDimitry Andric
writeSocketIdToPipe(lldb::pipe_t unnamed_pipe,llvm::StringRef socket_id)1870b57cec5SDimitry Andric Status writeSocketIdToPipe(lldb::pipe_t unnamed_pipe,
188349cc55cSDimitry Andric llvm::StringRef socket_id) {
1890b57cec5SDimitry Andric Pipe port_pipe{LLDB_INVALID_PIPE, unnamed_pipe};
1900b57cec5SDimitry Andric return writeSocketIdToPipe(port_pipe, socket_id);
1910b57cec5SDimitry Andric }
1920b57cec5SDimitry Andric
ConnectToRemote(MainLoop & mainloop,GDBRemoteCommunicationServerLLGS & gdb_server,bool reverse_connect,llvm::StringRef host_and_port,const char * const progname,const char * const subcommand,const char * const named_pipe_path,pipe_t unnamed_pipe,int connection_fd)1930b57cec5SDimitry Andric void ConnectToRemote(MainLoop &mainloop,
1940b57cec5SDimitry Andric GDBRemoteCommunicationServerLLGS &gdb_server,
195e8d8bef9SDimitry Andric bool reverse_connect, llvm::StringRef host_and_port,
1960b57cec5SDimitry Andric const char *const progname, const char *const subcommand,
1970b57cec5SDimitry Andric const char *const named_pipe_path, pipe_t unnamed_pipe,
1980b57cec5SDimitry Andric int connection_fd) {
1990b57cec5SDimitry Andric Status error;
2000b57cec5SDimitry Andric
2010b57cec5SDimitry Andric std::unique_ptr<Connection> connection_up;
202349cc55cSDimitry Andric std::string url;
203349cc55cSDimitry Andric
2040b57cec5SDimitry Andric if (connection_fd != -1) {
205349cc55cSDimitry Andric url = llvm::formatv("fd://{0}", connection_fd).str();
2060b57cec5SDimitry Andric
2070b57cec5SDimitry Andric // Create the connection.
208480093f4SDimitry Andric #if LLDB_ENABLE_POSIX && !defined _WIN32
2090b57cec5SDimitry Andric ::fcntl(connection_fd, F_SETFD, FD_CLOEXEC);
2100b57cec5SDimitry Andric #endif
211e8d8bef9SDimitry Andric } else if (!host_and_port.empty()) {
212349cc55cSDimitry Andric llvm::Expected<std::string> url_exp =
213349cc55cSDimitry Andric LLGSArgToURL(host_and_port, reverse_connect);
214349cc55cSDimitry Andric if (!url_exp) {
215349cc55cSDimitry Andric llvm::errs() << llvm::formatv("error: invalid host:port or URL '{0}': "
216349cc55cSDimitry Andric "{1}\n",
217349cc55cSDimitry Andric host_and_port,
218349cc55cSDimitry Andric llvm::toString(url_exp.takeError()));
2190b57cec5SDimitry Andric exit(-1);
2200b57cec5SDimitry Andric }
221349cc55cSDimitry Andric
222349cc55cSDimitry Andric url = std::move(url_exp.get());
2230b57cec5SDimitry Andric }
224349cc55cSDimitry Andric
225349cc55cSDimitry Andric if (!url.empty()) {
226349cc55cSDimitry Andric // Create the connection or server.
227349cc55cSDimitry Andric std::unique_ptr<ConnectionFileDescriptor> conn_fd_up{
228349cc55cSDimitry Andric new ConnectionFileDescriptor};
229349cc55cSDimitry Andric auto connection_result = conn_fd_up->Connect(
230349cc55cSDimitry Andric url,
231349cc55cSDimitry Andric [named_pipe_path, unnamed_pipe](llvm::StringRef socket_id) {
232349cc55cSDimitry Andric // If we have a named pipe to write the socket id back to, do that
233349cc55cSDimitry Andric // now.
2340b57cec5SDimitry Andric if (named_pipe_path && named_pipe_path[0]) {
235349cc55cSDimitry Andric Status error = writeSocketIdToPipe(named_pipe_path, socket_id);
2360b57cec5SDimitry Andric if (error.Fail())
237349cc55cSDimitry Andric llvm::errs() << llvm::formatv(
238*0fca6ea1SDimitry Andric "failed to write to the named pipe '{0}': {1}\n",
2390b57cec5SDimitry Andric named_pipe_path, error.AsCString());
2400b57cec5SDimitry Andric }
241349cc55cSDimitry Andric // If we have an unnamed pipe to write the socket id back to, do
242349cc55cSDimitry Andric // that now.
2430b57cec5SDimitry Andric else if (unnamed_pipe != LLDB_INVALID_PIPE) {
244349cc55cSDimitry Andric Status error = writeSocketIdToPipe(unnamed_pipe, socket_id);
2450b57cec5SDimitry Andric if (error.Fail())
246349cc55cSDimitry Andric llvm::errs() << llvm::formatv(
247349cc55cSDimitry Andric "failed to write to the unnamed pipe: {0}\n", error);
2480b57cec5SDimitry Andric }
249349cc55cSDimitry Andric },
250349cc55cSDimitry Andric &error);
2510b57cec5SDimitry Andric
2520b57cec5SDimitry Andric if (error.Fail()) {
253349cc55cSDimitry Andric llvm::errs() << llvm::formatv(
254349cc55cSDimitry Andric "error: failed to connect to client at '{0}': {1}\n", url, error);
255349cc55cSDimitry Andric exit(-1);
2560b57cec5SDimitry Andric }
257349cc55cSDimitry Andric if (connection_result != eConnectionStatusSuccess) {
258349cc55cSDimitry Andric llvm::errs() << llvm::formatv(
259349cc55cSDimitry Andric "error: failed to connect to client at '{0}' "
260349cc55cSDimitry Andric "(connection status: {1})\n",
261349cc55cSDimitry Andric url, static_cast<int>(connection_result));
262349cc55cSDimitry Andric exit(-1);
2630b57cec5SDimitry Andric }
264349cc55cSDimitry Andric connection_up = std::move(conn_fd_up);
2650b57cec5SDimitry Andric }
2660b57cec5SDimitry Andric error = gdb_server.InitializeConnection(std::move(connection_up));
2670b57cec5SDimitry Andric if (error.Fail()) {
268349cc55cSDimitry Andric llvm::errs() << llvm::formatv("failed to initialize connection\n", error);
2690b57cec5SDimitry Andric exit(-1);
2700b57cec5SDimitry Andric }
271349cc55cSDimitry Andric llvm::outs() << "Connection established.\n";
2720b57cec5SDimitry Andric }
2730b57cec5SDimitry Andric
274e8d8bef9SDimitry Andric namespace {
2755f757f3fSDimitry Andric using namespace llvm::opt;
2765f757f3fSDimitry Andric
277e8d8bef9SDimitry Andric enum ID {
278e8d8bef9SDimitry Andric OPT_INVALID = 0, // This is not an option ID.
2795f757f3fSDimitry Andric #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
280e8d8bef9SDimitry Andric #include "LLGSOptions.inc"
281e8d8bef9SDimitry Andric #undef OPTION
282e8d8bef9SDimitry Andric };
283e8d8bef9SDimitry Andric
284bdd1243dSDimitry Andric #define PREFIX(NAME, VALUE) \
285bdd1243dSDimitry Andric constexpr llvm::StringLiteral NAME##_init[] = VALUE; \
286bdd1243dSDimitry Andric constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \
287bdd1243dSDimitry Andric NAME##_init, std::size(NAME##_init) - 1);
288e8d8bef9SDimitry Andric #include "LLGSOptions.inc"
289e8d8bef9SDimitry Andric #undef PREFIX
290e8d8bef9SDimitry Andric
291bdd1243dSDimitry Andric static constexpr opt::OptTable::Info InfoTable[] = {
2925f757f3fSDimitry Andric #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
293e8d8bef9SDimitry Andric #include "LLGSOptions.inc"
294e8d8bef9SDimitry Andric #undef OPTION
295e8d8bef9SDimitry Andric };
296e8d8bef9SDimitry Andric
297bdd1243dSDimitry Andric class LLGSOptTable : public opt::GenericOptTable {
298e8d8bef9SDimitry Andric public:
LLGSOptTable()299bdd1243dSDimitry Andric LLGSOptTable() : opt::GenericOptTable(InfoTable) {}
300e8d8bef9SDimitry Andric
PrintHelp(llvm::StringRef Name)301e8d8bef9SDimitry Andric void PrintHelp(llvm::StringRef Name) {
302e8d8bef9SDimitry Andric std::string Usage =
303e8d8bef9SDimitry Andric (Name + " [options] [[host]:port] [[--] program args...]").str();
304fe6060f1SDimitry Andric OptTable::printHelp(llvm::outs(), Usage.c_str(), "lldb-server");
305e8d8bef9SDimitry Andric llvm::outs() << R"(
306e8d8bef9SDimitry Andric DESCRIPTION
307e8d8bef9SDimitry Andric lldb-server connects to the LLDB client, which drives the debugging session.
308e8d8bef9SDimitry Andric If no connection options are given, the [host]:port argument must be present
309e8d8bef9SDimitry Andric and will denote the address that lldb-server will listen on. [host] defaults
310e8d8bef9SDimitry Andric to "localhost" if empty. Port can be zero, in which case the port number will
311e8d8bef9SDimitry Andric be chosen dynamically and written to destinations given by --named-pipe and
312e8d8bef9SDimitry Andric --pipe arguments.
313e8d8bef9SDimitry Andric
314e8d8bef9SDimitry Andric If no target is selected at startup, lldb-server can be directed by the LLDB
315e8d8bef9SDimitry Andric client to launch or attach to a process.
316e8d8bef9SDimitry Andric
317e8d8bef9SDimitry Andric )";
318e8d8bef9SDimitry Andric }
319e8d8bef9SDimitry Andric };
320e8d8bef9SDimitry Andric } // namespace
321e8d8bef9SDimitry Andric
main_gdbserver(int argc,char * argv[])3220b57cec5SDimitry Andric int main_gdbserver(int argc, char *argv[]) {
3230b57cec5SDimitry Andric Status error;
3240b57cec5SDimitry Andric MainLoop mainloop;
3250b57cec5SDimitry Andric #ifndef _WIN32
3260b57cec5SDimitry Andric // Setup signal handlers first thing.
3270b57cec5SDimitry Andric signal(SIGPIPE, SIG_IGN);
3280b57cec5SDimitry Andric MainLoop::SignalHandleUP sighup_handle =
3290b57cec5SDimitry Andric mainloop.RegisterSignal(SIGHUP, sighup_handler, error);
3300b57cec5SDimitry Andric #endif
3310b57cec5SDimitry Andric
3320b57cec5SDimitry Andric const char *progname = argv[0];
3330b57cec5SDimitry Andric const char *subcommand = argv[1];
3340b57cec5SDimitry Andric std::string attach_target;
3350b57cec5SDimitry Andric std::string named_pipe_path;
3360b57cec5SDimitry Andric std::string log_file;
3370b57cec5SDimitry Andric StringRef
3380b57cec5SDimitry Andric log_channels; // e.g. "lldb process threads:gdb-remote default:linux all"
3390b57cec5SDimitry Andric lldb::pipe_t unnamed_pipe = LLDB_INVALID_PIPE;
3400b57cec5SDimitry Andric bool reverse_connect = false;
3410b57cec5SDimitry Andric int connection_fd = -1;
3420b57cec5SDimitry Andric
3430b57cec5SDimitry Andric // ProcessLaunchInfo launch_info;
3440b57cec5SDimitry Andric ProcessAttachInfo attach_info;
3450b57cec5SDimitry Andric
346e8d8bef9SDimitry Andric LLGSOptTable Opts;
347e8d8bef9SDimitry Andric llvm::BumpPtrAllocator Alloc;
348e8d8bef9SDimitry Andric llvm::StringSaver Saver(Alloc);
349e8d8bef9SDimitry Andric bool HasError = false;
350e8d8bef9SDimitry Andric opt::InputArgList Args = Opts.parseArgs(argc - 1, argv + 1, OPT_UNKNOWN,
351e8d8bef9SDimitry Andric Saver, [&](llvm::StringRef Msg) {
352e8d8bef9SDimitry Andric WithColor::error() << Msg << "\n";
353e8d8bef9SDimitry Andric HasError = true;
354e8d8bef9SDimitry Andric });
355e8d8bef9SDimitry Andric std::string Name =
356e8d8bef9SDimitry Andric (llvm::sys::path::filename(argv[0]) + " g[dbserver]").str();
357e8d8bef9SDimitry Andric std::string HelpText =
358e8d8bef9SDimitry Andric "Use '" + Name + " --help' for a complete list of options.\n";
359e8d8bef9SDimitry Andric if (HasError) {
360e8d8bef9SDimitry Andric llvm::errs() << HelpText;
361e8d8bef9SDimitry Andric return 1;
362e8d8bef9SDimitry Andric }
3630b57cec5SDimitry Andric
364e8d8bef9SDimitry Andric if (Args.hasArg(OPT_help)) {
365e8d8bef9SDimitry Andric Opts.PrintHelp(Name);
366e8d8bef9SDimitry Andric return 0;
367e8d8bef9SDimitry Andric }
3680b57cec5SDimitry Andric
3690b57cec5SDimitry Andric #ifndef _WIN32
370e8d8bef9SDimitry Andric if (Args.hasArg(OPT_setsid)) {
3710b57cec5SDimitry Andric // Put llgs into a new session. Terminals group processes
3720b57cec5SDimitry Andric // into sessions and when a special terminal key sequences
3730b57cec5SDimitry Andric // (like control+c) are typed they can cause signals to go out to
3740b57cec5SDimitry Andric // all processes in a session. Using this --setsid (-S) option
3750b57cec5SDimitry Andric // will cause debugserver to run in its own sessions and be free
3760b57cec5SDimitry Andric // from such issues.
3770b57cec5SDimitry Andric //
3780b57cec5SDimitry Andric // This is useful when llgs is spawned from a command
3790b57cec5SDimitry Andric // line application that uses llgs to do the debugging,
3800b57cec5SDimitry Andric // yet that application doesn't want llgs receiving the
3810b57cec5SDimitry Andric // signals sent to the session (i.e. dying when anyone hits ^C).
3820b57cec5SDimitry Andric {
3830b57cec5SDimitry Andric const ::pid_t new_sid = setsid();
3840b57cec5SDimitry Andric if (new_sid == -1) {
385e8d8bef9SDimitry Andric WithColor::warning()
386e8d8bef9SDimitry Andric << llvm::formatv("failed to set new session id for {0} ({1})\n",
387e8d8bef9SDimitry Andric LLGS_PROGRAM_NAME, llvm::sys::StrError());
3880b57cec5SDimitry Andric }
3890b57cec5SDimitry Andric }
390e8d8bef9SDimitry Andric }
3910b57cec5SDimitry Andric #endif
3920b57cec5SDimitry Andric
393e8d8bef9SDimitry Andric log_file = Args.getLastArgValue(OPT_log_file).str();
394e8d8bef9SDimitry Andric log_channels = Args.getLastArgValue(OPT_log_channels);
395e8d8bef9SDimitry Andric named_pipe_path = Args.getLastArgValue(OPT_named_pipe).str();
396e8d8bef9SDimitry Andric reverse_connect = Args.hasArg(OPT_reverse_connect);
397e8d8bef9SDimitry Andric attach_target = Args.getLastArgValue(OPT_attach).str();
398e8d8bef9SDimitry Andric if (Args.hasArg(OPT_pipe)) {
399e8d8bef9SDimitry Andric uint64_t Arg;
400e8d8bef9SDimitry Andric if (!llvm::to_integer(Args.getLastArgValue(OPT_pipe), Arg)) {
401e8d8bef9SDimitry Andric WithColor::error() << "invalid '--pipe' argument\n" << HelpText;
402e8d8bef9SDimitry Andric return 1;
4030b57cec5SDimitry Andric }
404e8d8bef9SDimitry Andric unnamed_pipe = (pipe_t)Arg;
4050b57cec5SDimitry Andric }
406e8d8bef9SDimitry Andric if (Args.hasArg(OPT_fd)) {
407e8d8bef9SDimitry Andric if (!llvm::to_integer(Args.getLastArgValue(OPT_fd), connection_fd)) {
408e8d8bef9SDimitry Andric WithColor::error() << "invalid '--fd' argument\n" << HelpText;
409e8d8bef9SDimitry Andric return 1;
410e8d8bef9SDimitry Andric }
4110b57cec5SDimitry Andric }
4120b57cec5SDimitry Andric
4130b57cec5SDimitry Andric if (!LLDBServerUtilities::SetupLogging(
4140b57cec5SDimitry Andric log_file, log_channels,
4150b57cec5SDimitry Andric LLDB_LOG_OPTION_PREPEND_TIMESTAMP |
4160b57cec5SDimitry Andric LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION))
4170b57cec5SDimitry Andric return -1;
4180b57cec5SDimitry Andric
419e8d8bef9SDimitry Andric std::vector<llvm::StringRef> Inputs;
420e8d8bef9SDimitry Andric for (opt::Arg *Arg : Args.filtered(OPT_INPUT))
421e8d8bef9SDimitry Andric Inputs.push_back(Arg->getValue());
422e8d8bef9SDimitry Andric if (opt::Arg *Arg = Args.getLastArg(OPT_REM)) {
423e8d8bef9SDimitry Andric for (const char *Val : Arg->getValues())
424e8d8bef9SDimitry Andric Inputs.push_back(Val);
4250b57cec5SDimitry Andric }
426e8d8bef9SDimitry Andric if (Inputs.empty() && connection_fd == -1) {
427e8d8bef9SDimitry Andric WithColor::error() << "no connection arguments\n" << HelpText;
428e8d8bef9SDimitry Andric return 1;
4290b57cec5SDimitry Andric }
4300b57cec5SDimitry Andric
43106c3fb27SDimitry Andric NativeProcessManager manager(mainloop);
43206c3fb27SDimitry Andric GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager);
4330b57cec5SDimitry Andric
434e8d8bef9SDimitry Andric llvm::StringRef host_and_port;
435e8d8bef9SDimitry Andric if (!Inputs.empty()) {
436e8d8bef9SDimitry Andric host_and_port = Inputs.front();
437e8d8bef9SDimitry Andric Inputs.erase(Inputs.begin());
438e8d8bef9SDimitry Andric }
4390b57cec5SDimitry Andric
4400b57cec5SDimitry Andric // Any arguments left over are for the program that we need to launch. If
4410b57cec5SDimitry Andric // there
4420b57cec5SDimitry Andric // are no arguments, then the GDB server will start up and wait for an 'A'
4430b57cec5SDimitry Andric // packet
4440b57cec5SDimitry Andric // to launch a program, or a vAttach packet to attach to an existing process,
4450b57cec5SDimitry Andric // unless
4460b57cec5SDimitry Andric // explicitly asked to attach with the --attach={pid|program_name} form.
4470b57cec5SDimitry Andric if (!attach_target.empty())
4480b57cec5SDimitry Andric handle_attach(gdb_server, attach_target);
449e8d8bef9SDimitry Andric else if (!Inputs.empty())
450e8d8bef9SDimitry Andric handle_launch(gdb_server, Inputs);
4510b57cec5SDimitry Andric
4520b57cec5SDimitry Andric // Print version info.
4539dba64beSDimitry Andric printf("%s-%s\n", LLGS_PROGRAM_NAME, LLGS_VERSION_STR);
4540b57cec5SDimitry Andric
4550b57cec5SDimitry Andric ConnectToRemote(mainloop, gdb_server, reverse_connect, host_and_port,
4560b57cec5SDimitry Andric progname, subcommand, named_pipe_path.c_str(),
4570b57cec5SDimitry Andric unnamed_pipe, connection_fd);
4580b57cec5SDimitry Andric
4590b57cec5SDimitry Andric if (!gdb_server.IsConnected()) {
4600b57cec5SDimitry Andric fprintf(stderr, "no connection information provided, unable to run\n");
4610b57cec5SDimitry Andric return 1;
4620b57cec5SDimitry Andric }
4630b57cec5SDimitry Andric
4640b57cec5SDimitry Andric Status ret = mainloop.Run();
4650b57cec5SDimitry Andric if (ret.Fail()) {
4660b57cec5SDimitry Andric fprintf(stderr, "lldb-server terminating due to error: %s\n",
4670b57cec5SDimitry Andric ret.AsCString());
4680b57cec5SDimitry Andric return 1;
4690b57cec5SDimitry Andric }
4700b57cec5SDimitry Andric fprintf(stderr, "lldb-server exiting...\n");
4710b57cec5SDimitry Andric
4720b57cec5SDimitry Andric return 0;
4730b57cec5SDimitry Andric }
474