llvm-project

Форк
0
/
lldb-platform.cpp 
383 строки · 12.0 Кб
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
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

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

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
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
        portmap_for_child.AllowPort(*available_port);
318
      else {
319
        llvm::consumeError(available_port.takeError());
320
        fprintf(stderr,
321
                "no available gdbserver port for connection - dropping...\n");
322
        delete conn;
323
        continue;
324
      }
325
      platform.SetPortMap(std::move(portmap_for_child));
326

327
      auto childPid = fork();
328
      if (childPid) {
329
        gdbserver_portmap.AssociatePortWithProcess(*available_port, childPid);
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
      // When not running in server mode, use all available ports
347
      platform.SetPortMap(std::move(gdbserver_portmap));
348
    }
349

350
    platform.SetConnection(std::unique_ptr<Connection>(conn));
351

352
    if (platform.IsConnected()) {
353
      if (inferior_arguments.GetArgumentCount() > 0) {
354
        lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
355
        std::optional<uint16_t> port = 0;
356
        std::string socket_name;
357
        Status error = platform.LaunchGDBServer(inferior_arguments,
358
                                                "", // hostname
359
                                                pid, port, socket_name);
360
        if (error.Success())
361
          platform.SetPendingGdbServer(pid, *port, socket_name);
362
        else
363
          fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString());
364
      }
365

366
      bool interrupt = false;
367
      bool done = false;
368
      while (!interrupt && !done) {
369
        if (platform.GetPacketAndSendResponse(std::nullopt, error, interrupt,
370
                                              done) !=
371
            GDBRemoteCommunication::PacketResult::Success)
372
          break;
373
      }
374

375
      if (error.Fail())
376
        WithColor::error() << error.AsCString() << '\n';
377
    }
378
  } while (g_server);
379

380
  fprintf(stderr, "lldb-server exiting...\n");
381

382
  return 0;
383
}
384

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.