brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.3 KiB · d67437a Raw
272 lines · cpp
1//===-- RequestHandler.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 "Handler/RequestHandler.h"10#include "DAP.h"11#include "EventHelper.h"12#include "Handler/ResponseHandler.h"13#include "JSONUtils.h"14#include "LLDBUtils.h"15#include "Protocol/ProtocolBase.h"16#include "Protocol/ProtocolRequests.h"17#include "RunInTerminal.h"18#include "lldb/API/SBDefines.h"19#include "lldb/API/SBEnvironment.h"20#include "llvm/Support/Error.h"21#include <mutex>22 23#if !defined(_WIN32)24#include <unistd.h>25#endif26 27using namespace lldb_dap::protocol;28 29namespace lldb_dap {30 31static std::vector<const char *>32MakeArgv(const llvm::ArrayRef<std::string> &strs) {33  // Create and return an array of "const char *", one for each C string in34  // "strs" and terminate the list with a NULL. This can be used for argument35  // vectors (argv) or environment vectors (envp) like those passed to the36  // "main" function in C programs.37  std::vector<const char *> argv;38  for (const auto &s : strs)39    argv.push_back(s.c_str());40  argv.push_back(nullptr);41  return argv;42}43 44static uint32_t SetLaunchFlag(uint32_t flags, bool flag,45                              lldb::LaunchFlags mask) {46  if (flag)47    flags |= mask;48  else49    flags &= ~mask;50 51  return flags;52}53 54static void55SetupIORedirection(const std::vector<std::optional<std::string>> &stdio,56                   lldb::SBLaunchInfo &launch_info) {57  size_t n = std::max(stdio.size(), static_cast<size_t>(3));58  for (size_t i = 0; i < n; i++) {59    std::optional<std::string> path;60    if (stdio.size() <= i)61      path = stdio.back();62    else63      path = stdio[i];64    if (!path)65      continue;66    switch (i) {67    case 0:68      launch_info.AddOpenFileAction(i, path->c_str(), true, false);69      break;70    case 1:71    case 2:72      launch_info.AddOpenFileAction(i, path->c_str(), false, true);73      break;74    default:75      launch_info.AddOpenFileAction(i, path->c_str(), true, true);76      break;77    }78  }79}80 81static llvm::Error82RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {83  if (!dap.clientFeatures.contains(84          protocol::eClientFeatureRunInTerminalRequest))85    return llvm::make_error<DAPError>("Cannot use runInTerminal, feature is "86                                      "not supported by the connected client");87 88  if (arguments.configuration.program.empty())89    return llvm::make_error<DAPError>(90        "program must be set to when using runInTerminal");91 92  dap.is_attach = true;93  lldb::SBAttachInfo attach_info;94 95  llvm::Expected<std::shared_ptr<FifoFile>> comm_file_or_err =96      CreateRunInTerminalCommFile();97  if (!comm_file_or_err)98    return comm_file_or_err.takeError();99  FifoFile &comm_file = *comm_file_or_err.get();100 101  RunInTerminalDebugAdapterCommChannel comm_channel(comm_file.m_path);102 103  lldb::pid_t debugger_pid = LLDB_INVALID_PROCESS_ID;104#if !defined(_WIN32)105  debugger_pid = getpid();106#endif107 108  llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(109      arguments.configuration.program, arguments.args, arguments.env,110      arguments.cwd, comm_file.m_path, debugger_pid, arguments.stdio,111      arguments.console == protocol::eConsoleExternalTerminal);112  dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal",113                                                    std::move(reverse_request));114 115  if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid())116    attach_info.SetProcessID(*pid);117  else118    return pid.takeError();119 120  std::optional<ScopeSyncMode> scope_sync_mode(dap.debugger);121  lldb::SBError error;122  dap.target.Attach(attach_info, error);123 124  if (error.Fail())125    return llvm::createStringError(llvm::inconvertibleErrorCode(),126                                   "Failed to attach to the target process. %s",127                                   comm_channel.GetLauncherError().c_str());128  // This will notify the runInTerminal launcher that we attached.129  // We have to make this async, as the function won't return until the launcher130  // resumes and reads the data.131  std::future<lldb::SBError> did_attach_message_success =132      comm_channel.NotifyDidAttach();133 134  // We just attached to the runInTerminal launcher, which was waiting to be135  // attached. We now resume it, so it can receive the didAttach notification136  // and then perform the exec. Upon continuing, the debugger will stop the137  // process right in the middle of the exec. To the user, what we are doing is138  // transparent, as they will only be able to see the process since the exec,139  // completely unaware of the preparatory work.140  dap.target.GetProcess().Continue();141 142  // Now that the actual target is just starting (i.e. exec was just invoked),143  // we return the debugger to its sync state.144  scope_sync_mode.reset();145 146  // If sending the notification failed, the launcher should be dead by now and147  // the async didAttach notification should have an error message, so we148  // return it. Otherwise, everything was a success.149  did_attach_message_success.wait();150  error = did_attach_message_success.get();151  if (error.Success())152    return llvm::Error::success();153  return llvm::createStringError(llvm::inconvertibleErrorCode(),154                                 error.GetCString());155}156 157void BaseRequestHandler::Run(const Request &request) {158  // If this request was cancelled, send a cancelled response.159  if (dap.IsCancelled(request)) {160    Response cancelled{161        /*request_seq=*/request.seq,162        /*command=*/request.command,163        /*success=*/false,164        /*message=*/eResponseMessageCancelled,165    };166    dap.Send(cancelled);167    return;168  }169 170  lldb::SBMutex lock = dap.GetAPIMutex();171  std::lock_guard<lldb::SBMutex> guard(lock);172 173  // FIXME: After all the requests have migrated from LegacyRequestHandler >174  // RequestHandler<> we should be able to move this into175  // RequestHandler<>::operator().176  operator()(request);177 178  // FIXME: After all the requests have migrated from LegacyRequestHandler >179  // RequestHandler<> we should be able to check `debugger.InterruptRequest` and180  // mark the response as cancelled.181}182 183llvm::Error BaseRequestHandler::LaunchProcess(184    const protocol::LaunchRequestArguments &arguments) const {185  const std::vector<std::string> &launchCommands = arguments.launchCommands;186 187  // Instantiate a launch info instance for the target.188  auto launch_info = dap.target.GetLaunchInfo();189 190  // Grab the current working directory if there is one and set it in the191  // launch info.192  if (!arguments.cwd.empty())193    launch_info.SetWorkingDirectory(arguments.cwd.data());194 195  // Extract any extra arguments and append them to our program arguments for196  // when we launch197  if (!arguments.args.empty())198    launch_info.SetArguments(MakeArgv(arguments.args).data(), true);199 200  // Pass any environment variables along that the user specified.201  if (!arguments.env.empty()) {202    lldb::SBEnvironment env;203    for (const auto &kv : arguments.env)204      env.Set(kv.first().data(), kv.second.c_str(), true);205    launch_info.SetEnvironment(env, true);206  }207 208  if (!arguments.stdio.empty() && !arguments.disableSTDIO)209    SetupIORedirection(arguments.stdio, launch_info);210 211  launch_info.SetDetachOnError(arguments.detachOnError);212  launch_info.SetShellExpandArguments(arguments.shellExpandArguments);213 214  auto flags = launch_info.GetLaunchFlags();215  flags =216      SetLaunchFlag(flags, arguments.disableASLR, lldb::eLaunchFlagDisableASLR);217  flags = SetLaunchFlag(flags, arguments.disableSTDIO,218                        lldb::eLaunchFlagDisableSTDIO);219  launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug |220                             lldb::eLaunchFlagStopAtEntry);221 222  {223    // Perform the launch in synchronous mode so that we don't have to worry224    // about process state changes during the launch.225    ScopeSyncMode scope_sync_mode(dap.debugger);226 227    if (arguments.console != protocol::eConsoleInternal) {228      if (llvm::Error err = RunInTerminal(dap, arguments))229        return err;230    } else if (launchCommands.empty()) {231      lldb::SBError error;232      dap.target.Launch(launch_info, error);233      if (error.Fail())234        return ToError(error);235    } else {236      // Set the launch info so that run commands can access the configured237      // launch details.238      dap.target.SetLaunchInfo(launch_info);239      if (llvm::Error err = dap.RunLaunchCommands(launchCommands))240        return err;241 242      // The custom commands might have created a new target so we should use243      // the selected target after these commands are run.244      dap.target = dap.debugger.GetSelectedTarget();245    }246  }247 248  // Make sure the process is launched and stopped at the entry point before249  // proceeding.250  lldb::SBError error =251      dap.WaitForProcessToStop(arguments.configuration.timeout);252  if (error.Fail())253    return ToError(error);254 255  return llvm::Error::success();256}257 258void BaseRequestHandler::PrintWelcomeMessage() const {259#ifdef LLDB_DAP_WELCOME_MESSAGE260  dap.SendOutput(OutputType::Console, LLDB_DAP_WELCOME_MESSAGE);261#endif262}263 264bool BaseRequestHandler::HasInstructionGranularity(265    const llvm::json::Object &arguments) const {266  if (std::optional<llvm::StringRef> value = arguments.getString("granularity"))267    return value == "instruction";268  return false;269}270 271} // namespace lldb_dap272