Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
39642 views
//===-- GDBRemoteCommunicationServerPlatform.cpp --------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "GDBRemoteCommunicationServerPlatform.h"910#include <cerrno>1112#include <chrono>13#include <csignal>14#include <cstring>15#include <mutex>16#include <optional>17#include <sstream>18#include <thread>1920#include "llvm/Support/FileSystem.h"21#include "llvm/Support/JSON.h"22#include "llvm/Support/Threading.h"2324#include "lldb/Host/Config.h"25#include "lldb/Host/ConnectionFileDescriptor.h"26#include "lldb/Host/FileAction.h"27#include "lldb/Host/Host.h"28#include "lldb/Host/HostInfo.h"29#include "lldb/Interpreter/CommandCompletions.h"30#include "lldb/Target/Platform.h"31#include "lldb/Target/UnixSignals.h"32#include "lldb/Utility/GDBRemote.h"33#include "lldb/Utility/LLDBLog.h"34#include "lldb/Utility/Log.h"35#include "lldb/Utility/StreamString.h"36#include "lldb/Utility/StructuredData.h"37#include "lldb/Utility/TildeExpressionResolver.h"38#include "lldb/Utility/UriParser.h"3940#include "lldb/Utility/StringExtractorGDBRemote.h"4142using namespace lldb;43using namespace lldb_private::process_gdb_remote;44using namespace lldb_private;4546GDBRemoteCommunicationServerPlatform::PortMap::PortMap(uint16_t min_port,47uint16_t max_port) {48assert(min_port);49for (; min_port < max_port; ++min_port)50m_port_map[min_port] = LLDB_INVALID_PROCESS_ID;51}5253void GDBRemoteCommunicationServerPlatform::PortMap::AllowPort(uint16_t port) {54assert(port);55// Do not modify existing mappings56m_port_map.insert({port, LLDB_INVALID_PROCESS_ID});57}5859llvm::Expected<uint16_t>60GDBRemoteCommunicationServerPlatform::PortMap::GetNextAvailablePort() {61if (m_port_map.empty())62return 0; // Bind to port zero and get a port, we didn't have any63// limitations6465for (auto &pair : m_port_map) {66if (pair.second == LLDB_INVALID_PROCESS_ID) {67pair.second = ~(lldb::pid_t)LLDB_INVALID_PROCESS_ID;68return pair.first;69}70}71return llvm::createStringError(llvm::inconvertibleErrorCode(),72"No free port found in port map");73}7475bool GDBRemoteCommunicationServerPlatform::PortMap::AssociatePortWithProcess(76uint16_t port, lldb::pid_t pid) {77auto pos = m_port_map.find(port);78if (pos != m_port_map.end()) {79pos->second = pid;80return true;81}82return false;83}8485bool GDBRemoteCommunicationServerPlatform::PortMap::FreePort(uint16_t port) {86std::map<uint16_t, lldb::pid_t>::iterator pos = m_port_map.find(port);87if (pos != m_port_map.end()) {88pos->second = LLDB_INVALID_PROCESS_ID;89return true;90}91return false;92}9394bool GDBRemoteCommunicationServerPlatform::PortMap::FreePortForProcess(95lldb::pid_t pid) {96if (!m_port_map.empty()) {97for (auto &pair : m_port_map) {98if (pair.second == pid) {99pair.second = LLDB_INVALID_PROCESS_ID;100return true;101}102}103}104return false;105}106107bool GDBRemoteCommunicationServerPlatform::PortMap::empty() const {108return m_port_map.empty();109}110111// GDBRemoteCommunicationServerPlatform constructor112GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform(113const Socket::SocketProtocol socket_protocol, const char *socket_scheme)114: GDBRemoteCommunicationServerCommon(),115m_socket_protocol(socket_protocol), m_socket_scheme(socket_scheme),116m_spawned_pids_mutex(), m_port_map(), m_port_offset(0) {117m_pending_gdb_server.pid = LLDB_INVALID_PROCESS_ID;118m_pending_gdb_server.port = 0;119120RegisterMemberFunctionHandler(121StringExtractorGDBRemote::eServerPacketType_qC,122&GDBRemoteCommunicationServerPlatform::Handle_qC);123RegisterMemberFunctionHandler(124StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir,125&GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir);126RegisterMemberFunctionHandler(127StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer,128&GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer);129RegisterMemberFunctionHandler(130StringExtractorGDBRemote::eServerPacketType_qQueryGDBServer,131&GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer);132RegisterMemberFunctionHandler(133StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess,134&GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess);135RegisterMemberFunctionHandler(136StringExtractorGDBRemote::eServerPacketType_qProcessInfo,137&GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo);138RegisterMemberFunctionHandler(139StringExtractorGDBRemote::eServerPacketType_qPathComplete,140&GDBRemoteCommunicationServerPlatform::Handle_qPathComplete);141RegisterMemberFunctionHandler(142StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir,143&GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir);144RegisterMemberFunctionHandler(145StringExtractorGDBRemote::eServerPacketType_jSignalsInfo,146&GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo);147148RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_interrupt,149[](StringExtractorGDBRemote packet, Status &error,150bool &interrupt, bool &quit) {151error.SetErrorString("interrupt received");152interrupt = true;153return PacketResult::Success;154});155}156157// Destructor158GDBRemoteCommunicationServerPlatform::~GDBRemoteCommunicationServerPlatform() =159default;160161Status GDBRemoteCommunicationServerPlatform::LaunchGDBServer(162const lldb_private::Args &args, std::string hostname, lldb::pid_t &pid,163std::optional<uint16_t> &port, std::string &socket_name) {164if (!port) {165llvm::Expected<uint16_t> available_port = m_port_map.GetNextAvailablePort();166if (available_port)167port = *available_port;168else169return Status(available_port.takeError());170}171172// Spawn a new thread to accept the port that gets bound after binding to173// port 0 (zero).174175// ignore the hostname send from the remote end, just use the ip address that176// we're currently communicating with as the hostname177178// Spawn a debugserver and try to get the port it listens to.179ProcessLaunchInfo debugserver_launch_info;180if (hostname.empty())181hostname = "127.0.0.1";182183Log *log = GetLog(LLDBLog::Platform);184LLDB_LOGF(log, "Launching debugserver with: %s:%u...", hostname.c_str(),185*port);186187// Do not run in a new session so that it can not linger after the platform188// closes.189debugserver_launch_info.SetLaunchInSeparateProcessGroup(false);190debugserver_launch_info.SetMonitorProcessCallback(191std::bind(&GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped,192this, std::placeholders::_1));193194std::ostringstream url;195// debugserver does not accept the URL scheme prefix.196#if !defined(__APPLE__)197url << m_socket_scheme << "://";198#endif199uint16_t *port_ptr = &*port;200if (m_socket_protocol == Socket::ProtocolTcp) {201std::string platform_uri = GetConnection()->GetURI();202std::optional<URI> parsed_uri = URI::Parse(platform_uri);203url << '[' << parsed_uri->hostname.str() << "]:" << *port;204} else {205socket_name = GetDomainSocketPath("gdbserver").GetPath();206url << socket_name;207port_ptr = nullptr;208}209210Status error = StartDebugserverProcess(211url.str().c_str(), nullptr, debugserver_launch_info, port_ptr, &args, -1);212213pid = debugserver_launch_info.GetProcessID();214if (pid != LLDB_INVALID_PROCESS_ID) {215std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);216m_spawned_pids.insert(pid);217if (*port > 0)218m_port_map.AssociatePortWithProcess(*port, pid);219} else {220if (*port > 0)221m_port_map.FreePort(*port);222}223return error;224}225226GDBRemoteCommunication::PacketResult227GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer(228StringExtractorGDBRemote &packet) {229// Spawn a local debugserver as a platform so we can then attach or launch a230// process...231232Log *log = GetLog(LLDBLog::Platform);233LLDB_LOGF(log, "GDBRemoteCommunicationServerPlatform::%s() called",234__FUNCTION__);235236ConnectionFileDescriptor file_conn;237std::string hostname;238packet.SetFilePos(::strlen("qLaunchGDBServer;"));239llvm::StringRef name;240llvm::StringRef value;241std::optional<uint16_t> port;242while (packet.GetNameColonValue(name, value)) {243if (name == "host")244hostname = std::string(value);245else if (name == "port") {246// Make the Optional valid so we can use its value247port = 0;248value.getAsInteger(0, *port);249}250}251252lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID;253std::string socket_name;254Status error =255LaunchGDBServer(Args(), hostname, debugserver_pid, port, socket_name);256if (error.Fail()) {257LLDB_LOGF(log,258"GDBRemoteCommunicationServerPlatform::%s() debugserver "259"launch failed: %s",260__FUNCTION__, error.AsCString());261return SendErrorResponse(9);262}263264LLDB_LOGF(log,265"GDBRemoteCommunicationServerPlatform::%s() debugserver "266"launched successfully as pid %" PRIu64,267__FUNCTION__, debugserver_pid);268269StreamGDBRemote response;270assert(port);271response.Printf("pid:%" PRIu64 ";port:%u;", debugserver_pid,272*port + m_port_offset);273if (!socket_name.empty()) {274response.PutCString("socket_name:");275response.PutStringAsRawHex8(socket_name);276response.PutChar(';');277}278279PacketResult packet_result = SendPacketNoLock(response.GetString());280if (packet_result != PacketResult::Success) {281if (debugserver_pid != LLDB_INVALID_PROCESS_ID)282Host::Kill(debugserver_pid, SIGINT);283}284return packet_result;285}286287GDBRemoteCommunication::PacketResult288GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer(289StringExtractorGDBRemote &packet) {290namespace json = llvm::json;291292if (m_pending_gdb_server.pid == LLDB_INVALID_PROCESS_ID)293return SendErrorResponse(4);294295json::Object server{{"port", m_pending_gdb_server.port}};296297if (!m_pending_gdb_server.socket_name.empty())298server.try_emplace("socket_name", m_pending_gdb_server.socket_name);299300json::Array server_list;301server_list.push_back(std::move(server));302303StreamGDBRemote response;304response.AsRawOstream() << std::move(server_list);305306StreamGDBRemote escaped_response;307escaped_response.PutEscapedBytes(response.GetString().data(),308response.GetSize());309return SendPacketNoLock(escaped_response.GetString());310}311312GDBRemoteCommunication::PacketResult313GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess(314StringExtractorGDBRemote &packet) {315packet.SetFilePos(::strlen("qKillSpawnedProcess:"));316317lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID);318319// verify that we know anything about this pid. Scope for locker320{321std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);322if (m_spawned_pids.find(pid) == m_spawned_pids.end()) {323// not a pid we know about324return SendErrorResponse(10);325}326}327328// go ahead and attempt to kill the spawned process329if (KillSpawnedProcess(pid))330return SendOKResponse();331else332return SendErrorResponse(11);333}334335bool GDBRemoteCommunicationServerPlatform::KillSpawnedProcess(lldb::pid_t pid) {336// make sure we know about this process337{338std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);339if (m_spawned_pids.find(pid) == m_spawned_pids.end())340return false;341}342343// first try a SIGTERM (standard kill)344Host::Kill(pid, SIGTERM);345346// check if that worked347for (size_t i = 0; i < 10; ++i) {348{349std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);350if (m_spawned_pids.find(pid) == m_spawned_pids.end()) {351// it is now killed352return true;353}354}355std::this_thread::sleep_for(std::chrono::milliseconds(10));356}357358{359std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);360if (m_spawned_pids.find(pid) == m_spawned_pids.end())361return true;362}363364// the launched process still lives. Now try killing it again, this time365// with an unblockable signal.366Host::Kill(pid, SIGKILL);367368for (size_t i = 0; i < 10; ++i) {369{370std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);371if (m_spawned_pids.find(pid) == m_spawned_pids.end()) {372// it is now killed373return true;374}375}376std::this_thread::sleep_for(std::chrono::milliseconds(10));377}378379// check one more time after the final sleep380{381std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);382if (m_spawned_pids.find(pid) == m_spawned_pids.end())383return true;384}385386// no luck - the process still lives387return false;388}389390GDBRemoteCommunication::PacketResult391GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo(392StringExtractorGDBRemote &packet) {393lldb::pid_t pid = m_process_launch_info.GetProcessID();394m_process_launch_info.Clear();395396if (pid == LLDB_INVALID_PROCESS_ID)397return SendErrorResponse(1);398399ProcessInstanceInfo proc_info;400if (!Host::GetProcessInfo(pid, proc_info))401return SendErrorResponse(1);402403StreamString response;404CreateProcessInfoResponse_DebugServerStyle(proc_info, response);405return SendPacketNoLock(response.GetString());406}407408GDBRemoteCommunication::PacketResult409GDBRemoteCommunicationServerPlatform::Handle_qPathComplete(410StringExtractorGDBRemote &packet) {411packet.SetFilePos(::strlen("qPathComplete:"));412const bool only_dir = (packet.GetHexMaxU32(false, 0) == 1);413if (packet.GetChar() != ',')414return SendErrorResponse(85);415std::string path;416packet.GetHexByteString(path);417418StringList matches;419StandardTildeExpressionResolver resolver;420if (only_dir)421CommandCompletions::DiskDirectories(path, matches, resolver);422else423CommandCompletions::DiskFiles(path, matches, resolver);424425StreamString response;426response.PutChar('M');427llvm::StringRef separator;428std::sort(matches.begin(), matches.end());429for (const auto &match : matches) {430response << separator;431separator = ",";432// encode result strings into hex bytes to avoid unexpected error caused by433// special characters like '$'.434response.PutStringAsRawHex8(match.c_str());435}436437return SendPacketNoLock(response.GetString());438}439440GDBRemoteCommunication::PacketResult441GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir(442StringExtractorGDBRemote &packet) {443444llvm::SmallString<64> cwd;445if (std::error_code ec = llvm::sys::fs::current_path(cwd))446return SendErrorResponse(ec.value());447448StreamString response;449response.PutBytesAsRawHex8(cwd.data(), cwd.size());450return SendPacketNoLock(response.GetString());451}452453GDBRemoteCommunication::PacketResult454GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir(455StringExtractorGDBRemote &packet) {456packet.SetFilePos(::strlen("QSetWorkingDir:"));457std::string path;458packet.GetHexByteString(path);459460if (std::error_code ec = llvm::sys::fs::set_current_path(path))461return SendErrorResponse(ec.value());462return SendOKResponse();463}464465GDBRemoteCommunication::PacketResult466GDBRemoteCommunicationServerPlatform::Handle_qC(467StringExtractorGDBRemote &packet) {468// NOTE: lldb should now be using qProcessInfo for process IDs. This path469// here470// should not be used. It is reporting process id instead of thread id. The471// correct answer doesn't seem to make much sense for lldb-platform.472// CONSIDER: flip to "unsupported".473lldb::pid_t pid = m_process_launch_info.GetProcessID();474475StreamString response;476response.Printf("QC%" PRIx64, pid);477478// If we launch a process and this GDB server is acting as a platform, then479// we need to clear the process launch state so we can start launching480// another process. In order to launch a process a bunch or packets need to481// be sent: environment packets, working directory, disable ASLR, and many482// more settings. When we launch a process we then need to know when to clear483// this information. Currently we are selecting the 'qC' packet as that484// packet which seems to make the most sense.485if (pid != LLDB_INVALID_PROCESS_ID) {486m_process_launch_info.Clear();487}488489return SendPacketNoLock(response.GetString());490}491492GDBRemoteCommunication::PacketResult493GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo(494StringExtractorGDBRemote &packet) {495StructuredData::Array signal_array;496497lldb::UnixSignalsSP signals = UnixSignals::CreateForHost();498for (auto signo = signals->GetFirstSignalNumber();499signo != LLDB_INVALID_SIGNAL_NUMBER;500signo = signals->GetNextSignalNumber(signo)) {501auto dictionary = std::make_shared<StructuredData::Dictionary>();502503dictionary->AddIntegerItem("signo", signo);504dictionary->AddStringItem("name", signals->GetSignalAsStringRef(signo));505506bool suppress, stop, notify;507signals->GetSignalInfo(signo, suppress, stop, notify);508dictionary->AddBooleanItem("suppress", suppress);509dictionary->AddBooleanItem("stop", stop);510dictionary->AddBooleanItem("notify", notify);511512signal_array.Push(dictionary);513}514515StreamString response;516signal_array.Dump(response);517return SendPacketNoLock(response.GetString());518}519520void GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped(521lldb::pid_t pid) {522std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);523m_port_map.FreePortForProcess(pid);524m_spawned_pids.erase(pid);525}526527Status GDBRemoteCommunicationServerPlatform::LaunchProcess() {528if (!m_process_launch_info.GetArguments().GetArgumentCount())529return Status("%s: no process command line specified to launch",530__FUNCTION__);531532// specify the process monitor if not already set. This should generally be533// what happens since we need to reap started processes.534if (!m_process_launch_info.GetMonitorProcessCallback())535m_process_launch_info.SetMonitorProcessCallback(std::bind(536&GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, this,537std::placeholders::_1));538539Status error = Host::LaunchProcess(m_process_launch_info);540if (!error.Success()) {541fprintf(stderr, "%s: failed to launch executable %s", __FUNCTION__,542m_process_launch_info.GetArguments().GetArgumentAtIndex(0));543return error;544}545546printf("Launched '%s' as process %" PRIu64 "...\n",547m_process_launch_info.GetArguments().GetArgumentAtIndex(0),548m_process_launch_info.GetProcessID());549550// add to list of spawned processes. On an lldb-gdbserver, we would expect551// there to be only one.552const auto pid = m_process_launch_info.GetProcessID();553if (pid != LLDB_INVALID_PROCESS_ID) {554// add to spawned pids555std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);556m_spawned_pids.insert(pid);557}558559return error;560}561562void GDBRemoteCommunicationServerPlatform::SetPortMap(PortMap &&port_map) {563m_port_map = std::move(port_map);564}565566const FileSpec &GDBRemoteCommunicationServerPlatform::GetDomainSocketDir() {567static FileSpec g_domainsocket_dir;568static llvm::once_flag g_once_flag;569570llvm::call_once(g_once_flag, []() {571const char *domainsocket_dir_env =572::getenv("LLDB_DEBUGSERVER_DOMAINSOCKET_DIR");573if (domainsocket_dir_env != nullptr)574g_domainsocket_dir = FileSpec(domainsocket_dir_env);575else576g_domainsocket_dir = HostInfo::GetProcessTempDir();577});578579return g_domainsocket_dir;580}581582FileSpec583GDBRemoteCommunicationServerPlatform::GetDomainSocketPath(const char *prefix) {584llvm::SmallString<128> socket_path;585llvm::SmallString<128> socket_name(586(llvm::StringRef(prefix) + ".%%%%%%").str());587588FileSpec socket_path_spec(GetDomainSocketDir());589socket_path_spec.AppendPathComponent(socket_name.c_str());590591llvm::sys::fs::createUniqueFile(socket_path_spec.GetPath().c_str(),592socket_path);593return FileSpec(socket_path.c_str());594}595596void GDBRemoteCommunicationServerPlatform::SetPortOffset(uint16_t port_offset) {597m_port_offset = port_offset;598}599600void GDBRemoteCommunicationServerPlatform::SetPendingGdbServer(601lldb::pid_t pid, uint16_t port, const std::string &socket_name) {602m_pending_gdb_server.pid = pid;603m_pending_gdb_server.port = port;604m_pending_gdb_server.socket_name = socket_name;605}606607608