brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.5 KiB · 24c0ca2 Raw
178 lines · cpp
1//===-- AttachRequestHandler.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 "DAP.h"10#include "EventHelper.h"11#include "JSONUtils.h"12#include "LLDBUtils.h"13#include "Protocol/ProtocolRequests.h"14#include "RequestHandler.h"15#include "lldb/API/SBAttachInfo.h"16#include "lldb/API/SBListener.h"17#include "lldb/lldb-defines.h"18#include "llvm/Support/Error.h"19#include "llvm/Support/FileSystem.h"20#include <cstdint>21 22using namespace llvm;23using namespace lldb_dap::protocol;24 25namespace lldb_dap {26 27/// The `attach` request is sent from the client to the debug adapter to attach28/// to a debuggee that is already running.29///30/// Since attaching is debugger/runtime specific, the arguments for this request31/// are not part of this specification.32Error AttachRequestHandler::Run(const AttachRequestArguments &args) const {33  // Initialize DAP debugger and related components if not sharing previously34  // launched debugger.35  std::optional<int> debugger_id = args.debuggerId;36  std::optional<lldb::user_id_t> target_id = args.targetId;37 38  // Validate that both debugger_id and target_id are provided together.39  if (debugger_id.has_value() != target_id.has_value()) {40    return llvm::createStringError(41        "Both debuggerId and targetId must be specified together for debugger "42        "reuse, or both must be omitted to create a new debugger");43  }44 45  if (Error err = debugger_id && target_id46                      ? dap.InitializeDebugger(*debugger_id, *target_id)47                      : dap.InitializeDebugger())48    return err;49 50  // Validate that we have a well formed attach request.51  if (args.attachCommands.empty() && args.coreFile.empty() &&52      args.configuration.program.empty() &&53      args.pid == LLDB_INVALID_PROCESS_ID &&54      args.gdbRemotePort == LLDB_DAP_INVALID_PORT && !target_id.has_value())55    return make_error<DAPError>(56        "expected one of 'pid', 'program', 'attachCommands', "57        "'coreFile', 'gdb-remote-port', or target_id to be specified");58 59  // Check if we have mutually exclusive arguments.60  if ((args.pid != LLDB_INVALID_PROCESS_ID) &&61      (args.gdbRemotePort != LLDB_DAP_INVALID_PORT))62    return make_error<DAPError>(63        "'pid' and 'gdb-remote-port' are mutually exclusive");64 65  dap.SetConfiguration(args.configuration, /*is_attach=*/true);66  if (!args.coreFile.empty())67    dap.stop_at_entry = true;68 69  PrintWelcomeMessage();70 71  // This is a hack for loading DWARF in .o files on Mac where the .o files72  // in the debug map of the main executable have relative paths which73  // require the lldb-dap binary to have its working directory set to that74  // relative root for the .o files in order to be able to load debug info.75  if (!dap.configuration.debuggerRoot.empty())76    sys::fs::set_current_path(dap.configuration.debuggerRoot);77 78  // Run any initialize LLDB commands the user specified in the launch.json79  if (llvm::Error err = dap.RunInitCommands())80    return err;81 82  dap.ConfigureSourceMaps();83 84  lldb::SBError error;85  lldb::SBTarget target;86  if (target_id) {87    // Use the unique target ID to get the target.88    target = dap.debugger.FindTargetByGloballyUniqueID(*target_id);89    if (!target.IsValid()) {90      error.SetErrorStringWithFormat("invalid target_id %lu in attach config",91                                     *target_id);92    }93  } else {94    target = dap.CreateTarget(error);95  }96 97  if (error.Fail())98    return ToError(error);99 100  dap.SetTarget(target);101 102  // Run any pre run LLDB commands the user specified in the launch.json103  if (Error err = dap.RunPreRunCommands())104    return err;105 106  if ((args.pid == LLDB_INVALID_PROCESS_ID ||107       args.gdbRemotePort == LLDB_DAP_INVALID_PORT) &&108      args.waitFor) {109    dap.SendOutput(OutputType::Console,110                   llvm::formatv("Waiting to attach to \"{0}\"...",111                                 dap.target.GetExecutable().GetFilename())112                       .str());113  }114 115  {116    // Perform the launch in synchronous mode so that we don't have to worry117    // about process state changes during the launch.118    ScopeSyncMode scope_sync_mode(dap.debugger);119 120    if (!args.attachCommands.empty()) {121      // Run the attach commands, after which we expect the debugger's selected122      // target to contain a valid and stopped process. Otherwise inform the123      // user that their command failed or the debugger is in an unexpected124      // state.125      if (llvm::Error err = dap.RunAttachCommands(args.attachCommands))126        return err;127 128      dap.target = dap.debugger.GetSelectedTarget();129 130      // Validate the attachCommand results.131      if (!dap.target.GetProcess().IsValid())132        return make_error<DAPError>(133            "attachCommands failed to attach to a process");134    } else if (!args.coreFile.empty()) {135      dap.target.LoadCore(args.coreFile.data(), error);136    } else if (args.gdbRemotePort != LLDB_DAP_INVALID_PORT) {137      lldb::SBListener listener = dap.debugger.GetListener();138 139      // If the user hasn't provided the hostname property, default140      // localhost being used.141      std::string connect_url =142          llvm::formatv("connect://{0}:", args.gdbRemoteHostname);143      connect_url += std::to_string(args.gdbRemotePort);144      dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",145                               error);146    } else if (!target_id.has_value()) {147      // Attach by pid or process name.148      lldb::SBAttachInfo attach_info;149      if (args.pid != LLDB_INVALID_PROCESS_ID)150        attach_info.SetProcessID(args.pid);151      else if (!dap.configuration.program.empty())152        attach_info.SetExecutable(dap.configuration.program.data());153      attach_info.SetWaitForLaunch(args.waitFor, /*async=*/false);154      dap.target.Attach(attach_info, error);155    }156    if (error.Fail())157      return ToError(error);158  }159 160  // Make sure the process is attached and stopped.161  error = dap.WaitForProcessToStop(args.configuration.timeout);162  if (error.Fail())163    return ToError(error);164 165  if (args.coreFile.empty() && !dap.target.GetProcess().IsValid())166    return make_error<DAPError>("failed to attach to process");167 168  dap.RunPostRunCommands();169 170  return Error::success();171}172 173void AttachRequestHandler::PostRun() const {174  dap.SendJSON(CreateEventObject("initialized"));175}176 177} // namespace lldb_dap178