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
39using namespace lldb;
40using namespace lldb_private;
41using namespace lldb_private::lldb_server;
42using namespace lldb_private::process_gdb_remote;
43using namespace llvm;
44
45// option descriptors for getopt_long_only()
46
47static int g_debug = 0;
48static int g_verbose = 0;
49static int g_server = 0;
50
51static struct option g_long_options[] = {
52 {.name: "debug", no_argument, .flag: &g_debug, .val: 1},
53 {.name: "verbose", no_argument, .flag: &g_verbose, .val: 1},
54 {.name: "log-file", required_argument, .flag: nullptr, .val: 'l'},
55 {.name: "log-channels", required_argument, .flag: nullptr, .val: 'c'},
56 {.name: "listen", required_argument, .flag: nullptr, .val: 'L'},
57 {.name: "port-offset", required_argument, .flag: nullptr, .val: 'p'},
58 {.name: "gdbserver-port", required_argument, .flag: nullptr, .val: 'P'},
59 {.name: "min-gdbserver-port", required_argument, .flag: nullptr, .val: 'm'},
60 {.name: "max-gdbserver-port", required_argument, .flag: nullptr, .val: 'M'},
61 {.name: "socket-file", required_argument, .flag: nullptr, .val: 'f'},
62 {.name: "server", no_argument, .flag: &g_server, .val: 1},
63 {.name: nullptr, .has_arg: 0, .flag: nullptr, .val: 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
75static 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
89static void display_usage(const char *progname, const char *subcommand) {
90 fprintf(stderr, format: "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(status: 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().GetStringRef());
100 Status error(llvm::sys::fs::create_directory(path: 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(OutputFileName: file_spec.GetPath(),
107 Write: [&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(E: std::move(Err)).c_str());
114 return status;
115}
116
117// main
118int 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, handler: 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(long_options: 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: argc, argv: argv, shortopts: short_options.c_str(),
156 longopts: g_long_options, longind: &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(s: optarg);
163 break;
164
165 case 'l': // Set Log File
166 if (optarg && optarg[0])
167 log_file.assign(s: 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(path: optarg, style: FileSpec::Style::native);
178 break;
179
180 case 'p': {
181 if (!llvm::to_integer(S: optarg, Num&: 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 Fmt: "port offset {0} is not in the "
189 "valid user port range of {1} - {2}\n",
190 Vals&: 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(S: optarg, Num&: 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 Fmt: "port number {0} is not in the "
207 "valid user port range of {1} - {2}\n",
208 Vals&: portnum, LOW_PORT, HIGH_PORT);
209 option_error = 1;
210 break;
211 }
212 if (ch == 'P')
213 gdbserver_portmap.AllowPort(port: 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, log_options: 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 Fmt: "--min-gdbserver-port ({0}) is not lower than "
237 "--max-gdbserver-port ({1})\n",
238 Vals&: min_gdbserver_port, Vals&: 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(status: 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, argv: 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 name: listen_host_port, child_processes_inherit: children_inherit_listen_socket, error));
265 if (error.Fail()) {
266 fprintf(stderr, format: "failed to create acceptor: %s", error.AsCString());
267 exit(status: socket_error);
268 }
269
270 error = acceptor_up->Listen(backlog);
271 if (error.Fail()) {
272 printf(format: "failed to listen: %s\n", error.AsCString());
273 exit(status: socket_error);
274 }
275 if (socket_file) {
276 error =
277 save_socket_id_to_file(socket_id: acceptor_up->GetLocalSocketId(), file_spec: socket_file);
278 if (error.Fail()) {
279 fprintf(stderr, format: "failed to write socket id to %s: %s\n",
280 socket_file.GetPath().c_str(), error.AsCString());
281 return 1;
282 }
283 }
284
285 do {
286 GDBRemoteCommunicationServerPlatform platform(
287 acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme());
288
289 if (port_offset > 0)
290 platform.SetPortOffset(port_offset);
291
292 if (!gdbserver_portmap.empty()) {
293 platform.SetPortMap(std::move(gdbserver_portmap));
294 }
295
296 const bool children_inherit_accept_socket = true;
297 Connection *conn = nullptr;
298 error = acceptor_up->Accept(child_processes_inherit: children_inherit_accept_socket, conn);
299 if (error.Fail()) {
300 WithColor::error() << error.AsCString() << '\n';
301 exit(status: socket_error);
302 }
303 printf(format: "Connection established.\n");
304 if (g_server) {
305 // Collect child zombie processes.
306#if !defined(_WIN32)
307 while (waitpid(pid: -1, stat_loc: nullptr, WNOHANG) > 0)
308 ;
309#endif
310 if (fork()) {
311 // Parent doesn't need a connection to the lldb client
312 delete conn;
313
314 // Parent will continue to listen for new connections.
315 continue;
316 } else {
317 // Child process will handle the connection and exit.
318 g_server = 0;
319 // Listening socket is owned by parent process.
320 acceptor_up.release();
321 }
322 } else {
323 // If not running as a server, this process will not accept
324 // connections while a connection is active.
325 acceptor_up.reset();
326 }
327 platform.SetConnection(std::unique_ptr<Connection>(conn));
328
329 if (platform.IsConnected()) {
330 if (inferior_arguments.GetArgumentCount() > 0) {
331 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
332 std::optional<uint16_t> port = 0;
333 std::string socket_name;
334 Status error = platform.LaunchGDBServer(args: inferior_arguments,
335 hostname: "", // hostname
336 pid, port, socket_name);
337 if (error.Success())
338 platform.SetPendingGdbServer(pid, port: *port, socket_name);
339 else
340 fprintf(stderr, format: "failed to start gdbserver: %s\n", error.AsCString());
341 }
342
343 bool interrupt = false;
344 bool done = false;
345 while (!interrupt && !done) {
346 if (platform.GetPacketAndSendResponse(timeout: std::nullopt, error, interrupt,
347 quit&: done) !=
348 GDBRemoteCommunication::PacketResult::Success)
349 break;
350 }
351
352 if (error.Fail())
353 WithColor::error() << error.AsCString() << '\n';
354 }
355 } while (g_server);
356
357 fprintf(stderr, format: "lldb-server exiting...\n");
358
359 return 0;
360}
361

source code of lldb/tools/lldb-server/lldb-platform.cpp