/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/tools/lldb-server/lldb-platform.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
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" |
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 | 4 | #define LOW_PORT (IPPORT_RESERVED) |
67 | 2 | #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 | 0 | static void signal_handler(int signo) { |
76 | 0 | switch (signo) { |
77 | 0 | 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 | 0 | llvm::errs() << "SIGHUP received, exiting lldb-server...\n"; |
83 | 0 | abort(); |
84 | 0 | break; |
85 | 0 | } |
86 | 0 | } |
87 | | #endif |
88 | | |
89 | 1 | static void display_usage(const char *progname, const char *subcommand) { |
90 | 1 | fprintf(stderr, "Usage:\n %s %s [--log-file log-file-name] [--log-channels " |
91 | 1 | "log-channel-list] [--port-file port-file-path] --server " |
92 | 1 | "--listen port\n", |
93 | 1 | progname, subcommand); |
94 | 1 | exit(0); |
95 | 1 | } |
96 | | |
97 | | static Status save_socket_id_to_file(const std::string &socket_id, |
98 | 0 | const FileSpec &file_spec) { |
99 | 0 | FileSpec temp_file_spec(file_spec.GetDirectory().GetStringRef()); |
100 | 0 | Status error(llvm::sys::fs::create_directory(temp_file_spec.GetPath())); |
101 | 0 | if (error.Fail()) |
102 | 0 | return Status("Failed to create directory %s: %s", |
103 | 0 | temp_file_spec.GetCString(), error.AsCString()); |
104 | | |
105 | 0 | llvm::SmallString<64> temp_file_path; |
106 | 0 | temp_file_spec.AppendPathComponent("port-file.%%%%%%"); |
107 | 0 | temp_file_path = temp_file_spec.GetPath(); |
108 | |
|
109 | 0 | Status status; |
110 | 0 | if (auto Err = |
111 | 0 | handleErrors(llvm::writeFileAtomically( |
112 | 0 | temp_file_path, file_spec.GetPath(), socket_id), |
113 | 0 | [&status, &file_spec](const AtomicFileWriteError &E) { |
114 | 0 | std::string ErrorMsgBuffer; |
115 | 0 | llvm::raw_string_ostream S(ErrorMsgBuffer); |
116 | 0 | E.log(S); |
117 | |
|
118 | 0 | switch (E.Error) { |
119 | 0 | case atomic_write_error::failed_to_create_uniq_file: |
120 | 0 | status = Status("Failed to create temp file: %s", |
121 | 0 | ErrorMsgBuffer.c_str()); |
122 | 0 | break; |
123 | 0 | case atomic_write_error::output_stream_error: |
124 | 0 | status = Status("Failed to write to port file."); |
125 | 0 | break; |
126 | 0 | case atomic_write_error::failed_to_rename_temp_file: |
127 | 0 | status = Status("Failed to rename file %s to %s: %s", |
128 | 0 | ErrorMsgBuffer.c_str(), |
129 | 0 | file_spec.GetPath().c_str(), |
130 | 0 | ErrorMsgBuffer.c_str()); |
131 | 0 | break; |
132 | 0 | } |
133 | 0 | })) { |
134 | 0 | return Status("Failed to atomically write file %s", |
135 | 0 | file_spec.GetPath().c_str()); |
136 | 0 | } |
137 | 0 | return status; |
138 | 0 | } |
139 | | |
140 | | // main |
141 | 1 | int main_platform(int argc, char *argv[]) { |
142 | 1 | const char *progname = argv[0]; |
143 | 1 | const char *subcommand = argv[1]; |
144 | 1 | argc--; |
145 | 1 | argv++; |
146 | 1 | #if !defined(_WIN32) |
147 | 1 | signal(SIGPIPE, SIG_IGN); |
148 | 1 | signal(SIGHUP, signal_handler); |
149 | 1 | #endif |
150 | 1 | int long_option_index = 0; |
151 | 1 | Status error; |
152 | 1 | std::string listen_host_port; |
153 | 1 | int ch; |
154 | | |
155 | 1 | std::string log_file; |
156 | 1 | StringRef |
157 | 1 | log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" |
158 | | |
159 | 1 | GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap; |
160 | 1 | int min_gdbserver_port = 0; |
161 | 1 | int max_gdbserver_port = 0; |
162 | 1 | uint16_t port_offset = 0; |
163 | | |
164 | 1 | FileSpec socket_file; |
165 | 1 | bool show_usage = false; |
166 | 1 | int option_error = 0; |
167 | 1 | int socket_error = -1; |
168 | | |
169 | 1 | std::string short_options(OptionParser::GetShortOptionString(g_long_options)); |
170 | | |
171 | | #if __GLIBC__ |
172 | | optind = 0; |
173 | | #else |
174 | 1 | optreset = 1; |
175 | 1 | optind = 1; |
176 | 1 | #endif |
177 | | |
178 | 5 | while ((ch = getopt_long_only(argc, argv, short_options.c_str(), |
179 | 5 | g_long_options, &long_option_index)) != -1) { |
180 | 4 | switch (ch) { |
181 | 1 | case 0: // Any optional that auto set themselves will return 0 |
182 | 1 | break; |
183 | | |
184 | 1 | case 'L': |
185 | 1 | listen_host_port.append(optarg); |
186 | 1 | break; |
187 | | |
188 | 0 | case 'l': // Set Log File |
189 | 0 | if (optarg && optarg[0]) |
190 | 0 | log_file.assign(optarg); |
191 | 0 | break; |
192 | | |
193 | 0 | case 'c': // Log Channels |
194 | 0 | if (optarg && optarg[0]) |
195 | 0 | log_channels = StringRef(optarg); |
196 | 0 | break; |
197 | | |
198 | 0 | case 'f': // Socket file |
199 | 0 | if (optarg && optarg[0]) |
200 | 0 | socket_file.SetFile(optarg, FileSpec::Style::native); |
201 | 0 | break; |
202 | | |
203 | 0 | case 'p': { |
204 | 0 | if (!llvm::to_integer(optarg, port_offset)) { |
205 | 0 | WithColor::error() << "invalid port offset string " << optarg << "\n"; |
206 | 0 | option_error = 4; |
207 | 0 | break; |
208 | 0 | } |
209 | 0 | if (port_offset < LOW_PORT || port_offset > HIGH_PORT) { |
210 | 0 | WithColor::error() << llvm::formatv( |
211 | 0 | "port offset {0} is not in the " |
212 | 0 | "valid user port range of {1} - {2}\n", |
213 | 0 | port_offset, LOW_PORT, HIGH_PORT); |
214 | 0 | option_error = 5; |
215 | 0 | } |
216 | 0 | } break; |
217 | | |
218 | 0 | case 'P': |
219 | 1 | case 'm': |
220 | 2 | case 'M': { |
221 | 2 | uint16_t portnum; |
222 | 2 | if (!llvm::to_integer(optarg, portnum)) { |
223 | 0 | WithColor::error() << "invalid port number string " << optarg << "\n"; |
224 | 0 | option_error = 2; |
225 | 0 | break; |
226 | 0 | } |
227 | 2 | if (portnum < LOW_PORT || portnum > HIGH_PORT) { |
228 | 0 | WithColor::error() << llvm::formatv( |
229 | 0 | "port number {0} is not in the " |
230 | 0 | "valid user port range of {1} - {2}\n", |
231 | 0 | portnum, LOW_PORT, HIGH_PORT); |
232 | 0 | option_error = 1; |
233 | 0 | break; |
234 | 0 | } |
235 | 2 | if (ch == 'P') |
236 | 0 | gdbserver_portmap.AllowPort(portnum); |
237 | 2 | else if (ch == 'm') |
238 | 1 | min_gdbserver_port = portnum; |
239 | 1 | else |
240 | 1 | max_gdbserver_port = portnum; |
241 | 2 | } break; |
242 | | |
243 | 0 | case 'h': /* fall-through is intentional */ |
244 | 0 | case '?': |
245 | 0 | show_usage = true; |
246 | 0 | break; |
247 | 4 | } |
248 | 4 | } |
249 | | |
250 | 1 | if (!LLDBServerUtilities::SetupLogging(log_file, log_channels, 0)) |
251 | 0 | return -1; |
252 | | |
253 | | // Make a port map for a port range that was specified. |
254 | 1 | if (min_gdbserver_port && min_gdbserver_port < max_gdbserver_port) { |
255 | 0 | gdbserver_portmap = GDBRemoteCommunicationServerPlatform::PortMap( |
256 | 0 | min_gdbserver_port, max_gdbserver_port); |
257 | 1 | } else if (min_gdbserver_port || max_gdbserver_port0 ) { |
258 | 1 | WithColor::error() << llvm::formatv( |
259 | 1 | "--min-gdbserver-port ({0}) is not lower than " |
260 | 1 | "--max-gdbserver-port ({1})\n", |
261 | 1 | min_gdbserver_port, max_gdbserver_port); |
262 | 1 | option_error = 3; |
263 | 1 | } |
264 | | |
265 | | // Print usage and exit if no listening port is specified. |
266 | 1 | if (listen_host_port.empty()) |
267 | 0 | show_usage = true; |
268 | | |
269 | 1 | if (show_usage || option_error) { |
270 | 1 | display_usage(progname, subcommand); |
271 | 1 | exit(option_error); |
272 | 1 | } |
273 | | |
274 | | // Skip any options we consumed with getopt_long_only. |
275 | 0 | argc -= optind; |
276 | 0 | argv += optind; |
277 | 0 | lldb_private::Args inferior_arguments; |
278 | 0 | inferior_arguments.SetArguments(argc, const_cast<const char **>(argv)); |
279 | |
|
280 | 0 | const bool children_inherit_listen_socket = false; |
281 | | // the test suite makes many connections in parallel, let's not miss any. |
282 | | // The highest this should get reasonably is a function of the number |
283 | | // of target CPUs. For now, let's just use 100. |
284 | 0 | const int backlog = 100; |
285 | |
|
286 | 0 | std::unique_ptr<Acceptor> acceptor_up(Acceptor::Create( |
287 | 0 | listen_host_port, children_inherit_listen_socket, error)); |
288 | 0 | if (error.Fail()) { |
289 | 0 | fprintf(stderr, "failed to create acceptor: %s", error.AsCString()); |
290 | 0 | exit(socket_error); |
291 | 0 | } |
292 | | |
293 | 0 | error = acceptor_up->Listen(backlog); |
294 | 0 | if (error.Fail()) { |
295 | 0 | printf("failed to listen: %s\n", error.AsCString()); |
296 | 0 | exit(socket_error); |
297 | 0 | } |
298 | 0 | if (socket_file) { |
299 | 0 | error = |
300 | 0 | save_socket_id_to_file(acceptor_up->GetLocalSocketId(), socket_file); |
301 | 0 | if (error.Fail()) { |
302 | 0 | fprintf(stderr, "failed to write socket id to %s: %s\n", |
303 | 0 | socket_file.GetPath().c_str(), error.AsCString()); |
304 | 0 | return 1; |
305 | 0 | } |
306 | 0 | } |
307 | | |
308 | 0 | do { |
309 | 0 | GDBRemoteCommunicationServerPlatform platform( |
310 | 0 | acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme()); |
311 | |
|
312 | 0 | if (port_offset > 0) |
313 | 0 | platform.SetPortOffset(port_offset); |
314 | |
|
315 | 0 | if (!gdbserver_portmap.empty()) { |
316 | 0 | platform.SetPortMap(std::move(gdbserver_portmap)); |
317 | 0 | } |
318 | |
|
319 | 0 | const bool children_inherit_accept_socket = true; |
320 | 0 | Connection *conn = nullptr; |
321 | 0 | error = acceptor_up->Accept(children_inherit_accept_socket, conn); |
322 | 0 | if (error.Fail()) { |
323 | 0 | WithColor::error() << error.AsCString() << '\n'; |
324 | 0 | exit(socket_error); |
325 | 0 | } |
326 | 0 | printf("Connection established.\n"); |
327 | 0 | if (g_server) { |
328 | | // Collect child zombie processes. |
329 | 0 | #if !defined(_WIN32) |
330 | 0 | while (waitpid(-1, nullptr, WNOHANG) > 0) |
331 | 0 | ; |
332 | 0 | #endif |
333 | 0 | if (fork()) { |
334 | | // Parent doesn't need a connection to the lldb client |
335 | 0 | delete conn; |
336 | | |
337 | | // Parent will continue to listen for new connections. |
338 | 0 | continue; |
339 | 0 | } else { |
340 | | // Child process will handle the connection and exit. |
341 | 0 | g_server = 0; |
342 | | // Listening socket is owned by parent process. |
343 | 0 | acceptor_up.release(); |
344 | 0 | } |
345 | 0 | } else { |
346 | | // If not running as a server, this process will not accept |
347 | | // connections while a connection is active. |
348 | 0 | acceptor_up.reset(); |
349 | 0 | } |
350 | 0 | platform.SetConnection(std::unique_ptr<Connection>(conn)); |
351 | |
|
352 | 0 | if (platform.IsConnected()) { |
353 | 0 | if (inferior_arguments.GetArgumentCount() > 0) { |
354 | 0 | lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; |
355 | 0 | llvm::Optional<uint16_t> port = 0; |
356 | 0 | std::string socket_name; |
357 | 0 | Status error = platform.LaunchGDBServer(inferior_arguments, |
358 | 0 | "", // hostname |
359 | 0 | pid, port, socket_name); |
360 | 0 | if (error.Success()) |
361 | 0 | platform.SetPendingGdbServer(pid, *port, socket_name); |
362 | 0 | else |
363 | 0 | fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString()); |
364 | 0 | } |
365 | |
|
366 | 0 | bool interrupt = false; |
367 | 0 | bool done = false; |
368 | 0 | while (!interrupt && !done) { |
369 | 0 | if (platform.GetPacketAndSendResponse(llvm::None, error, interrupt, |
370 | 0 | done) != |
371 | 0 | GDBRemoteCommunication::PacketResult::Success) |
372 | 0 | break; |
373 | 0 | } |
374 | |
|
375 | 0 | if (error.Fail()) |
376 | 0 | WithColor::error() << error.AsCString() << '\n'; |
377 | 0 | } |
378 | 0 | } while (g_server); |
379 | | |
380 | 0 | fprintf(stderr, "lldb-server exiting...\n"); |
381 | |
|
382 | 0 | return 0; |
383 | 0 | } |