1 //===-- lldb-platform.cpp ---------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
10 #if defined(__APPLE__)
11 #include <netinet/in.h>
23 #include "llvm/Support/FileSystem.h"
24 #include "llvm/Support/FileUtilities.h"
25 #include "llvm/Support/WithColor.h"
26 #include "llvm/Support/raw_ostream.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"
40 using namespace lldb_private
;
41 using namespace lldb_private::lldb_server
;
42 using namespace lldb_private::process_gdb_remote
;
45 // option descriptors for getopt_long_only()
47 static int g_debug
= 0;
48 static int g_verbose
= 0;
49 static int g_server
= 0;
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}};
65 #if defined(__APPLE__)
66 #define LOW_PORT (IPPORT_RESERVED)
67 #define HIGH_PORT (IPPORT_HIFIRSTAUTO)
69 #define LOW_PORT (1024u)
70 #define HIGH_PORT (49151u)
75 static void signal_handler(int signo
) {
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
81 // to be invoked and wreaking havoc on the threads still running.
82 Host::SystemLog(Host::eSystemLogWarning
,
83 "SIGHUP received, exiting lldb-server...\n");
90 static void display_usage(const char *progname
, const char *subcommand
) {
91 fprintf(stderr
, "Usage:\n %s %s [--log-file log-file-name] [--log-channels "
92 "log-channel-list] [--port-file port-file-path] --server "
94 progname
, subcommand
);
98 static Status
save_socket_id_to_file(const std::string
&socket_id
,
99 const FileSpec
&file_spec
) {
100 FileSpec
temp_file_spec(file_spec
.GetDirectory().GetStringRef());
101 Status
error(llvm::sys::fs::create_directory(temp_file_spec
.GetPath()));
103 return Status("Failed to create directory %s: %s",
104 temp_file_spec
.GetCString(), error
.AsCString());
106 llvm::SmallString
<64> temp_file_path
;
107 temp_file_spec
.AppendPathComponent("port-file.%%%%%%");
108 temp_file_path
= temp_file_spec
.GetPath();
112 handleErrors(llvm::writeFileAtomically(
113 temp_file_path
, file_spec
.GetPath(), socket_id
),
114 [&status
, &file_spec
](const AtomicFileWriteError
&E
) {
115 std::string ErrorMsgBuffer
;
116 llvm::raw_string_ostream
S(ErrorMsgBuffer
);
120 case atomic_write_error::failed_to_create_uniq_file
:
121 status
= Status("Failed to create temp file: %s",
122 ErrorMsgBuffer
.c_str());
124 case atomic_write_error::output_stream_error
:
125 status
= Status("Failed to write to port file.");
127 case atomic_write_error::failed_to_rename_temp_file
:
128 status
= Status("Failed to rename file %s to %s: %s",
129 ErrorMsgBuffer
.c_str(),
130 file_spec
.GetPath().c_str(),
131 ErrorMsgBuffer
.c_str());
135 return Status("Failed to atomically write file %s",
136 file_spec
.GetPath().c_str());
142 int main_platform(int argc
, char *argv
[]) {
143 const char *progname
= argv
[0];
144 const char *subcommand
= argv
[1];
148 signal(SIGPIPE
, SIG_IGN
);
149 signal(SIGHUP
, signal_handler
);
151 int long_option_index
= 0;
153 std::string listen_host_port
;
156 std::string log_file
;
158 log_channels
; // e.g. "lldb process threads:gdb-remote default:linux all"
160 GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap
;
161 int min_gdbserver_port
= 0;
162 int max_gdbserver_port
= 0;
163 uint16_t port_offset
= 0;
165 FileSpec socket_file
;
166 bool show_usage
= false;
167 int option_error
= 0;
168 int socket_error
= -1;
170 std::string
short_options(OptionParser::GetShortOptionString(g_long_options
));
179 while ((ch
= getopt_long_only(argc
, argv
, short_options
.c_str(),
180 g_long_options
, &long_option_index
)) != -1) {
182 case 0: // Any optional that auto set themselves will return 0
186 listen_host_port
.append(optarg
);
189 case 'l': // Set Log File
190 if (optarg
&& optarg
[0])
191 log_file
.assign(optarg
);
194 case 'c': // Log Channels
195 if (optarg
&& optarg
[0])
196 log_channels
= StringRef(optarg
);
199 case 'f': // Socket file
200 if (optarg
&& optarg
[0])
201 socket_file
.SetFile(optarg
, FileSpec::Style::native
);
205 if (!llvm::to_integer(optarg
, port_offset
)) {
206 WithColor::error() << "invalid port offset string " << optarg
<< "\n";
210 if (port_offset
< LOW_PORT
|| port_offset
> HIGH_PORT
) {
211 WithColor::error() << llvm::formatv(
212 "port offset {0} is not in the "
213 "valid user port range of {1} - {2}\n",
214 port_offset
, LOW_PORT
, HIGH_PORT
);
223 if (!llvm::to_integer(optarg
, portnum
)) {
224 WithColor::error() << "invalid port number string " << optarg
<< "\n";
228 if (portnum
< LOW_PORT
|| portnum
> HIGH_PORT
) {
229 WithColor::error() << llvm::formatv(
230 "port number {0} is not in the "
231 "valid user port range of {1} - {2}\n",
232 portnum
, LOW_PORT
, HIGH_PORT
);
237 gdbserver_portmap
.AllowPort(portnum
);
239 min_gdbserver_port
= portnum
;
241 max_gdbserver_port
= portnum
;
244 case 'h': /* fall-through is intentional */
251 if (!LLDBServerUtilities::SetupLogging(log_file
, log_channels
, 0))
254 // Make a port map for a port range that was specified.
255 if (min_gdbserver_port
&& min_gdbserver_port
< max_gdbserver_port
) {
256 gdbserver_portmap
= GDBRemoteCommunicationServerPlatform::PortMap(
257 min_gdbserver_port
, max_gdbserver_port
);
258 } else if (min_gdbserver_port
|| max_gdbserver_port
) {
259 WithColor::error() << llvm::formatv(
260 "--min-gdbserver-port ({0}) is not lower than "
261 "--max-gdbserver-port ({1})\n",
262 min_gdbserver_port
, max_gdbserver_port
);
266 // Print usage and exit if no listening port is specified.
267 if (listen_host_port
.empty())
270 if (show_usage
|| option_error
) {
271 display_usage(progname
, subcommand
);
275 // Skip any options we consumed with getopt_long_only.
278 lldb_private::Args inferior_arguments
;
279 inferior_arguments
.SetArguments(argc
, const_cast<const char **>(argv
));
281 const bool children_inherit_listen_socket
= false;
282 // the test suite makes many connections in parallel, let's not miss any.
283 // The highest this should get reasonably is a function of the number
284 // of target CPUs. For now, let's just use 100.
285 const int backlog
= 100;
287 std::unique_ptr
<Acceptor
> acceptor_up(Acceptor::Create(
288 listen_host_port
, children_inherit_listen_socket
, error
));
290 fprintf(stderr
, "failed to create acceptor: %s", error
.AsCString());
294 error
= acceptor_up
->Listen(backlog
);
296 printf("failed to listen: %s\n", error
.AsCString());
301 save_socket_id_to_file(acceptor_up
->GetLocalSocketId(), socket_file
);
303 fprintf(stderr
, "failed to write socket id to %s: %s\n",
304 socket_file
.GetPath().c_str(), error
.AsCString());
310 GDBRemoteCommunicationServerPlatform
platform(
311 acceptor_up
->GetSocketProtocol(), acceptor_up
->GetSocketScheme());
314 platform
.SetPortOffset(port_offset
);
316 if (!gdbserver_portmap
.empty()) {
317 platform
.SetPortMap(std::move(gdbserver_portmap
));
320 const bool children_inherit_accept_socket
= true;
321 Connection
*conn
= nullptr;
322 error
= acceptor_up
->Accept(children_inherit_accept_socket
, conn
);
324 WithColor::error() << error
.AsCString() << '\n';
327 printf("Connection established.\n");
329 // Collect child zombie processes.
331 while (waitpid(-1, nullptr, WNOHANG
) > 0)
335 // Parent doesn't need a connection to the lldb client
338 // Parent will continue to listen for new connections.
341 // Child process will handle the connection and exit.
343 // Listening socket is owned by parent process.
344 acceptor_up
.release();
347 // If not running as a server, this process will not accept
348 // connections while a connection is active.
351 platform
.SetConnection(std::unique_ptr
<Connection
>(conn
));
353 if (platform
.IsConnected()) {
354 if (inferior_arguments
.GetArgumentCount() > 0) {
355 lldb::pid_t pid
= LLDB_INVALID_PROCESS_ID
;
356 llvm::Optional
<uint16_t> port
= 0;
357 std::string socket_name
;
358 Status error
= platform
.LaunchGDBServer(inferior_arguments
,
360 pid
, port
, socket_name
);
362 platform
.SetPendingGdbServer(pid
, *port
, socket_name
);
364 fprintf(stderr
, "failed to start gdbserver: %s\n", error
.AsCString());
367 bool interrupt
= false;
369 while (!interrupt
&& !done
) {
370 if (platform
.GetPacketAndSendResponse(llvm::None
, error
, interrupt
,
372 GDBRemoteCommunication::PacketResult::Success
)
377 WithColor::error() << error
.AsCString() << '\n';
381 fprintf(stderr
, "lldb-server exiting...\n");