| 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 constructor |
| 48 | GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform( |
| 49 | const Socket::SocketProtocol socket_protocol, uint16_t gdbserver_port) |
| 50 | : GDBRemoteCommunicationServerCommon(), m_socket_protocol(socket_protocol), |
| 51 | m_gdbserver_port(gdbserver_port) { |
| 52 | |
| 53 | RegisterMemberFunctionHandler( |
| 54 | packet_type: StringExtractorGDBRemote::eServerPacketType_qC, |
| 55 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qC); |
| 56 | RegisterMemberFunctionHandler( |
| 57 | packet_type: StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir, |
| 58 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir); |
| 59 | RegisterMemberFunctionHandler( |
| 60 | packet_type: StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer, |
| 61 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer); |
| 62 | RegisterMemberFunctionHandler( |
| 63 | packet_type: StringExtractorGDBRemote::eServerPacketType_qQueryGDBServer, |
| 64 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer); |
| 65 | RegisterMemberFunctionHandler( |
| 66 | packet_type: StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess, |
| 67 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess); |
| 68 | RegisterMemberFunctionHandler( |
| 69 | packet_type: StringExtractorGDBRemote::eServerPacketType_qProcessInfo, |
| 70 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo); |
| 71 | RegisterMemberFunctionHandler( |
| 72 | packet_type: StringExtractorGDBRemote::eServerPacketType_qPathComplete, |
| 73 | handler: &GDBRemoteCommunicationServerPlatform::Handle_qPathComplete); |
| 74 | RegisterMemberFunctionHandler( |
| 75 | packet_type: StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir, |
| 76 | handler: &GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir); |
| 77 | RegisterMemberFunctionHandler( |
| 78 | packet_type: StringExtractorGDBRemote::eServerPacketType_jSignalsInfo, |
| 79 | handler: &GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo); |
| 80 | |
| 81 | RegisterPacketHandler(packet_type: StringExtractorGDBRemote::eServerPacketType_interrupt, |
| 82 | handler: [](StringExtractorGDBRemote packet, Status &error, |
| 83 | bool &interrupt, bool &quit) { |
| 84 | error = Status::FromErrorString(str: "interrupt received" ); |
| 85 | interrupt = true; |
| 86 | return PacketResult::Success; |
| 87 | }); |
| 88 | } |
| 89 | |
| 90 | // Destructor |
| 91 | GDBRemoteCommunicationServerPlatform::~GDBRemoteCommunicationServerPlatform() = |
| 92 | default; |
| 93 | |
| 94 | Status GDBRemoteCommunicationServerPlatform::LaunchGDBServer( |
| 95 | const lldb_private::Args &args, lldb::pid_t &pid, std::string &socket_name, |
| 96 | shared_fd_t fd) { |
| 97 | std::ostringstream url; |
| 98 | if (fd == SharedSocket::kInvalidFD) { |
| 99 | if (m_socket_protocol == Socket::ProtocolTcp) { |
| 100 | // Just check that GDBServer exists. GDBServer must be launched after |
| 101 | // accepting the connection. |
| 102 | if (!GetDebugserverPath(platform: nullptr)) |
| 103 | return Status::FromErrorString(str: "unable to locate debugserver" ); |
| 104 | return Status(); |
| 105 | } |
| 106 | |
| 107 | // debugserver does not accept the URL scheme prefix. |
| 108 | #if !defined(__APPLE__) |
| 109 | url << Socket::FindSchemeByProtocol(protocol: m_socket_protocol) << "://" ; |
| 110 | #endif |
| 111 | socket_name = GetDomainSocketPath(prefix: "gdbserver" ).GetPath(); |
| 112 | url << socket_name; |
| 113 | } else { |
| 114 | if (m_socket_protocol != Socket::ProtocolTcp) |
| 115 | return Status::FromErrorString(str: "protocol must be tcp" ); |
| 116 | } |
| 117 | |
| 118 | // Spawn a debugserver and try to get the port it listens to. |
| 119 | ProcessLaunchInfo debugserver_launch_info; |
| 120 | Log *log = GetLog(mask: LLDBLog::Platform); |
| 121 | LLDB_LOG(log, "Launching debugserver url='{0}', fd={1}..." , url.str(), fd); |
| 122 | |
| 123 | // Do not run in a new session so that it can not linger after the platform |
| 124 | // closes. |
| 125 | debugserver_launch_info.SetLaunchInSeparateProcessGroup(false); |
| 126 | debugserver_launch_info.SetMonitorProcessCallback( |
| 127 | [](lldb::pid_t, int, int) {}); |
| 128 | |
| 129 | Status error = StartDebugserverProcess( |
| 130 | url: url.str().c_str(), platform: nullptr, launch_info&: debugserver_launch_info, port: nullptr, inferior_args: &args, pass_comm_fd: fd); |
| 131 | |
| 132 | if (error.Success()) { |
| 133 | pid = debugserver_launch_info.GetProcessID(); |
| 134 | AddSpawnedProcess(pid); |
| 135 | LLDB_LOGF(log, |
| 136 | "GDBRemoteCommunicationServerPlatform::%s() " |
| 137 | "debugserver launched successfully as pid %" PRIu64, |
| 138 | __FUNCTION__, pid); |
| 139 | } else { |
| 140 | LLDB_LOGF(log, |
| 141 | "GDBRemoteCommunicationServerPlatform::%s() " |
| 142 | "debugserver launch failed: %s" , |
| 143 | __FUNCTION__, error.AsCString()); |
| 144 | } |
| 145 | return error; |
| 146 | } |
| 147 | |
| 148 | GDBRemoteCommunication::PacketResult |
| 149 | GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer( |
| 150 | StringExtractorGDBRemote &packet) { |
| 151 | // Spawn a local debugserver as a platform so we can then attach or launch a |
| 152 | // process... |
| 153 | |
| 154 | Log *log = GetLog(mask: LLDBLog::Platform); |
| 155 | LLDB_LOGF(log, "GDBRemoteCommunicationServerPlatform::%s() called" , |
| 156 | __FUNCTION__); |
| 157 | |
| 158 | ConnectionFileDescriptor file_conn; |
| 159 | std::string hostname; |
| 160 | packet.SetFilePos(::strlen(s: "qLaunchGDBServer;" )); |
| 161 | llvm::StringRef name; |
| 162 | llvm::StringRef value; |
| 163 | std::optional<uint16_t> port; |
| 164 | while (packet.GetNameColonValue(name, value)) { |
| 165 | if (name == "host" ) |
| 166 | hostname = std::string(value); |
| 167 | else if (name == "port" ) { |
| 168 | // Make the Optional valid so we can use its value |
| 169 | port = 0; |
| 170 | value.getAsInteger(Radix: 0, Result&: *port); |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | // Ignore client's hostname and the port. |
| 175 | |
| 176 | lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; |
| 177 | std::string socket_name; |
| 178 | Status error = LaunchGDBServer(args: Args(), pid&: debugserver_pid, socket_name, |
| 179 | fd: SharedSocket::kInvalidFD); |
| 180 | if (error.Fail()) |
| 181 | return SendErrorResponse(error: 9); // EBADF |
| 182 | |
| 183 | StreamGDBRemote response; |
| 184 | uint16_t gdbserver_port = socket_name.empty() ? m_gdbserver_port : 0; |
| 185 | response.Printf(format: "pid:%" PRIu64 ";port:%u;" , debugserver_pid, gdbserver_port); |
| 186 | if (!socket_name.empty()) { |
| 187 | response.PutCString(cstr: "socket_name:" ); |
| 188 | response.PutStringAsRawHex8(s: socket_name); |
| 189 | response.PutChar(ch: ';'); |
| 190 | } |
| 191 | |
| 192 | PacketResult packet_result = SendPacketNoLock(payload: response.GetString()); |
| 193 | if (packet_result != PacketResult::Success) { |
| 194 | if (debugserver_pid != LLDB_INVALID_PROCESS_ID) |
| 195 | Host::Kill(pid: debugserver_pid, SIGINT); |
| 196 | } |
| 197 | return packet_result; |
| 198 | } |
| 199 | |
| 200 | GDBRemoteCommunication::PacketResult |
| 201 | GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer( |
| 202 | StringExtractorGDBRemote &packet) { |
| 203 | namespace json = llvm::json; |
| 204 | |
| 205 | if (!m_pending_gdb_server_socket_name) |
| 206 | return SendErrorResponse(error: 4); |
| 207 | |
| 208 | json::Object server{{.K: "port" , .V: m_pending_gdb_server_socket_name->empty() |
| 209 | ? m_gdbserver_port |
| 210 | : 0}}; |
| 211 | |
| 212 | if (!m_pending_gdb_server_socket_name->empty()) |
| 213 | server.try_emplace(K: "socket_name" , Args&: *m_pending_gdb_server_socket_name); |
| 214 | |
| 215 | json::Array server_list; |
| 216 | server_list.push_back(E: std::move(server)); |
| 217 | |
| 218 | StreamGDBRemote response; |
| 219 | response.AsRawOstream() << std::move(server_list); |
| 220 | |
| 221 | StreamGDBRemote escaped_response; |
| 222 | escaped_response.PutEscapedBytes(s: response.GetString().data(), |
| 223 | src_len: response.GetSize()); |
| 224 | return SendPacketNoLock(payload: escaped_response.GetString()); |
| 225 | } |
| 226 | |
| 227 | GDBRemoteCommunication::PacketResult |
| 228 | GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess( |
| 229 | StringExtractorGDBRemote &packet) { |
| 230 | packet.SetFilePos(::strlen(s: "qKillSpawnedProcess:" )); |
| 231 | |
| 232 | lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID); |
| 233 | |
| 234 | // verify that we know anything about this pid. |
| 235 | if (!SpawnedProcessIsRunning(pid)) { |
| 236 | // not a pid we know about |
| 237 | return SendErrorResponse(error: 10); |
| 238 | } |
| 239 | |
| 240 | // go ahead and attempt to kill the spawned process |
| 241 | if (KillSpawnedProcess(pid)) |
| 242 | return SendOKResponse(); |
| 243 | else |
| 244 | return SendErrorResponse(error: 11); |
| 245 | } |
| 246 | |
| 247 | void GDBRemoteCommunicationServerPlatform::AddSpawnedProcess(lldb::pid_t pid) { |
| 248 | assert(pid != LLDB_INVALID_PROCESS_ID); |
| 249 | std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
| 250 | m_spawned_pids.insert(x: pid); |
| 251 | } |
| 252 | |
| 253 | bool GDBRemoteCommunicationServerPlatform::SpawnedProcessIsRunning( |
| 254 | lldb::pid_t pid) { |
| 255 | std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
| 256 | return (m_spawned_pids.find(x: pid) != m_spawned_pids.end()); |
| 257 | } |
| 258 | |
| 259 | bool GDBRemoteCommunicationServerPlatform::KillSpawnedProcess(lldb::pid_t pid) { |
| 260 | // make sure we know about this process |
| 261 | if (!SpawnedProcessIsRunning(pid)) { |
| 262 | // it seems the process has been finished recently |
| 263 | return true; |
| 264 | } |
| 265 | |
| 266 | // first try a SIGTERM (standard kill) |
| 267 | Host::Kill(pid, SIGTERM); |
| 268 | |
| 269 | // check if that worked |
| 270 | for (size_t i = 0; i < 10; ++i) { |
| 271 | if (!SpawnedProcessIsRunning(pid)) { |
| 272 | // it is now killed |
| 273 | return true; |
| 274 | } |
| 275 | std::this_thread::sleep_for(rtime: std::chrono::milliseconds(10)); |
| 276 | } |
| 277 | |
| 278 | if (!SpawnedProcessIsRunning(pid)) |
| 279 | return true; |
| 280 | |
| 281 | // the launched process still lives. Now try killing it again, this time |
| 282 | // with an unblockable signal. |
| 283 | Host::Kill(pid, SIGKILL); |
| 284 | |
| 285 | for (size_t i = 0; i < 10; ++i) { |
| 286 | if (!SpawnedProcessIsRunning(pid)) { |
| 287 | // it is now killed |
| 288 | return true; |
| 289 | } |
| 290 | std::this_thread::sleep_for(rtime: std::chrono::milliseconds(10)); |
| 291 | } |
| 292 | |
| 293 | // check one more time after the final sleep |
| 294 | return !SpawnedProcessIsRunning(pid); |
| 295 | } |
| 296 | |
| 297 | GDBRemoteCommunication::PacketResult |
| 298 | GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo( |
| 299 | StringExtractorGDBRemote &packet) { |
| 300 | lldb::pid_t pid = m_process_launch_info.GetProcessID(); |
| 301 | m_process_launch_info.Clear(); |
| 302 | |
| 303 | if (pid == LLDB_INVALID_PROCESS_ID) |
| 304 | return SendErrorResponse(error: 1); |
| 305 | |
| 306 | ProcessInstanceInfo proc_info; |
| 307 | if (!Host::GetProcessInfo(pid, proc_info)) |
| 308 | return SendErrorResponse(error: 1); |
| 309 | |
| 310 | StreamString response; |
| 311 | CreateProcessInfoResponse_DebugServerStyle(proc_info, response); |
| 312 | return SendPacketNoLock(payload: response.GetString()); |
| 313 | } |
| 314 | |
| 315 | GDBRemoteCommunication::PacketResult |
| 316 | GDBRemoteCommunicationServerPlatform::Handle_qPathComplete( |
| 317 | StringExtractorGDBRemote &packet) { |
| 318 | packet.SetFilePos(::strlen(s: "qPathComplete:" )); |
| 319 | const bool only_dir = (packet.GetHexMaxU32(little_endian: false, fail_value: 0) == 1); |
| 320 | if (packet.GetChar() != ',') |
| 321 | return SendErrorResponse(error: 85); |
| 322 | std::string path; |
| 323 | packet.GetHexByteString(str&: path); |
| 324 | |
| 325 | StringList matches; |
| 326 | StandardTildeExpressionResolver resolver; |
| 327 | if (only_dir) |
| 328 | CommandCompletions::DiskDirectories(partial_file_name: path, matches, Resolver&: resolver); |
| 329 | else |
| 330 | CommandCompletions::DiskFiles(partial_file_name: path, matches, Resolver&: resolver); |
| 331 | |
| 332 | StreamString response; |
| 333 | response.PutChar(ch: 'M'); |
| 334 | llvm::StringRef separator; |
| 335 | std::sort(first: matches.begin(), last: matches.end()); |
| 336 | for (const auto &match : matches) { |
| 337 | response << separator; |
| 338 | separator = "," ; |
| 339 | // encode result strings into hex bytes to avoid unexpected error caused by |
| 340 | // special characters like '$'. |
| 341 | response.PutStringAsRawHex8(s: match.c_str()); |
| 342 | } |
| 343 | |
| 344 | return SendPacketNoLock(payload: response.GetString()); |
| 345 | } |
| 346 | |
| 347 | GDBRemoteCommunication::PacketResult |
| 348 | GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir( |
| 349 | StringExtractorGDBRemote &packet) { |
| 350 | |
| 351 | llvm::SmallString<64> cwd; |
| 352 | if (std::error_code ec = llvm::sys::fs::current_path(result&: cwd)) |
| 353 | return SendErrorResponse(error: ec.value()); |
| 354 | |
| 355 | StreamString response; |
| 356 | response.PutBytesAsRawHex8(src: cwd.data(), src_len: cwd.size()); |
| 357 | return SendPacketNoLock(payload: response.GetString()); |
| 358 | } |
| 359 | |
| 360 | GDBRemoteCommunication::PacketResult |
| 361 | GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir( |
| 362 | StringExtractorGDBRemote &packet) { |
| 363 | packet.SetFilePos(::strlen(s: "QSetWorkingDir:" )); |
| 364 | std::string path; |
| 365 | packet.GetHexByteString(str&: path); |
| 366 | |
| 367 | if (std::error_code ec = llvm::sys::fs::set_current_path(path)) |
| 368 | return SendErrorResponse(error: ec.value()); |
| 369 | return SendOKResponse(); |
| 370 | } |
| 371 | |
| 372 | GDBRemoteCommunication::PacketResult |
| 373 | GDBRemoteCommunicationServerPlatform::Handle_qC( |
| 374 | StringExtractorGDBRemote &packet) { |
| 375 | // NOTE: lldb should now be using qProcessInfo for process IDs. This path |
| 376 | // here |
| 377 | // should not be used. It is reporting process id instead of thread id. The |
| 378 | // correct answer doesn't seem to make much sense for lldb-platform. |
| 379 | // CONSIDER: flip to "unsupported". |
| 380 | lldb::pid_t pid = m_process_launch_info.GetProcessID(); |
| 381 | |
| 382 | StreamString response; |
| 383 | response.Printf(format: "QC%" PRIx64, pid); |
| 384 | |
| 385 | // If we launch a process and this GDB server is acting as a platform, then |
| 386 | // we need to clear the process launch state so we can start launching |
| 387 | // another process. In order to launch a process a bunch or packets need to |
| 388 | // be sent: environment packets, working directory, disable ASLR, and many |
| 389 | // more settings. When we launch a process we then need to know when to clear |
| 390 | // this information. Currently we are selecting the 'qC' packet as that |
| 391 | // packet which seems to make the most sense. |
| 392 | if (pid != LLDB_INVALID_PROCESS_ID) { |
| 393 | m_process_launch_info.Clear(); |
| 394 | } |
| 395 | |
| 396 | return SendPacketNoLock(payload: response.GetString()); |
| 397 | } |
| 398 | |
| 399 | GDBRemoteCommunication::PacketResult |
| 400 | GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo( |
| 401 | StringExtractorGDBRemote &packet) { |
| 402 | StructuredData::Array signal_array; |
| 403 | |
| 404 | lldb::UnixSignalsSP signals = UnixSignals::CreateForHost(); |
| 405 | for (auto signo = signals->GetFirstSignalNumber(); |
| 406 | signo != LLDB_INVALID_SIGNAL_NUMBER; |
| 407 | signo = signals->GetNextSignalNumber(current_signal: signo)) { |
| 408 | auto dictionary = std::make_shared<StructuredData::Dictionary>(); |
| 409 | |
| 410 | dictionary->AddIntegerItem(key: "signo" , value: signo); |
| 411 | dictionary->AddStringItem(key: "name" , value: signals->GetSignalAsStringRef(signo)); |
| 412 | |
| 413 | bool suppress, stop, notify; |
| 414 | signals->GetSignalInfo(signo, should_suppress&: suppress, should_stop&: stop, should_notify&: notify); |
| 415 | dictionary->AddBooleanItem(key: "suppress" , value: suppress); |
| 416 | dictionary->AddBooleanItem(key: "stop" , value: stop); |
| 417 | dictionary->AddBooleanItem(key: "notify" , value: notify); |
| 418 | |
| 419 | signal_array.Push(item: dictionary); |
| 420 | } |
| 421 | |
| 422 | StreamString response; |
| 423 | signal_array.Dump(s&: response); |
| 424 | return SendPacketNoLock(payload: response.GetString()); |
| 425 | } |
| 426 | |
| 427 | void GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped( |
| 428 | lldb::pid_t pid) { |
| 429 | std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
| 430 | m_spawned_pids.erase(x: pid); |
| 431 | } |
| 432 | |
| 433 | Status GDBRemoteCommunicationServerPlatform::LaunchProcess() { |
| 434 | if (!m_process_launch_info.GetArguments().GetArgumentCount()) |
| 435 | return Status::FromErrorStringWithFormat( |
| 436 | format: "%s: no process command line specified to launch" , __FUNCTION__); |
| 437 | |
| 438 | // specify the process monitor if not already set. This should generally be |
| 439 | // what happens since we need to reap started processes. |
| 440 | if (!m_process_launch_info.GetMonitorProcessCallback()) |
| 441 | m_process_launch_info.SetMonitorProcessCallback(std::bind( |
| 442 | f: &GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, args: this, |
| 443 | args: std::placeholders::_1)); |
| 444 | |
| 445 | Status error = Host::LaunchProcess(launch_info&: m_process_launch_info); |
| 446 | if (!error.Success()) { |
| 447 | fprintf(stderr, format: "%s: failed to launch executable %s" , __FUNCTION__, |
| 448 | m_process_launch_info.GetArguments().GetArgumentAtIndex(idx: 0)); |
| 449 | return error; |
| 450 | } |
| 451 | |
| 452 | printf(format: "Launched '%s' as process %" PRIu64 "...\n" , |
| 453 | m_process_launch_info.GetArguments().GetArgumentAtIndex(idx: 0), |
| 454 | m_process_launch_info.GetProcessID()); |
| 455 | |
| 456 | // add to list of spawned processes. On an lldb-gdbserver, we would expect |
| 457 | // there to be only one. |
| 458 | const auto pid = m_process_launch_info.GetProcessID(); |
| 459 | AddSpawnedProcess(pid); |
| 460 | |
| 461 | return error; |
| 462 | } |
| 463 | |
| 464 | const FileSpec &GDBRemoteCommunicationServerPlatform::GetDomainSocketDir() { |
| 465 | static FileSpec g_domainsocket_dir; |
| 466 | static llvm::once_flag g_once_flag; |
| 467 | |
| 468 | llvm::call_once(flag&: g_once_flag, F: []() { |
| 469 | const char *domainsocket_dir_env = |
| 470 | ::getenv(name: "LLDB_DEBUGSERVER_DOMAINSOCKET_DIR" ); |
| 471 | if (domainsocket_dir_env != nullptr) |
| 472 | g_domainsocket_dir = FileSpec(domainsocket_dir_env); |
| 473 | else |
| 474 | g_domainsocket_dir = HostInfo::GetProcessTempDir(); |
| 475 | }); |
| 476 | |
| 477 | return g_domainsocket_dir; |
| 478 | } |
| 479 | |
| 480 | FileSpec |
| 481 | GDBRemoteCommunicationServerPlatform::GetDomainSocketPath(const char *prefix) { |
| 482 | llvm::SmallString<128> socket_path; |
| 483 | llvm::SmallString<128> socket_name( |
| 484 | (llvm::StringRef(prefix) + ".%%%%%%" ).str()); |
| 485 | |
| 486 | FileSpec socket_path_spec(GetDomainSocketDir()); |
| 487 | socket_path_spec.AppendPathComponent(component: socket_name.c_str()); |
| 488 | |
| 489 | llvm::sys::fs::createUniqueFile(Model: socket_path_spec.GetPath().c_str(), |
| 490 | ResultPath&: socket_path); |
| 491 | return FileSpec(socket_path.c_str()); |
| 492 | } |
| 493 | |
| 494 | void GDBRemoteCommunicationServerPlatform::SetPendingGdbServer( |
| 495 | const std::string &socket_name) { |
| 496 | m_pending_gdb_server_socket_name = socket_name; |
| 497 | } |
| 498 | |