604 lines
20 KiB
C++
604 lines
20 KiB
C++
//===-- 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/LLDBLog.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(),
|
|
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 = GetLog(LLDBLog::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));
|
|
|
|
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) {
|
|
std::string platform_uri = GetConnection()->GetURI();
|
|
llvm::Optional<URI> parsed_uri = URI::Parse(platform_uri);
|
|
url << '[' << parsed_uri->hostname.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 = GetLog(LLDBLog::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);
|
|
}
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
void 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);
|
|
}
|
|
|
|
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));
|
|
|
|
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.GetPath().c_str(),
|
|
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;
|
|
}
|