blob: 6f137d09fee4fc96a9f879694877fead7d1b1ee0 [file] [log] [blame]
//===-- 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("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 = 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.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;
}