xref: /freebsd/contrib/llvm-project/lldb/tools/lldb-server/lldb-platform.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===-- lldb-platform.cpp ---------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <cerrno>
10 #if defined(__APPLE__)
11 #include <netinet/in.h>
12 #endif
13 #include <csignal>
14 #include <cstdint>
15 #include <cstdio>
16 #include <cstdlib>
17 #include <cstring>
18 #if !defined(_WIN32)
19 #include <sys/wait.h>
20 #endif
21 #include <fstream>
22 #include <optional>
23 
24 #include "llvm/Support/FileSystem.h"
25 #include "llvm/Support/WithColor.h"
26 #include "llvm/Support/raw_ostream.h"
27 
28 #include "Acceptor.h"
29 #include "LLDBServerUtilities.h"
30 #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h"
31 #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
32 #include "lldb/Host/ConnectionFileDescriptor.h"
33 #include "lldb/Host/HostGetOpt.h"
34 #include "lldb/Host/OptionParser.h"
35 #include "lldb/Host/common/TCPSocket.h"
36 #include "lldb/Utility/FileSpec.h"
37 #include "lldb/Utility/Status.h"
38 
39 using namespace lldb;
40 using namespace lldb_private;
41 using namespace lldb_private::lldb_server;
42 using namespace lldb_private::process_gdb_remote;
43 using namespace llvm;
44 
45 // option descriptors for getopt_long_only()
46 
47 static int g_debug = 0;
48 static int g_verbose = 0;
49 static int g_server = 0;
50 
51 static struct option g_long_options[] = {
52     {"debug", no_argument, &g_debug, 1},
53     {"verbose", no_argument, &g_verbose, 1},
54     {"log-file", required_argument, nullptr, 'l'},
55     {"log-channels", required_argument, nullptr, 'c'},
56     {"listen", required_argument, nullptr, 'L'},
57     {"port-offset", required_argument, nullptr, 'p'},
58     {"gdbserver-port", required_argument, nullptr, 'P'},
59     {"min-gdbserver-port", required_argument, nullptr, 'm'},
60     {"max-gdbserver-port", required_argument, nullptr, 'M'},
61     {"socket-file", required_argument, nullptr, 'f'},
62     {"server", no_argument, &g_server, 1},
63     {nullptr, 0, nullptr, 0}};
64 
65 #if defined(__APPLE__)
66 #define LOW_PORT (IPPORT_RESERVED)
67 #define HIGH_PORT (IPPORT_HIFIRSTAUTO)
68 #else
69 #define LOW_PORT (1024u)
70 #define HIGH_PORT (49151u)
71 #endif
72 
73 #if !defined(_WIN32)
74 // Watch for signals
signal_handler(int signo)75 static void signal_handler(int signo) {
76   switch (signo) {
77   case SIGHUP:
78     // Use SIGINT first, if that does not work, use SIGHUP as a last resort.
79     // And we should not call exit() here because it results in the global
80     // destructors to be invoked and wreaking havoc on the threads still
81     // running.
82     llvm::errs() << "SIGHUP received, exiting lldb-server...\n";
83     abort();
84     break;
85   }
86 }
87 #endif
88 
display_usage(const char * progname,const char * subcommand)89 static void display_usage(const char *progname, const char *subcommand) {
90   fprintf(stderr, "Usage:\n  %s %s [--log-file log-file-name] [--log-channels "
91                   "log-channel-list] [--port-file port-file-path] --server "
92                   "--listen port\n",
93           progname, subcommand);
94   exit(0);
95 }
96 
save_socket_id_to_file(const std::string & socket_id,const FileSpec & file_spec)97 static Status save_socket_id_to_file(const std::string &socket_id,
98                                      const FileSpec &file_spec) {
99   FileSpec temp_file_spec(file_spec.GetDirectory().GetStringRef());
100   Status error(llvm::sys::fs::create_directory(temp_file_spec.GetPath()));
101   if (error.Fail())
102     return Status("Failed to create directory %s: %s",
103                   temp_file_spec.GetPath().c_str(), error.AsCString());
104 
105   Status status;
106   if (auto Err = llvm::writeToOutput(file_spec.GetPath(),
107                                      [&socket_id](llvm::raw_ostream &OS) {
108                                        OS << socket_id;
109                                        return llvm::Error::success();
110                                      }))
111     return Status("Failed to atomically write file %s: %s",
112                   file_spec.GetPath().c_str(),
113                   llvm::toString(std::move(Err)).c_str());
114   return status;
115 }
116 
117 // main
main_platform(int argc,char * argv[])118 int main_platform(int argc, char *argv[]) {
119   const char *progname = argv[0];
120   const char *subcommand = argv[1];
121   argc--;
122   argv++;
123 #if !defined(_WIN32)
124   signal(SIGPIPE, SIG_IGN);
125   signal(SIGHUP, signal_handler);
126 #endif
127   int long_option_index = 0;
128   Status error;
129   std::string listen_host_port;
130   int ch;
131 
132   std::string log_file;
133   StringRef
134       log_channels; // e.g. "lldb process threads:gdb-remote default:linux all"
135 
136   GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap;
137   int min_gdbserver_port = 0;
138   int max_gdbserver_port = 0;
139   uint16_t port_offset = 0;
140 
141   FileSpec socket_file;
142   bool show_usage = false;
143   int option_error = 0;
144   int socket_error = -1;
145 
146   std::string short_options(OptionParser::GetShortOptionString(g_long_options));
147 
148 #if __GLIBC__
149   optind = 0;
150 #else
151   optreset = 1;
152   optind = 1;
153 #endif
154 
155   while ((ch = getopt_long_only(argc, argv, short_options.c_str(),
156                                 g_long_options, &long_option_index)) != -1) {
157     switch (ch) {
158     case 0: // Any optional that auto set themselves will return 0
159       break;
160 
161     case 'L':
162       listen_host_port.append(optarg);
163       break;
164 
165     case 'l': // Set Log File
166       if (optarg && optarg[0])
167         log_file.assign(optarg);
168       break;
169 
170     case 'c': // Log Channels
171       if (optarg && optarg[0])
172         log_channels = StringRef(optarg);
173       break;
174 
175     case 'f': // Socket file
176       if (optarg && optarg[0])
177         socket_file.SetFile(optarg, FileSpec::Style::native);
178       break;
179 
180     case 'p': {
181       if (!llvm::to_integer(optarg, port_offset)) {
182         WithColor::error() << "invalid port offset string " << optarg << "\n";
183         option_error = 4;
184         break;
185       }
186       if (port_offset < LOW_PORT || port_offset > HIGH_PORT) {
187         WithColor::error() << llvm::formatv(
188             "port offset {0} is not in the "
189             "valid user port range of {1} - {2}\n",
190             port_offset, LOW_PORT, HIGH_PORT);
191         option_error = 5;
192       }
193     } break;
194 
195     case 'P':
196     case 'm':
197     case 'M': {
198       uint16_t portnum;
199       if (!llvm::to_integer(optarg, portnum)) {
200         WithColor::error() << "invalid port number string " << optarg << "\n";
201         option_error = 2;
202         break;
203       }
204       if (portnum < LOW_PORT || portnum > HIGH_PORT) {
205         WithColor::error() << llvm::formatv(
206             "port number {0} is not in the "
207             "valid user port range of {1} - {2}\n",
208             portnum, LOW_PORT, HIGH_PORT);
209         option_error = 1;
210         break;
211       }
212       if (ch == 'P')
213         gdbserver_portmap.AllowPort(portnum);
214       else if (ch == 'm')
215         min_gdbserver_port = portnum;
216       else
217         max_gdbserver_port = portnum;
218     } break;
219 
220     case 'h': /* fall-through is intentional */
221     case '?':
222       show_usage = true;
223       break;
224     }
225   }
226 
227   if (!LLDBServerUtilities::SetupLogging(log_file, log_channels, 0))
228     return -1;
229 
230   // Make a port map for a port range that was specified.
231   if (min_gdbserver_port && min_gdbserver_port < max_gdbserver_port) {
232     gdbserver_portmap = GDBRemoteCommunicationServerPlatform::PortMap(
233         min_gdbserver_port, max_gdbserver_port);
234   } else if (min_gdbserver_port || max_gdbserver_port) {
235     WithColor::error() << llvm::formatv(
236         "--min-gdbserver-port ({0}) is not lower than "
237         "--max-gdbserver-port ({1})\n",
238         min_gdbserver_port, max_gdbserver_port);
239     option_error = 3;
240   }
241 
242   // Print usage and exit if no listening port is specified.
243   if (listen_host_port.empty())
244     show_usage = true;
245 
246   if (show_usage || option_error) {
247     display_usage(progname, subcommand);
248     exit(option_error);
249   }
250 
251   // Skip any options we consumed with getopt_long_only.
252   argc -= optind;
253   argv += optind;
254   lldb_private::Args inferior_arguments;
255   inferior_arguments.SetArguments(argc, const_cast<const char **>(argv));
256 
257   const bool children_inherit_listen_socket = false;
258   // the test suite makes many connections in parallel, let's not miss any.
259   // The highest this should get reasonably is a function of the number
260   // of target CPUs. For now, let's just use 100.
261   const int backlog = 100;
262 
263   std::unique_ptr<Acceptor> acceptor_up(Acceptor::Create(
264       listen_host_port, children_inherit_listen_socket, error));
265   if (error.Fail()) {
266     fprintf(stderr, "failed to create acceptor: %s", error.AsCString());
267     exit(socket_error);
268   }
269 
270   error = acceptor_up->Listen(backlog);
271   if (error.Fail()) {
272     printf("failed to listen: %s\n", error.AsCString());
273     exit(socket_error);
274   }
275   if (socket_file) {
276     error =
277         save_socket_id_to_file(acceptor_up->GetLocalSocketId(), socket_file);
278     if (error.Fail()) {
279       fprintf(stderr, "failed to write socket id to %s: %s\n",
280               socket_file.GetPath().c_str(), error.AsCString());
281       return 1;
282     }
283   }
284 
285   GDBRemoteCommunicationServerPlatform platform(
286       acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme());
287   if (port_offset > 0)
288     platform.SetPortOffset(port_offset);
289 
290   do {
291     const bool children_inherit_accept_socket = true;
292     Connection *conn = nullptr;
293     error = acceptor_up->Accept(children_inherit_accept_socket, conn);
294     if (error.Fail()) {
295       WithColor::error() << error.AsCString() << '\n';
296       exit(socket_error);
297     }
298     printf("Connection established.\n");
299 
300     if (g_server) {
301       // Collect child zombie processes.
302 #if !defined(_WIN32)
303       ::pid_t waitResult;
304       while ((waitResult = waitpid(-1, nullptr, WNOHANG)) > 0) {
305         // waitResult is the child pid
306         gdbserver_portmap.FreePortForProcess(waitResult);
307       }
308 #endif
309       // TODO: Clean up portmap for Windows when children die
310       // See https://github.com/llvm/llvm-project/issues/90923
311 
312       // After collecting zombie ports, get the next available
313       GDBRemoteCommunicationServerPlatform::PortMap portmap_for_child;
314       llvm::Expected<uint16_t> available_port =
315           gdbserver_portmap.GetNextAvailablePort();
316       if (available_port) {
317         // GetNextAvailablePort() may return 0 if gdbserver_portmap is empty.
318         if (*available_port)
319           portmap_for_child.AllowPort(*available_port);
320       } else {
321         llvm::consumeError(available_port.takeError());
322         fprintf(stderr,
323                 "no available gdbserver port for connection - dropping...\n");
324         delete conn;
325         continue;
326       }
327       platform.SetPortMap(std::move(portmap_for_child));
328 
329       auto childPid = fork();
330       if (childPid) {
331         gdbserver_portmap.AssociatePortWithProcess(*available_port, childPid);
332         // Parent doesn't need a connection to the lldb client
333         delete conn;
334 
335         // Parent will continue to listen for new connections.
336         continue;
337       } else {
338         // Child process will handle the connection and exit.
339         g_server = 0;
340         // Listening socket is owned by parent process.
341         acceptor_up.release();
342       }
343     } else {
344       // If not running as a server, this process will not accept
345       // connections while a connection is active.
346       acceptor_up.reset();
347 
348       // When not running in server mode, use all available ports
349       platform.SetPortMap(std::move(gdbserver_portmap));
350     }
351 
352     platform.SetConnection(std::unique_ptr<Connection>(conn));
353 
354     if (platform.IsConnected()) {
355       if (inferior_arguments.GetArgumentCount() > 0) {
356         lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
357         std::optional<uint16_t> port;
358         std::string socket_name;
359         Status error = platform.LaunchGDBServer(inferior_arguments,
360                                                 "", // hostname
361                                                 pid, port, socket_name);
362         if (error.Success())
363           platform.SetPendingGdbServer(pid, *port, socket_name);
364         else
365           fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString());
366       }
367 
368       bool interrupt = false;
369       bool done = false;
370       while (!interrupt && !done) {
371         if (platform.GetPacketAndSendResponse(std::nullopt, error, interrupt,
372                                               done) !=
373             GDBRemoteCommunication::PacketResult::Success)
374           break;
375       }
376 
377       if (error.Fail())
378         WithColor::error() << error.AsCString() << '\n';
379     }
380   } while (g_server);
381 
382   fprintf(stderr, "lldb-server exiting...\n");
383 
384   return 0;
385 }
386