| //===-- GDBRemoteCommunicationServerPlatform.cpp --------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "GDBRemoteCommunicationServerPlatform.h" |
| |
| #include <cerrno> |
| |
| #include <chrono> |
| #include <csignal> |
| #include <cstring> |
| #include <mutex> |
| #include <sstream> |
| #include <thread> |
| |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/JSON.h" |
| #include "llvm/Support/Threading.h" |
| |
| #include "lldb/Host/Config.h" |
| #include "lldb/Host/ConnectionFileDescriptor.h" |
| #include "lldb/Host/FileAction.h" |
| #include "lldb/Host/Host.h" |
| #include "lldb/Host/HostInfo.h" |
| #include "lldb/Interpreter/CommandCompletions.h" |
| #include "lldb/Target/Platform.h" |
| #include "lldb/Target/UnixSignals.h" |
| #include "lldb/Utility/GDBRemote.h" |
| #include "lldb/Utility/Log.h" |
| #include "lldb/Utility/StreamString.h" |
| #include "lldb/Utility/StructuredData.h" |
| #include "lldb/Utility/TildeExpressionResolver.h" |
| #include "lldb/Utility/UriParser.h" |
| |
| #include "lldb/Utility/StringExtractorGDBRemote.h" |
| |
| using namespace lldb; |
| using namespace lldb_private::process_gdb_remote; |
| using namespace lldb_private; |
| |
| GDBRemoteCommunicationServerPlatform::PortMap::PortMap(uint16_t min_port, |
| uint16_t max_port) { |
| for (; min_port < max_port; ++min_port) |
| m_port_map[min_port] = LLDB_INVALID_PROCESS_ID; |
| } |
| |
| void GDBRemoteCommunicationServerPlatform::PortMap::AllowPort(uint16_t port) { |
| // Do not modify existing mappings |
| m_port_map.insert({port, LLDB_INVALID_PROCESS_ID}); |
| } |
| |
| llvm::Expected<uint16_t> |
| GDBRemoteCommunicationServerPlatform::PortMap::GetNextAvailablePort() { |
| if (m_port_map.empty()) |
| return 0; // Bind to port zero and get a port, we didn't have any |
| // limitations |
| |
| for (auto &pair : m_port_map) { |
| if (pair.second == LLDB_INVALID_PROCESS_ID) { |
| pair.second = ~(lldb::pid_t)LLDB_INVALID_PROCESS_ID; |
| return pair.first; |
| } |
| } |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), |
| "No free port found in port map"); |
| } |
| |
| bool GDBRemoteCommunicationServerPlatform::PortMap::AssociatePortWithProcess( |
| uint16_t port, lldb::pid_t pid) { |
| auto pos = m_port_map.find(port); |
| if (pos != m_port_map.end()) { |
| pos->second = pid; |
| return true; |
| } |
| return false; |
| } |
| |
| bool GDBRemoteCommunicationServerPlatform::PortMap::FreePort(uint16_t port) { |
| std::map<uint16_t, lldb::pid_t>::iterator pos = m_port_map.find(port); |
| if (pos != m_port_map.end()) { |
| pos->second = LLDB_INVALID_PROCESS_ID; |
| return true; |
| } |
| return false; |
| } |
| |
| bool GDBRemoteCommunicationServerPlatform::PortMap::FreePortForProcess( |
| lldb::pid_t pid) { |
| if (!m_port_map.empty()) { |
| for (auto &pair : m_port_map) { |
| if (pair.second == pid) { |
| pair.second = LLDB_INVALID_PROCESS_ID; |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool GDBRemoteCommunicationServerPlatform::PortMap::empty() const { |
| return m_port_map.empty(); |
| } |
| |
| // GDBRemoteCommunicationServerPlatform constructor |
| GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform( |
| const Socket::SocketProtocol socket_protocol, const char *socket_scheme) |
| : GDBRemoteCommunicationServerCommon("gdb-remote.server", |
| "gdb-remote.server.rx_packet"), |
| m_socket_protocol(socket_protocol), m_socket_scheme(socket_scheme), |
| m_spawned_pids_mutex(), m_port_map(), m_port_offset(0) { |
| m_pending_gdb_server.pid = LLDB_INVALID_PROCESS_ID; |
| m_pending_gdb_server.port = 0; |
| |
| RegisterMemberFunctionHandler( |
| StringExtractorGDBRemote::eServerPacketType_qC, |
| &GDBRemoteCommunicationServerPlatform::Handle_qC); |
| RegisterMemberFunctionHandler( |
| StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir, |
| &GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir); |
| RegisterMemberFunctionHandler( |
| StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer, |
| &GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer); |
| RegisterMemberFunctionHandler( |
| StringExtractorGDBRemote::eServerPacketType_qQueryGDBServer, |
| &GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer); |
| RegisterMemberFunctionHandler( |
| StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess, |
| &GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess); |
| RegisterMemberFunctionHandler( |
| StringExtractorGDBRemote::eServerPacketType_qProcessInfo, |
| &GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo); |
| RegisterMemberFunctionHandler( |
| StringExtractorGDBRemote::eServerPacketType_qPathComplete, |
| &GDBRemoteCommunicationServerPlatform::Handle_qPathComplete); |
| RegisterMemberFunctionHandler( |
| StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir, |
| &GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir); |
| RegisterMemberFunctionHandler( |
| StringExtractorGDBRemote::eServerPacketType_jSignalsInfo, |
| &GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo); |
| |
| RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_interrupt, |
| [](StringExtractorGDBRemote packet, Status &error, |
| bool &interrupt, bool &quit) { |
| error.SetErrorString("interrupt received"); |
| interrupt = true; |
| return PacketResult::Success; |
| }); |
| } |
| |
| // Destructor |
| GDBRemoteCommunicationServerPlatform::~GDBRemoteCommunicationServerPlatform() = |
| default; |
| |
| Status GDBRemoteCommunicationServerPlatform::LaunchGDBServer( |
| const lldb_private::Args &args, std::string hostname, lldb::pid_t &pid, |
| llvm::Optional<uint16_t> &port, std::string &socket_name) { |
| if (!port) { |
| llvm::Expected<uint16_t> available_port = m_port_map.GetNextAvailablePort(); |
| if (available_port) |
| port = *available_port; |
| else |
| return Status(available_port.takeError()); |
| } |
| |
| // Spawn a new thread to accept the port that gets bound after binding to |
| // port 0 (zero). |
| |
| // ignore the hostname send from the remote end, just use the ip address that |
| // we're currently communicating with as the hostname |
| |
| // Spawn a debugserver and try to get the port it listens to. |
| ProcessLaunchInfo debugserver_launch_info; |
| if (hostname.empty()) |
| hostname = "127.0.0.1"; |
| |
| Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); |
| LLDB_LOGF(log, "Launching debugserver with: %s:%u...", hostname.c_str(), |
| *port); |
| |
| // Do not run in a new session so that it can not linger after the platform |
| // closes. |
| debugserver_launch_info.SetLaunchInSeparateProcessGroup(false); |
| debugserver_launch_info.SetMonitorProcessCallback( |
| std::bind(&GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, |
| this, std::placeholders::_1), |
| false); |
| |
| std::ostringstream url; |
| // debugserver does not accept the URL scheme prefix. |
| #if !defined(__APPLE__) |
| url << m_socket_scheme << "://"; |
| #endif |
| uint16_t *port_ptr = port.getPointer(); |
| if (m_socket_protocol == Socket::ProtocolTcp) { |
| llvm::StringRef platform_scheme; |
| llvm::StringRef platform_ip; |
| int platform_port; |
| llvm::StringRef platform_path; |
| std::string platform_uri = GetConnection()->GetURI(); |
| bool ok = UriParser::Parse(platform_uri, platform_scheme, platform_ip, |
| platform_port, platform_path); |
| UNUSED_IF_ASSERT_DISABLED(ok); |
| assert(ok); |
| url << '[' << platform_ip.str() << "]:" << *port; |
| } else { |
| socket_name = GetDomainSocketPath("gdbserver").GetPath(); |
| url << socket_name; |
| port_ptr = nullptr; |
| } |
| |
| Status error = StartDebugserverProcess( |
| url.str().c_str(), nullptr, debugserver_launch_info, port_ptr, &args, -1); |
| |
| pid = debugserver_launch_info.GetProcessID(); |
| if (pid != LLDB_INVALID_PROCESS_ID) { |
| std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
| m_spawned_pids.insert(pid); |
| if (*port > 0) |
| m_port_map.AssociatePortWithProcess(*port, pid); |
| } else { |
| if (*port > 0) |
| m_port_map.FreePort(*port); |
| } |
| return error; |
| } |
| |
| GDBRemoteCommunication::PacketResult |
| GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer( |
| StringExtractorGDBRemote &packet) { |
| // Spawn a local debugserver as a platform so we can then attach or launch a |
| // process... |
| |
| Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); |
| LLDB_LOGF(log, "GDBRemoteCommunicationServerPlatform::%s() called", |
| __FUNCTION__); |
| |
| ConnectionFileDescriptor file_conn; |
| std::string hostname; |
| packet.SetFilePos(::strlen("qLaunchGDBServer;")); |
| llvm::StringRef name; |
| llvm::StringRef value; |
| llvm::Optional<uint16_t> port; |
| while (packet.GetNameColonValue(name, value)) { |
| if (name.equals("host")) |
| hostname = std::string(value); |
| else if (name.equals("port")) { |
| // Make the Optional valid so we can use its value |
| port = 0; |
| value.getAsInteger(0, port.getValue()); |
| } |
| } |
| |
| lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; |
| std::string socket_name; |
| Status error = |
| LaunchGDBServer(Args(), hostname, debugserver_pid, port, socket_name); |
| if (error.Fail()) { |
| LLDB_LOGF(log, |
| "GDBRemoteCommunicationServerPlatform::%s() debugserver " |
| "launch failed: %s", |
| __FUNCTION__, error.AsCString()); |
| return SendErrorResponse(9); |
| } |
| |
| LLDB_LOGF(log, |
| "GDBRemoteCommunicationServerPlatform::%s() debugserver " |
| "launched successfully as pid %" PRIu64, |
| __FUNCTION__, debugserver_pid); |
| |
| StreamGDBRemote response; |
| assert(port); |
| response.Printf("pid:%" PRIu64 ";port:%u;", debugserver_pid, |
| *port + m_port_offset); |
| if (!socket_name.empty()) { |
| response.PutCString("socket_name:"); |
| response.PutStringAsRawHex8(socket_name); |
| response.PutChar(';'); |
| } |
| |
| PacketResult packet_result = SendPacketNoLock(response.GetString()); |
| if (packet_result != PacketResult::Success) { |
| if (debugserver_pid != LLDB_INVALID_PROCESS_ID) |
| Host::Kill(debugserver_pid, SIGINT); |
| } |
| return packet_result; |
| } |
| |
| GDBRemoteCommunication::PacketResult |
| GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer( |
| StringExtractorGDBRemote &packet) { |
| namespace json = llvm::json; |
| |
| if (m_pending_gdb_server.pid == LLDB_INVALID_PROCESS_ID) |
| return SendErrorResponse(4); |
| |
| json::Object server{{"port", m_pending_gdb_server.port}}; |
| |
| if (!m_pending_gdb_server.socket_name.empty()) |
| server.try_emplace("socket_name", m_pending_gdb_server.socket_name); |
| |
| json::Array server_list; |
| server_list.push_back(std::move(server)); |
| |
| StreamGDBRemote response; |
| response.AsRawOstream() << std::move(server_list); |
| |
| StreamGDBRemote escaped_response; |
| escaped_response.PutEscapedBytes(response.GetString().data(), |
| response.GetSize()); |
| return SendPacketNoLock(escaped_response.GetString()); |
| } |
| |
| GDBRemoteCommunication::PacketResult |
| GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess( |
| StringExtractorGDBRemote &packet) { |
| packet.SetFilePos(::strlen("qKillSpawnedProcess:")); |
| |
| lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID); |
| |
| // verify that we know anything about this pid. Scope for locker |
| { |
| std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
| if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { |
| // not a pid we know about |
| return SendErrorResponse(10); |
| } |
| } |
| |
| // go ahead and attempt to kill the spawned process |
| if (KillSpawnedProcess(pid)) |
| return SendOKResponse(); |
| else |
| return SendErrorResponse(11); |
| } |
| |
| bool GDBRemoteCommunicationServerPlatform::KillSpawnedProcess(lldb::pid_t pid) { |
| // make sure we know about this process |
| { |
| std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
| if (m_spawned_pids.find(pid) == m_spawned_pids.end()) |
| return false; |
| } |
| |
| // first try a SIGTERM (standard kill) |
| Host::Kill(pid, SIGTERM); |
| |
| // check if that worked |
| for (size_t i = 0; i < 10; ++i) { |
| { |
| std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
| if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { |
| // it is now killed |
| return true; |
| } |
| } |
| std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
| } |
| |
| { |
| std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
| if (m_spawned_pids.find(pid) == m_spawned_pids.end()) |
| return true; |
| } |
| |
| // the launched process still lives. Now try killing it again, this time |
| // with an unblockable signal. |
| Host::Kill(pid, SIGKILL); |
| |
| for (size_t i = 0; i < 10; ++i) { |
| { |
| std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
| if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { |
| // it is now killed |
| return true; |
| } |
| } |
| std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
| } |
| |
| // check one more time after the final sleep |
| { |
| std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
| if (m_spawned_pids.find(pid) == m_spawned_pids.end()) |
| return true; |
| } |
| |
| // no luck - the process still lives |
| return false; |
| } |
| |
| GDBRemoteCommunication::PacketResult |
| GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo( |
| StringExtractorGDBRemote &packet) { |
| lldb::pid_t pid = m_process_launch_info.GetProcessID(); |
| m_process_launch_info.Clear(); |
| |
| if (pid == LLDB_INVALID_PROCESS_ID) |
| return SendErrorResponse(1); |
| |
| ProcessInstanceInfo proc_info; |
| if (!Host::GetProcessInfo(pid, proc_info)) |
| return SendErrorResponse(1); |
| |
| StreamString response; |
| CreateProcessInfoResponse_DebugServerStyle(proc_info, response); |
| return SendPacketNoLock(response.GetString()); |
| } |
| |
| GDBRemoteCommunication::PacketResult |
| GDBRemoteCommunicationServerPlatform::Handle_qPathComplete( |
| StringExtractorGDBRemote &packet) { |
| packet.SetFilePos(::strlen("qPathComplete:")); |
| const bool only_dir = (packet.GetHexMaxU32(false, 0) == 1); |
| if (packet.GetChar() != ',') |
| return SendErrorResponse(85); |
| std::string path; |
| packet.GetHexByteString(path); |
| |
| StringList matches; |
| StandardTildeExpressionResolver resolver; |
| if (only_dir) |
| CommandCompletions::DiskDirectories(path, matches, resolver); |
| else |
| CommandCompletions::DiskFiles(path, matches, resolver); |
| |
| StreamString response; |
| response.PutChar('M'); |
| llvm::StringRef separator; |
| std::sort(matches.begin(), matches.end()); |
| for (const auto &match : matches) { |
| response << separator; |
| separator = ","; |
| // encode result strings into hex bytes to avoid unexpected error caused by |
| // special characters like '$'. |
| response.PutStringAsRawHex8(match.c_str()); |
| } |
| |
| return SendPacketNoLock(response.GetString()); |
| } |
| |
| GDBRemoteCommunication::PacketResult |
| GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir( |
| StringExtractorGDBRemote &packet) { |
| |
| llvm::SmallString<64> cwd; |
| if (std::error_code ec = llvm::sys::fs::current_path(cwd)) |
| return SendErrorResponse(ec.value()); |
| |
| StreamString response; |
| response.PutBytesAsRawHex8(cwd.data(), cwd.size()); |
| return SendPacketNoLock(response.GetString()); |
| } |
| |
| GDBRemoteCommunication::PacketResult |
| GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir( |
| StringExtractorGDBRemote &packet) { |
| packet.SetFilePos(::strlen("QSetWorkingDir:")); |
| std::string path; |
| packet.GetHexByteString(path); |
| |
| if (std::error_code ec = llvm::sys::fs::set_current_path(path)) |
| return SendErrorResponse(ec.value()); |
| return SendOKResponse(); |
| } |
| |
| GDBRemoteCommunication::PacketResult |
| GDBRemoteCommunicationServerPlatform::Handle_qC( |
| StringExtractorGDBRemote &packet) { |
| // NOTE: lldb should now be using qProcessInfo for process IDs. This path |
| // here |
| // should not be used. It is reporting process id instead of thread id. The |
| // correct answer doesn't seem to make much sense for lldb-platform. |
| // CONSIDER: flip to "unsupported". |
| lldb::pid_t pid = m_process_launch_info.GetProcessID(); |
| |
| StreamString response; |
| response.Printf("QC%" PRIx64, pid); |
| |
| // If we launch a process and this GDB server is acting as a platform, then |
| // we need to clear the process launch state so we can start launching |
| // another process. In order to launch a process a bunch or packets need to |
| // be sent: environment packets, working directory, disable ASLR, and many |
| // more settings. When we launch a process we then need to know when to clear |
| // this information. Currently we are selecting the 'qC' packet as that |
| // packet which seems to make the most sense. |
| if (pid != LLDB_INVALID_PROCESS_ID) { |
| m_process_launch_info.Clear(); |
| } |
| |
| return SendPacketNoLock(response.GetString()); |
| } |
| |
| GDBRemoteCommunication::PacketResult |
| GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo( |
| StringExtractorGDBRemote &packet) { |
| StructuredData::Array signal_array; |
| |
| lldb::UnixSignalsSP signals = UnixSignals::CreateForHost(); |
| for (auto signo = signals->GetFirstSignalNumber(); |
| signo != LLDB_INVALID_SIGNAL_NUMBER; |
| signo = signals->GetNextSignalNumber(signo)) { |
| auto dictionary = std::make_shared<StructuredData::Dictionary>(); |
| |
| dictionary->AddIntegerItem("signo", signo); |
| dictionary->AddStringItem("name", signals->GetSignalAsCString(signo)); |
| |
| bool suppress, stop, notify; |
| signals->GetSignalInfo(signo, suppress, stop, notify); |
| dictionary->AddBooleanItem("suppress", suppress); |
| dictionary->AddBooleanItem("stop", stop); |
| dictionary->AddBooleanItem("notify", notify); |
| |
| signal_array.Push(dictionary); |
| } |
| |
| StreamString response; |
| signal_array.Dump(response); |
| return SendPacketNoLock(response.GetString()); |
| } |
| |
| bool GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped( |
| lldb::pid_t pid) { |
| std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
| m_port_map.FreePortForProcess(pid); |
| m_spawned_pids.erase(pid); |
| return true; |
| } |
| |
| Status GDBRemoteCommunicationServerPlatform::LaunchProcess() { |
| if (!m_process_launch_info.GetArguments().GetArgumentCount()) |
| return Status("%s: no process command line specified to launch", |
| __FUNCTION__); |
| |
| // specify the process monitor if not already set. This should generally be |
| // what happens since we need to reap started processes. |
| if (!m_process_launch_info.GetMonitorProcessCallback()) |
| m_process_launch_info.SetMonitorProcessCallback( |
| std::bind( |
| &GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, |
| this, std::placeholders::_1), |
| false); |
| |
| Status error = Host::LaunchProcess(m_process_launch_info); |
| if (!error.Success()) { |
| fprintf(stderr, "%s: failed to launch executable %s", __FUNCTION__, |
| m_process_launch_info.GetArguments().GetArgumentAtIndex(0)); |
| return error; |
| } |
| |
| printf("Launched '%s' as process %" PRIu64 "...\n", |
| m_process_launch_info.GetArguments().GetArgumentAtIndex(0), |
| m_process_launch_info.GetProcessID()); |
| |
| // add to list of spawned processes. On an lldb-gdbserver, we would expect |
| // there to be only one. |
| const auto pid = m_process_launch_info.GetProcessID(); |
| if (pid != LLDB_INVALID_PROCESS_ID) { |
| // add to spawned pids |
| std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); |
| m_spawned_pids.insert(pid); |
| } |
| |
| return error; |
| } |
| |
| void GDBRemoteCommunicationServerPlatform::SetPortMap(PortMap &&port_map) { |
| m_port_map = port_map; |
| } |
| |
| const FileSpec &GDBRemoteCommunicationServerPlatform::GetDomainSocketDir() { |
| static FileSpec g_domainsocket_dir; |
| static llvm::once_flag g_once_flag; |
| |
| llvm::call_once(g_once_flag, []() { |
| const char *domainsocket_dir_env = |
| ::getenv("LLDB_DEBUGSERVER_DOMAINSOCKET_DIR"); |
| if (domainsocket_dir_env != nullptr) |
| g_domainsocket_dir = FileSpec(domainsocket_dir_env); |
| else |
| g_domainsocket_dir = HostInfo::GetProcessTempDir(); |
| }); |
| |
| return g_domainsocket_dir; |
| } |
| |
| FileSpec |
| GDBRemoteCommunicationServerPlatform::GetDomainSocketPath(const char *prefix) { |
| llvm::SmallString<128> socket_path; |
| llvm::SmallString<128> socket_name( |
| (llvm::StringRef(prefix) + ".%%%%%%").str()); |
| |
| FileSpec socket_path_spec(GetDomainSocketDir()); |
| socket_path_spec.AppendPathComponent(socket_name.c_str()); |
| |
| llvm::sys::fs::createUniqueFile(socket_path_spec.GetCString(), socket_path); |
| return FileSpec(socket_path.c_str()); |
| } |
| |
| void GDBRemoteCommunicationServerPlatform::SetPortOffset(uint16_t port_offset) { |
| m_port_offset = port_offset; |
| } |
| |
| void GDBRemoteCommunicationServerPlatform::SetPendingGdbServer( |
| lldb::pid_t pid, uint16_t port, const std::string &socket_name) { |
| m_pending_gdb_server.pid = pid; |
| m_pending_gdb_server.port = port; |
| m_pending_gdb_server.socket_name = socket_name; |
| } |