brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.1 KiB · 5876c3a Raw
498 lines · cpp
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-exception6//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 43using namespace lldb;44using namespace lldb_private::process_gdb_remote;45using namespace lldb_private;46 47// GDBRemoteCommunicationServerPlatform constructor48GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform(49    FileSpec debugserver_path, const Socket::SocketProtocol socket_protocol,50    uint16_t gdbserver_port)51    : m_debugserver_path(std::move(debugserver_path)),52      m_socket_protocol(socket_protocol), m_gdbserver_port(gdbserver_port) {53 54  RegisterMemberFunctionHandler(55      StringExtractorGDBRemote::eServerPacketType_qC,56      &GDBRemoteCommunicationServerPlatform::Handle_qC);57  RegisterMemberFunctionHandler(58      StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir,59      &GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir);60  RegisterMemberFunctionHandler(61      StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer,62      &GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer);63  RegisterMemberFunctionHandler(64      StringExtractorGDBRemote::eServerPacketType_qQueryGDBServer,65      &GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer);66  RegisterMemberFunctionHandler(67      StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess,68      &GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess);69  RegisterMemberFunctionHandler(70      StringExtractorGDBRemote::eServerPacketType_qProcessInfo,71      &GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo);72  RegisterMemberFunctionHandler(73      StringExtractorGDBRemote::eServerPacketType_qPathComplete,74      &GDBRemoteCommunicationServerPlatform::Handle_qPathComplete);75  RegisterMemberFunctionHandler(76      StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir,77      &GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir);78  RegisterMemberFunctionHandler(79      StringExtractorGDBRemote::eServerPacketType_jSignalsInfo,80      &GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo);81 82  RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_interrupt,83                        [](StringExtractorGDBRemote packet, Status &error,84                           bool &interrupt, bool &quit) {85                          error = Status::FromErrorString("interrupt received");86                          interrupt = true;87                          return PacketResult::Success;88                        });89}90 91// Destructor92GDBRemoteCommunicationServerPlatform::~GDBRemoteCommunicationServerPlatform() =93    default;94 95Status GDBRemoteCommunicationServerPlatform::LaunchGDBServer(96    const lldb_private::Args &args, lldb::pid_t &pid, std::string &socket_name,97    shared_fd_t fd) {98  Log *log = GetLog(LLDBLog::Platform);99 100  ProcessLaunchInfo debugserver_launch_info;101  // Do not run in a new session so that it can not linger after the platform102  // closes.103  debugserver_launch_info.SetLaunchInSeparateProcessGroup(false);104  debugserver_launch_info.SetMonitorProcessCallback(105      [](lldb::pid_t, int, int) {});106  if (!FileSystem::Instance().Exists(m_debugserver_path))107    return Status::FromErrorString("debugserver does not exist");108  debugserver_launch_info.SetExecutableFile(m_debugserver_path,109                                            /*add_exe_file_as_first_arg=*/true);110 111  Status error;112  if (fd == SharedSocket::kInvalidFD) {113    if (m_socket_protocol == Socket::ProtocolTcp) {114      // The server will be launched after accepting the connection.115      return Status();116    }117 118    std::ostringstream url;119    // debugserver does not accept the URL scheme prefix.120#if !defined(__APPLE__)121    url << Socket::FindSchemeByProtocol(m_socket_protocol) << "://";122#endif123    socket_name = GetDomainSocketPath("gdbserver").GetPath();124    url << socket_name;125    error = StartDebugserverProcess(url.str(), debugserver_launch_info, &args);126  } else {127    if (m_socket_protocol != Socket::ProtocolTcp)128      return Status::FromErrorString("protocol must be tcp");129    error = StartDebugserverProcess(fd, debugserver_launch_info, &args);130  }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 148GDBRemoteCommunication::PacketResult149GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer(150    StringExtractorGDBRemote &packet) {151  // Spawn a local debugserver as a platform so we can then attach or launch a152  // process...153 154  Log *log = GetLog(LLDBLog::Platform);155  LLDB_LOGF(log, "GDBRemoteCommunicationServerPlatform::%s() called",156            __FUNCTION__);157 158  ConnectionFileDescriptor file_conn;159  std::string hostname;160  packet.SetFilePos(::strlen("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 value169      port = 0;170      value.getAsInteger(0, *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(), debugserver_pid, socket_name,179                                 SharedSocket::kInvalidFD);180  if (error.Fail())181    return SendErrorResponse(9); // EBADF182 183  StreamGDBRemote response;184  uint16_t gdbserver_port = socket_name.empty() ? m_gdbserver_port : 0;185  response.Printf("pid:%" PRIu64 ";port:%u;", debugserver_pid, gdbserver_port);186  if (!socket_name.empty()) {187    response.PutCString("socket_name:");188    response.PutStringAsRawHex8(socket_name);189    response.PutChar(';');190  }191 192  PacketResult packet_result = SendPacketNoLock(response.GetString());193  if (packet_result != PacketResult::Success) {194    if (debugserver_pid != LLDB_INVALID_PROCESS_ID)195      Host::Kill(debugserver_pid, SIGINT);196  }197  return packet_result;198}199 200GDBRemoteCommunication::PacketResult201GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer(202    StringExtractorGDBRemote &packet) {203  namespace json = llvm::json;204 205  if (!m_pending_gdb_server_socket_name)206    return SendErrorResponse(4);207 208  json::Object server{{"port", m_pending_gdb_server_socket_name->empty()209                                   ? m_gdbserver_port210                                   : 0}};211 212  if (!m_pending_gdb_server_socket_name->empty())213    server.try_emplace("socket_name", *m_pending_gdb_server_socket_name);214 215  json::Array server_list;216  server_list.push_back(std::move(server));217 218  StreamGDBRemote response;219  response.AsRawOstream() << std::move(server_list);220 221  StreamGDBRemote escaped_response;222  escaped_response.PutEscapedBytes(response.GetString().data(),223                                   response.GetSize());224  return SendPacketNoLock(escaped_response.GetString());225}226 227GDBRemoteCommunication::PacketResult228GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess(229    StringExtractorGDBRemote &packet) {230  packet.SetFilePos(::strlen("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 about237    return SendErrorResponse(10);238  }239 240  // go ahead and attempt to kill the spawned process241  if (KillSpawnedProcess(pid))242    return SendOKResponse();243  else244    return SendErrorResponse(11);245}246 247void 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(pid);251}252 253bool 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(pid) != m_spawned_pids.end());257}258 259bool GDBRemoteCommunicationServerPlatform::KillSpawnedProcess(lldb::pid_t pid) {260  // make sure we know about this process261  if (!SpawnedProcessIsRunning(pid)) {262    // it seems the process has been finished recently263    return true;264  }265 266  // first try a SIGTERM (standard kill)267  Host::Kill(pid, SIGTERM);268 269  // check if that worked270  for (size_t i = 0; i < 10; ++i) {271    if (!SpawnedProcessIsRunning(pid)) {272      // it is now killed273      return true;274    }275    std::this_thread::sleep_for(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 time282  // 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 killed288      return true;289    }290    std::this_thread::sleep_for(std::chrono::milliseconds(10));291  }292 293  // check one more time after the final sleep294  return !SpawnedProcessIsRunning(pid);295}296 297GDBRemoteCommunication::PacketResult298GDBRemoteCommunicationServerPlatform::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(1);305 306  ProcessInstanceInfo proc_info;307  if (!Host::GetProcessInfo(pid, proc_info))308    return SendErrorResponse(1);309 310  StreamString response;311  CreateProcessInfoResponse_DebugServerStyle(proc_info, response);312  return SendPacketNoLock(response.GetString());313}314 315GDBRemoteCommunication::PacketResult316GDBRemoteCommunicationServerPlatform::Handle_qPathComplete(317    StringExtractorGDBRemote &packet) {318  packet.SetFilePos(::strlen("qPathComplete:"));319  const bool only_dir = (packet.GetHexMaxU32(false, 0) == 1);320  if (packet.GetChar() != ',')321    return SendErrorResponse(85);322  std::string path;323  packet.GetHexByteString(path);324 325  StringList matches;326  StandardTildeExpressionResolver resolver;327  if (only_dir)328    CommandCompletions::DiskDirectories(path, matches, resolver);329  else330    CommandCompletions::DiskFiles(path, matches, resolver);331 332  StreamString response;333  response.PutChar('M');334  llvm::StringRef separator;335  std::sort(matches.begin(), 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 by340    // special characters like '$'.341    response.PutStringAsRawHex8(match.c_str());342  }343 344  return SendPacketNoLock(response.GetString());345}346 347GDBRemoteCommunication::PacketResult348GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir(349    StringExtractorGDBRemote &packet) {350 351  llvm::SmallString<64> cwd;352  if (std::error_code ec = llvm::sys::fs::current_path(cwd))353    return SendErrorResponse(ec.value());354 355  StreamString response;356  response.PutBytesAsRawHex8(cwd.data(), cwd.size());357  return SendPacketNoLock(response.GetString());358}359 360GDBRemoteCommunication::PacketResult361GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir(362    StringExtractorGDBRemote &packet) {363  packet.SetFilePos(::strlen("QSetWorkingDir:"));364  std::string path;365  packet.GetHexByteString(path);366 367  if (std::error_code ec = llvm::sys::fs::set_current_path(path))368    return SendErrorResponse(ec.value());369  return SendOKResponse();370}371 372GDBRemoteCommunication::PacketResult373GDBRemoteCommunicationServerPlatform::Handle_qC(374    StringExtractorGDBRemote &packet) {375  // NOTE: lldb should now be using qProcessInfo for process IDs.  This path376  // here377  // should not be used.  It is reporting process id instead of thread id.  The378  // 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("QC%" PRIx64, pid);384 385  // If we launch a process and this GDB server is acting as a platform, then386  // we need to clear the process launch state so we can start launching387  // another process. In order to launch a process a bunch or packets need to388  // be sent: environment packets, working directory, disable ASLR, and many389  // more settings. When we launch a process we then need to know when to clear390  // this information. Currently we are selecting the 'qC' packet as that391  // 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(response.GetString());397}398 399GDBRemoteCommunication::PacketResult400GDBRemoteCommunicationServerPlatform::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(signo)) {408    auto dictionary = std::make_shared<StructuredData::Dictionary>();409 410    dictionary->AddIntegerItem("signo", signo);411    dictionary->AddStringItem("name", signals->GetSignalAsStringRef(signo));412 413    bool suppress, stop, notify;414    signals->GetSignalInfo(signo, suppress, stop, notify);415    dictionary->AddBooleanItem("suppress", suppress);416    dictionary->AddBooleanItem("stop", stop);417    dictionary->AddBooleanItem("notify", notify);418 419    signal_array.Push(dictionary);420  }421 422  StreamString response;423  signal_array.Dump(response);424  return SendPacketNoLock(response.GetString());425}426 427void GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped(428    lldb::pid_t pid) {429  std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);430  m_spawned_pids.erase(pid);431}432 433Status GDBRemoteCommunicationServerPlatform::LaunchProcess() {434  if (!m_process_launch_info.GetArguments().GetArgumentCount())435    return Status::FromErrorStringWithFormat(436        "%s: no process command line specified to launch", __FUNCTION__);437 438  // specify the process monitor if not already set.  This should generally be439  // 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        &GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, this,443        std::placeholders::_1));444 445  Status error = Host::LaunchProcess(m_process_launch_info);446  if (!error.Success()) {447    fprintf(stderr, "%s: failed to launch executable %s", __FUNCTION__,448            m_process_launch_info.GetArguments().GetArgumentAtIndex(0));449    return error;450  }451 452  printf("Launched '%s' as process %" PRIu64 "...\n",453         m_process_launch_info.GetArguments().GetArgumentAtIndex(0),454         m_process_launch_info.GetProcessID());455 456  // add to list of spawned processes.  On an lldb-gdbserver, we would expect457  // there to be only one.458  const auto pid = m_process_launch_info.GetProcessID();459  AddSpawnedProcess(pid);460 461  return error;462}463 464const FileSpec &GDBRemoteCommunicationServerPlatform::GetDomainSocketDir() {465  static FileSpec g_domainsocket_dir;466  static llvm::once_flag g_once_flag;467 468  llvm::call_once(g_once_flag, []() {469    const char *domainsocket_dir_env =470        ::getenv("LLDB_DEBUGSERVER_DOMAINSOCKET_DIR");471    if (domainsocket_dir_env != nullptr)472      g_domainsocket_dir = FileSpec(domainsocket_dir_env);473    else474      g_domainsocket_dir = HostInfo::GetProcessTempDir();475  });476 477  return g_domainsocket_dir;478}479 480FileSpec481GDBRemoteCommunicationServerPlatform::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(socket_name.c_str());488 489  llvm::sys::fs::createUniqueFile(socket_path_spec.GetPath().c_str(),490                                  socket_path);491  return FileSpec(socket_path.c_str());492}493 494void GDBRemoteCommunicationServerPlatform::SetPendingGdbServer(495    const std::string &socket_name) {496  m_pending_gdb_server_socket_name = socket_name;497}498