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