1 | //===-- GDBRemoteCommunicationServerPlatform.cpp --------------------------===// |
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 "GDBRemoteCommunicationServerPlatform.h" |
10 | |
11 | #include <cerrno> |
12 | |
13 | #include <chrono> |
14 | #include <csignal> |
15 | #include <cstring> |
16 | #include <mutex> |
17 | #include <optional> |
18 | #include <sstream> |
19 | #include <thread> |
20 | |
21 | #include "llvm/Support/FileSystem.h" |
22 | #include "llvm/Support/JSON.h" |
23 | #include "llvm/Support/Threading.h" |
24 | |
25 | #include "lldb/Host/Config.h" |
26 | #include "lldb/Host/ConnectionFileDescriptor.h" |
27 | #include "lldb/Host/FileAction.h" |
28 | #include "lldb/Host/Host.h" |
29 | #include "lldb/Host/HostInfo.h" |
30 | #include "lldb/Interpreter/CommandCompletions.h" |
31 | #include "lldb/Target/Platform.h" |
32 | #include "lldb/Target/UnixSignals.h" |
33 | #include "lldb/Utility/GDBRemote.h" |
34 | #include "lldb/Utility/LLDBLog.h" |
35 | #include "lldb/Utility/Log.h" |
36 | #include "lldb/Utility/StreamString.h" |
37 | #include "lldb/Utility/StructuredData.h" |
38 | #include "lldb/Utility/TildeExpressionResolver.h" |
39 | #include "lldb/Utility/UriParser.h" |
40 | |
41 | #include "lldb/Utility/StringExtractorGDBRemote.h" |
42 | |
43 | using namespace lldb; |
44 | using namespace lldb_private::process_gdb_remote; |
45 | using namespace lldb_private; |
46 | |
47 | GDBRemoteCommunicationServerPlatform::PortMap::PortMap(uint16_t min_port, |
48 | uint16_t max_port) { |
49 | for (; min_port < max_port; ++min_port) |
50 | m_port_map[min_port] = LLDB_INVALID_PROCESS_ID; |
51 | } |
52 | |
53 | void GDBRemoteCommunicationServerPlatform::PortMap::AllowPort(uint16_t port) { |
54 | // Do not modify existing mappings |
55 | m_port_map.insert(x: {port, LLDB_INVALID_PROCESS_ID}); |
56 | } |
57 | |
58 | llvm::Expected<uint16_t> |
59 | GDBRemoteCommunicationServerPlatform::PortMap::GetNextAvailablePort() { |
60 | if (m_port_map.empty()) |
61 | return 0; // Bind to port zero and get a port, we didn't have any |
62 | // limitations |
63 | |
64 | for (auto &pair : m_port_map) { |
65 | if (pair.second == LLDB_INVALID_PROCESS_ID) { |
66 | pair.second = ~(lldb::pid_t)LLDB_INVALID_PROCESS_ID; |
67 | return pair.first; |
68 | } |
69 | } |
70 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
71 | Msg: "No free port found in port map" ); |
72 | } |
73 | |
74 | bool GDBRemoteCommunicationServerPlatform::PortMap::AssociatePortWithProcess( |
75 | uint16_t port, lldb::pid_t pid) { |
76 | auto pos = m_port_map.find(x: port); |
77 | if (pos != m_port_map.end()) { |
78 | pos->second = pid; |
79 | return true; |
80 | } |
81 | return false; |
82 | } |
83 | |
84 | bool GDBRemoteCommunicationServerPlatform::PortMap::FreePort(uint16_t port) { |
85 | std::map<uint16_t, lldb::pid_t>::iterator pos = m_port_map.find(x: port); |
86 | if (pos != m_port_map.end()) { |
87 | pos->second = LLDB_INVALID_PROCESS_ID; |
88 | return true; |
89 | } |
90 | return false; |
91 | } |
92 | |
93 | bool GDBRemoteCommunicationServerPlatform::PortMap::FreePortForProcess( |
94 | lldb::pid_t pid) { |
95 | if (!m_port_map.empty()) { |
96 | for (auto &pair : m_port_map) { |
97 | if (pair.second == pid) { |
98 | pair.second = LLDB_INVALID_PROCESS_ID; |
99 | return true; |
100 | } |
101 | } |
102 | } |
103 | return false; |
104 | } |
105 | |
106 | bool GDBRemoteCommunicationServerPlatform::PortMap::empty() const { |
107 | return m_port_map.empty(); |
108 | } |
109 | |
110 | // GDBRemoteCommunicationServerPlatform constructor |
111 | GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform( |
112 | const Socket::SocketProtocol socket_protocol, const char *socket_scheme) |
113 | : GDBRemoteCommunicationServerCommon(), |
114 | m_socket_protocol(socket_protocol), m_socket_scheme(socket_scheme), |
115 | m_spawned_pids_mutex(), m_port_map(), m_port_offset(0) { |
116 | m_pending_gdb_server.pid = LLDB_INVALID_PROCESS_ID; |
117 | m_pending_gdb_server.port = 0; |
118 | |
119 | RegisterMemberFunctionHandler( |
120 | packet_type: StringExtractorGDBRemote::eServerPacketType_qC, |
121 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qC); |
122 | RegisterMemberFunctionHandler( |
123 | packet_type: StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir, |
124 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir); |
125 | RegisterMemberFunctionHandler( |
126 | packet_type: StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer, |
127 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer); |
128 | RegisterMemberFunctionHandler( |
129 | packet_type: StringExtractorGDBRemote::eServerPacketType_qQueryGDBServer, |
130 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer); |
131 | RegisterMemberFunctionHandler( |
132 | packet_type: StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess, |
133 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess); |
134 | RegisterMemberFunctionHandler( |
135 | packet_type: StringExtractorGDBRemote::eServerPacketType_qProcessInfo, |
136 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo); |
137 | RegisterMemberFunctionHandler( |
138 | packet_type: StringExtractorGDBRemote::eServerPacketType_qPathComplete, |
139 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qPathComplete); |
140 | RegisterMemberFunctionHandler( |
141 | packet_type: StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir, |
142 | handler: &GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir); |
143 | RegisterMemberFunctionHandler( |
144 | packet_type: StringExtractorGDBRemote::eServerPacketType_jSignalsInfo, |
145 | handler: &GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo); |
146 | |
147 | RegisterPacketHandler(packet_type: StringExtractorGDBRemote::eServerPacketType_interrupt, |
148 | handler: [](StringExtractorGDBRemote packet, Status &error, |
149 | bool &interrupt, bool &quit) { |
150 | error.SetErrorString("interrupt received" ); |
151 | interrupt = true; |
152 | return PacketResult::Success; |
153 | }); |
154 | } |
155 | |
156 | // Destructor |
157 | GDBRemoteCommunicationServerPlatform::~GDBRemoteCommunicationServerPlatform() = |
158 | default; |
159 | |
160 | Status GDBRemoteCommunicationServerPlatform::LaunchGDBServer( |
161 | const lldb_private::Args &args, std::string hostname, lldb::pid_t &pid, |
162 | std::optional<uint16_t> &port, std::string &socket_name) { |
163 | if (!port) { |
164 | llvm::Expected<uint16_t> available_port = m_port_map.GetNextAvailablePort(); |
165 | if (available_port) |
166 | port = *available_port; |
167 | else |
168 | return Status(available_port.takeError()); |
169 | } |
170 | |
171 | // Spawn a new thread to accept the port that gets bound after binding to |
172 | // port 0 (zero). |
173 | |
174 | // ignore the hostname send from the remote end, just use the ip address that |
175 | // we're currently communicating with as the hostname |
176 | |
177 | // Spawn a debugserver and try to get the port it listens to. |
178 | ProcessLaunchInfo debugserver_launch_info; |
179 | if (hostname.empty()) |
180 | hostname = "127.0.0.1" ; |
181 | |
182 | Log *log = GetLog(mask: LLDBLog::Platform); |
183 | LLDB_LOGF(log, "Launching debugserver with: %s:%u..." , hostname.c_str(), |
184 | *port); |
185 | |
186 | // Do not run in a new session so that it can not linger after the platform |
187 | // closes. |
188 | debugserver_launch_info.SetLaunchInSeparateProcessGroup(false); |
189 | debugserver_launch_info.SetMonitorProcessCallback( |
190 | std::bind(f: &GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, |
191 | args: this, args: std::placeholders::_1)); |
192 | |
193 | std::ostringstream url; |
194 | // debugserver does not accept the URL scheme prefix. |
195 | #if !defined(__APPLE__) |
196 | url << m_socket_scheme << "://" ; |
197 | #endif |
198 | uint16_t *port_ptr = &*port; |
199 | if (m_socket_protocol == Socket::ProtocolTcp) { |
200 | std::string platform_uri = GetConnection()->GetURI(); |
201 | std::optional<URI> parsed_uri = URI::Parse(uri: platform_uri); |
202 | url << '[' << parsed_uri->hostname.str() << "]:" << *port; |
203 | } else { |
204 | socket_name = GetDomainSocketPath(prefix: "gdbserver" ).GetPath(); |
205 | url << socket_name; |
206 | port_ptr = nullptr; |
207 | } |
208 | |
209 | Status error = StartDebugserverProcess( |
210 | url: url.str().c_str(), platform: nullptr, launch_info&: debugserver_launch_info, port: port_ptr, inferior_args: &args, pass_comm_fd: -1); |
211 | |
212 | pid = debugserver_launch_info.GetProcessID(); |
213 | if (pid != LLDB_INVALID_PROCESS_ID) { |
214 | std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
215 | m_spawned_pids.insert(x: pid); |
216 | if (*port > 0) |
217 | m_port_map.AssociatePortWithProcess(port: *port, pid); |
218 | } else { |
219 | if (*port > 0) |
220 | m_port_map.FreePort(port: *port); |
221 | } |
222 | return error; |
223 | } |
224 | |
225 | GDBRemoteCommunication::PacketResult |
226 | GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer( |
227 | StringExtractorGDBRemote &packet) { |
228 | // Spawn a local debugserver as a platform so we can then attach or launch a |
229 | // process... |
230 | |
231 | Log *log = GetLog(mask: LLDBLog::Platform); |
232 | LLDB_LOGF(log, "GDBRemoteCommunicationServerPlatform::%s() called" , |
233 | __FUNCTION__); |
234 | |
235 | ConnectionFileDescriptor file_conn; |
236 | std::string hostname; |
237 | packet.SetFilePos(::strlen(s: "qLaunchGDBServer;" )); |
238 | llvm::StringRef name; |
239 | llvm::StringRef value; |
240 | std::optional<uint16_t> port; |
241 | while (packet.GetNameColonValue(name, value)) { |
242 | if (name.equals(RHS: "host" )) |
243 | hostname = std::string(value); |
244 | else if (name.equals(RHS: "port" )) { |
245 | // Make the Optional valid so we can use its value |
246 | port = 0; |
247 | value.getAsInteger(Radix: 0, Result&: *port); |
248 | } |
249 | } |
250 | |
251 | lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; |
252 | std::string socket_name; |
253 | Status error = |
254 | LaunchGDBServer(args: Args(), hostname, pid&: debugserver_pid, port, socket_name); |
255 | if (error.Fail()) { |
256 | LLDB_LOGF(log, |
257 | "GDBRemoteCommunicationServerPlatform::%s() debugserver " |
258 | "launch failed: %s" , |
259 | __FUNCTION__, error.AsCString()); |
260 | return SendErrorResponse(error: 9); |
261 | } |
262 | |
263 | LLDB_LOGF(log, |
264 | "GDBRemoteCommunicationServerPlatform::%s() debugserver " |
265 | "launched successfully as pid %" PRIu64, |
266 | __FUNCTION__, debugserver_pid); |
267 | |
268 | StreamGDBRemote response; |
269 | assert(port); |
270 | response.Printf(format: "pid:%" PRIu64 ";port:%u;" , debugserver_pid, |
271 | *port + m_port_offset); |
272 | if (!socket_name.empty()) { |
273 | response.PutCString(cstr: "socket_name:" ); |
274 | response.PutStringAsRawHex8(s: socket_name); |
275 | response.PutChar(ch: ';'); |
276 | } |
277 | |
278 | PacketResult packet_result = SendPacketNoLock(payload: response.GetString()); |
279 | if (packet_result != PacketResult::Success) { |
280 | if (debugserver_pid != LLDB_INVALID_PROCESS_ID) |
281 | Host::Kill(pid: debugserver_pid, SIGINT); |
282 | } |
283 | return packet_result; |
284 | } |
285 | |
286 | GDBRemoteCommunication::PacketResult |
287 | GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer( |
288 | StringExtractorGDBRemote &packet) { |
289 | namespace json = llvm::json; |
290 | |
291 | if (m_pending_gdb_server.pid == LLDB_INVALID_PROCESS_ID) |
292 | return SendErrorResponse(error: 4); |
293 | |
294 | json::Object server{{.K: "port" , .V: m_pending_gdb_server.port}}; |
295 | |
296 | if (!m_pending_gdb_server.socket_name.empty()) |
297 | server.try_emplace(K: "socket_name" , Args&: m_pending_gdb_server.socket_name); |
298 | |
299 | json::Array server_list; |
300 | server_list.push_back(E: std::move(server)); |
301 | |
302 | StreamGDBRemote response; |
303 | response.AsRawOstream() << std::move(server_list); |
304 | |
305 | StreamGDBRemote escaped_response; |
306 | escaped_response.PutEscapedBytes(s: response.GetString().data(), |
307 | src_len: response.GetSize()); |
308 | return SendPacketNoLock(payload: escaped_response.GetString()); |
309 | } |
310 | |
311 | GDBRemoteCommunication::PacketResult |
312 | GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess( |
313 | StringExtractorGDBRemote &packet) { |
314 | packet.SetFilePos(::strlen(s: "qKillSpawnedProcess:" )); |
315 | |
316 | lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID); |
317 | |
318 | // verify that we know anything about this pid. Scope for locker |
319 | { |
320 | std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
321 | if (m_spawned_pids.find(x: pid) == m_spawned_pids.end()) { |
322 | // not a pid we know about |
323 | return SendErrorResponse(error: 10); |
324 | } |
325 | } |
326 | |
327 | // go ahead and attempt to kill the spawned process |
328 | if (KillSpawnedProcess(pid)) |
329 | return SendOKResponse(); |
330 | else |
331 | return SendErrorResponse(error: 11); |
332 | } |
333 | |
334 | bool GDBRemoteCommunicationServerPlatform::KillSpawnedProcess(lldb::pid_t pid) { |
335 | // make sure we know about this process |
336 | { |
337 | std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
338 | if (m_spawned_pids.find(x: pid) == m_spawned_pids.end()) |
339 | return false; |
340 | } |
341 | |
342 | // first try a SIGTERM (standard kill) |
343 | Host::Kill(pid, SIGTERM); |
344 | |
345 | // check if that worked |
346 | for (size_t i = 0; i < 10; ++i) { |
347 | { |
348 | std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
349 | if (m_spawned_pids.find(x: pid) == m_spawned_pids.end()) { |
350 | // it is now killed |
351 | return true; |
352 | } |
353 | } |
354 | std::this_thread::sleep_for(rtime: std::chrono::milliseconds(10)); |
355 | } |
356 | |
357 | { |
358 | std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
359 | if (m_spawned_pids.find(x: pid) == m_spawned_pids.end()) |
360 | return true; |
361 | } |
362 | |
363 | // the launched process still lives. Now try killing it again, this time |
364 | // with an unblockable signal. |
365 | Host::Kill(pid, SIGKILL); |
366 | |
367 | for (size_t i = 0; i < 10; ++i) { |
368 | { |
369 | std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
370 | if (m_spawned_pids.find(x: pid) == m_spawned_pids.end()) { |
371 | // it is now killed |
372 | return true; |
373 | } |
374 | } |
375 | std::this_thread::sleep_for(rtime: std::chrono::milliseconds(10)); |
376 | } |
377 | |
378 | // check one more time after the final sleep |
379 | { |
380 | std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
381 | if (m_spawned_pids.find(x: pid) == m_spawned_pids.end()) |
382 | return true; |
383 | } |
384 | |
385 | // no luck - the process still lives |
386 | return false; |
387 | } |
388 | |
389 | GDBRemoteCommunication::PacketResult |
390 | GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo( |
391 | StringExtractorGDBRemote &packet) { |
392 | lldb::pid_t pid = m_process_launch_info.GetProcessID(); |
393 | m_process_launch_info.Clear(); |
394 | |
395 | if (pid == LLDB_INVALID_PROCESS_ID) |
396 | return SendErrorResponse(error: 1); |
397 | |
398 | ProcessInstanceInfo proc_info; |
399 | if (!Host::GetProcessInfo(pid, proc_info)) |
400 | return SendErrorResponse(error: 1); |
401 | |
402 | StreamString response; |
403 | CreateProcessInfoResponse_DebugServerStyle(proc_info, response); |
404 | return SendPacketNoLock(payload: response.GetString()); |
405 | } |
406 | |
407 | GDBRemoteCommunication::PacketResult |
408 | GDBRemoteCommunicationServerPlatform::Handle_qPathComplete( |
409 | StringExtractorGDBRemote &packet) { |
410 | packet.SetFilePos(::strlen(s: "qPathComplete:" )); |
411 | const bool only_dir = (packet.GetHexMaxU32(little_endian: false, fail_value: 0) == 1); |
412 | if (packet.GetChar() != ',') |
413 | return SendErrorResponse(error: 85); |
414 | std::string path; |
415 | packet.GetHexByteString(str&: path); |
416 | |
417 | StringList matches; |
418 | StandardTildeExpressionResolver resolver; |
419 | if (only_dir) |
420 | CommandCompletions::DiskDirectories(partial_file_name: path, matches, Resolver&: resolver); |
421 | else |
422 | CommandCompletions::DiskFiles(partial_file_name: path, matches, Resolver&: resolver); |
423 | |
424 | StreamString response; |
425 | response.PutChar(ch: 'M'); |
426 | llvm::StringRef separator; |
427 | std::sort(first: matches.begin(), last: matches.end()); |
428 | for (const auto &match : matches) { |
429 | response << separator; |
430 | separator = "," ; |
431 | // encode result strings into hex bytes to avoid unexpected error caused by |
432 | // special characters like '$'. |
433 | response.PutStringAsRawHex8(s: match.c_str()); |
434 | } |
435 | |
436 | return SendPacketNoLock(payload: response.GetString()); |
437 | } |
438 | |
439 | GDBRemoteCommunication::PacketResult |
440 | GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir( |
441 | StringExtractorGDBRemote &packet) { |
442 | |
443 | llvm::SmallString<64> cwd; |
444 | if (std::error_code ec = llvm::sys::fs::current_path(result&: cwd)) |
445 | return SendErrorResponse(error: ec.value()); |
446 | |
447 | StreamString response; |
448 | response.PutBytesAsRawHex8(src: cwd.data(), src_len: cwd.size()); |
449 | return SendPacketNoLock(payload: response.GetString()); |
450 | } |
451 | |
452 | GDBRemoteCommunication::PacketResult |
453 | GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir( |
454 | StringExtractorGDBRemote &packet) { |
455 | packet.SetFilePos(::strlen(s: "QSetWorkingDir:" )); |
456 | std::string path; |
457 | packet.GetHexByteString(str&: path); |
458 | |
459 | if (std::error_code ec = llvm::sys::fs::set_current_path(path)) |
460 | return SendErrorResponse(error: ec.value()); |
461 | return SendOKResponse(); |
462 | } |
463 | |
464 | GDBRemoteCommunication::PacketResult |
465 | GDBRemoteCommunicationServerPlatform::Handle_qC( |
466 | StringExtractorGDBRemote &packet) { |
467 | // NOTE: lldb should now be using qProcessInfo for process IDs. This path |
468 | // here |
469 | // should not be used. It is reporting process id instead of thread id. The |
470 | // correct answer doesn't seem to make much sense for lldb-platform. |
471 | // CONSIDER: flip to "unsupported". |
472 | lldb::pid_t pid = m_process_launch_info.GetProcessID(); |
473 | |
474 | StreamString response; |
475 | response.Printf(format: "QC%" PRIx64, pid); |
476 | |
477 | // If we launch a process and this GDB server is acting as a platform, then |
478 | // we need to clear the process launch state so we can start launching |
479 | // another process. In order to launch a process a bunch or packets need to |
480 | // be sent: environment packets, working directory, disable ASLR, and many |
481 | // more settings. When we launch a process we then need to know when to clear |
482 | // this information. Currently we are selecting the 'qC' packet as that |
483 | // packet which seems to make the most sense. |
484 | if (pid != LLDB_INVALID_PROCESS_ID) { |
485 | m_process_launch_info.Clear(); |
486 | } |
487 | |
488 | return SendPacketNoLock(payload: response.GetString()); |
489 | } |
490 | |
491 | GDBRemoteCommunication::PacketResult |
492 | GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo( |
493 | StringExtractorGDBRemote &packet) { |
494 | StructuredData::Array signal_array; |
495 | |
496 | lldb::UnixSignalsSP signals = UnixSignals::CreateForHost(); |
497 | for (auto signo = signals->GetFirstSignalNumber(); |
498 | signo != LLDB_INVALID_SIGNAL_NUMBER; |
499 | signo = signals->GetNextSignalNumber(current_signal: signo)) { |
500 | auto dictionary = std::make_shared<StructuredData::Dictionary>(); |
501 | |
502 | dictionary->AddIntegerItem(key: "signo" , value: signo); |
503 | dictionary->AddStringItem(key: "name" , value: signals->GetSignalAsStringRef(signo)); |
504 | |
505 | bool suppress, stop, notify; |
506 | signals->GetSignalInfo(signo, should_suppress&: suppress, should_stop&: stop, should_notify&: notify); |
507 | dictionary->AddBooleanItem(key: "suppress" , value: suppress); |
508 | dictionary->AddBooleanItem(key: "stop" , value: stop); |
509 | dictionary->AddBooleanItem(key: "notify" , value: notify); |
510 | |
511 | signal_array.Push(item: dictionary); |
512 | } |
513 | |
514 | StreamString response; |
515 | signal_array.Dump(s&: response); |
516 | return SendPacketNoLock(payload: response.GetString()); |
517 | } |
518 | |
519 | void GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped( |
520 | lldb::pid_t pid) { |
521 | std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
522 | m_port_map.FreePortForProcess(pid); |
523 | m_spawned_pids.erase(x: pid); |
524 | } |
525 | |
526 | Status GDBRemoteCommunicationServerPlatform::LaunchProcess() { |
527 | if (!m_process_launch_info.GetArguments().GetArgumentCount()) |
528 | return Status("%s: no process command line specified to launch" , |
529 | __FUNCTION__); |
530 | |
531 | // specify the process monitor if not already set. This should generally be |
532 | // what happens since we need to reap started processes. |
533 | if (!m_process_launch_info.GetMonitorProcessCallback()) |
534 | m_process_launch_info.SetMonitorProcessCallback(std::bind( |
535 | f: &GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, args: this, |
536 | args: std::placeholders::_1)); |
537 | |
538 | Status error = Host::LaunchProcess(launch_info&: m_process_launch_info); |
539 | if (!error.Success()) { |
540 | fprintf(stderr, format: "%s: failed to launch executable %s" , __FUNCTION__, |
541 | m_process_launch_info.GetArguments().GetArgumentAtIndex(idx: 0)); |
542 | return error; |
543 | } |
544 | |
545 | printf(format: "Launched '%s' as process %" PRIu64 "...\n" , |
546 | m_process_launch_info.GetArguments().GetArgumentAtIndex(idx: 0), |
547 | m_process_launch_info.GetProcessID()); |
548 | |
549 | // add to list of spawned processes. On an lldb-gdbserver, we would expect |
550 | // there to be only one. |
551 | const auto pid = m_process_launch_info.GetProcessID(); |
552 | if (pid != LLDB_INVALID_PROCESS_ID) { |
553 | // add to spawned pids |
554 | std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
555 | m_spawned_pids.insert(x: pid); |
556 | } |
557 | |
558 | return error; |
559 | } |
560 | |
561 | void GDBRemoteCommunicationServerPlatform::SetPortMap(PortMap &&port_map) { |
562 | m_port_map = std::move(port_map); |
563 | } |
564 | |
565 | const FileSpec &GDBRemoteCommunicationServerPlatform::GetDomainSocketDir() { |
566 | static FileSpec g_domainsocket_dir; |
567 | static llvm::once_flag g_once_flag; |
568 | |
569 | llvm::call_once(flag&: g_once_flag, F: []() { |
570 | const char *domainsocket_dir_env = |
571 | ::getenv(name: "LLDB_DEBUGSERVER_DOMAINSOCKET_DIR" ); |
572 | if (domainsocket_dir_env != nullptr) |
573 | g_domainsocket_dir = FileSpec(domainsocket_dir_env); |
574 | else |
575 | g_domainsocket_dir = HostInfo::GetProcessTempDir(); |
576 | }); |
577 | |
578 | return g_domainsocket_dir; |
579 | } |
580 | |
581 | FileSpec |
582 | GDBRemoteCommunicationServerPlatform::GetDomainSocketPath(const char *prefix) { |
583 | llvm::SmallString<128> socket_path; |
584 | llvm::SmallString<128> socket_name( |
585 | (llvm::StringRef(prefix) + ".%%%%%%" ).str()); |
586 | |
587 | FileSpec socket_path_spec(GetDomainSocketDir()); |
588 | socket_path_spec.AppendPathComponent(component: socket_name.c_str()); |
589 | |
590 | llvm::sys::fs::createUniqueFile(Model: socket_path_spec.GetPath().c_str(), |
591 | ResultPath&: socket_path); |
592 | return FileSpec(socket_path.c_str()); |
593 | } |
594 | |
595 | void GDBRemoteCommunicationServerPlatform::SetPortOffset(uint16_t port_offset) { |
596 | m_port_offset = port_offset; |
597 | } |
598 | |
599 | void GDBRemoteCommunicationServerPlatform::SetPendingGdbServer( |
600 | lldb::pid_t pid, uint16_t port, const std::string &socket_name) { |
601 | m_pending_gdb_server.pid = pid; |
602 | m_pending_gdb_server.port = port; |
603 | m_pending_gdb_server.socket_name = socket_name; |
604 | } |
605 | |