brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.6 KiB · 3fc3622 Raw
244 lines · cpp
1//===----------------------------------------------------------------------===//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 "lldb/Host/Config.h"10#include "lldb/Host/File.h"11#include "lldb/Host/FileSystem.h"12#include "lldb/Host/Host.h"13#include "lldb/Host/MainLoop.h"14#include "lldb/Host/MainLoopBase.h"15#include "lldb/Host/ProcessLaunchInfo.h"16#include "lldb/Host/Socket.h"17#include "lldb/Initialization/SystemInitializerCommon.h"18#include "lldb/Initialization/SystemLifetimeManager.h"19#include "lldb/Protocol/MCP/Server.h"20#include "lldb/Utility/FileSpec.h"21#include "lldb/Utility/Status.h"22#include "lldb/Utility/UriParser.h"23#include "lldb/lldb-forward.h"24#include "llvm/ADT/ScopeExit.h"25#include "llvm/ADT/StringRef.h"26#include "llvm/Support/Error.h"27#include "llvm/Support/InitLLVM.h"28#include "llvm/Support/ManagedStatic.h"29#include "llvm/Support/Signals.h"30#include "llvm/Support/WithColor.h"31#include <chrono>32#include <cstdlib>33#include <memory>34#include <thread>35 36#if defined(_WIN32)37#include <fcntl.h>38#endif39 40using namespace llvm;41using namespace lldb;42using namespace lldb_protocol::mcp;43 44using lldb_private::Environment;45using lldb_private::File;46using lldb_private::FileSpec;47using lldb_private::FileSystem;48using lldb_private::Host;49using lldb_private::MainLoop;50using lldb_private::MainLoopBase;51using lldb_private::NativeFile;52 53namespace {54 55#if defined(_WIN32)56constexpr StringLiteral kDriverName = "lldb.exe";57#else58constexpr StringLiteral kDriverName = "lldb";59#endif60 61constexpr size_t kForwardIOBufferSize = 1024;62 63inline void exitWithError(llvm::Error Err, StringRef Prefix = "") {64  handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {65    WithColor::error(errs(), Prefix) << Info.message() << '\n';66  });67  std::exit(EXIT_FAILURE);68}69 70FileSpec driverPath() {71  Environment host_env = Host::GetEnvironment();72 73  // Check if an override for which lldb we're using exists, otherwise look next74  // to the current binary.75  std::string lldb_exe_path = host_env.lookup("LLDB_EXE_PATH");76  auto &fs = FileSystem::Instance();77  if (fs.Exists(lldb_exe_path))78    return FileSpec(lldb_exe_path);79 80  FileSpec lldb_exec_spec = lldb_private::HostInfo::GetProgramFileSpec();81  lldb_exec_spec.SetFilename(kDriverName);82  return lldb_exec_spec;83}84 85llvm::Error launch() {86  FileSpec lldb_exec = driverPath();87  lldb_private::ProcessLaunchInfo info;88  info.SetMonitorProcessCallback(89      &lldb_private::ProcessLaunchInfo::NoOpMonitorCallback);90  info.SetExecutableFile(lldb_exec,91                         /*add_exe_file_as_first_arg=*/true);92  info.GetArguments().AppendArgument("-O");93  info.GetArguments().AppendArgument("protocol start MCP");94  return Host::LaunchProcess(info).takeError();95}96 97Expected<ServerInfo> loadOrStart(98    // FIXME: This should become a CLI arg.99    lldb_private::Timeout<std::micro> timeout = std::chrono::seconds(30)) {100  using namespace std::chrono;101  bool started = false;102 103  const auto deadline = steady_clock::now() + *timeout;104  while (steady_clock::now() < deadline) {105    Expected<std::vector<ServerInfo>> servers = ServerInfo::Load();106    if (!servers)107      return servers.takeError();108 109    if (servers->empty()) {110      if (!started) {111        started = true;112        if (llvm::Error err = launch())113          return std::move(err);114      }115 116      // FIXME: Can we use MainLoop to watch the directory?117      std::this_thread::sleep_for(microseconds(250));118      continue;119    }120 121    // FIXME: Support selecting / multiplexing a specific lldb instance.122    if (servers->size() > 1)123      return createStringError("too many MCP servers running, picking a "124                               "specific one is not yet implemented");125 126    return servers->front();127  }128 129  return createStringError("timed out waiting for MCP server to start");130}131 132void forwardIO(MainLoopBase &loop, IOObjectSP &from, IOObjectSP &to) {133  char buf[kForwardIOBufferSize];134  size_t num_bytes = sizeof(buf);135 136  if (llvm::Error err = from->Read(buf, num_bytes).takeError())137    exitWithError(std::move(err));138 139  // EOF reached.140  if (num_bytes == 0)141    return loop.RequestTermination();142 143  if (llvm::Error err = to->Write(buf, num_bytes).takeError())144    exitWithError(std::move(err));145}146 147llvm::Error connectAndForwardIO(lldb_private::MainLoop &loop, ServerInfo &info,148                                IOObjectSP &input_sp, IOObjectSP &output_sp) {149  auto uri = lldb_private::URI::Parse(info.connection_uri);150  if (!uri)151    return createStringError("invalid connection_uri");152 153  std::optional<lldb_private::Socket::ProtocolModePair> protocol_and_mode =154      lldb_private::Socket::GetProtocolAndMode(uri->scheme);155 156  if (!protocol_and_mode)157    return createStringError("unknown protocol scheme");158 159  lldb_private::Status status;160  std::unique_ptr<lldb_private::Socket> sock =161      lldb_private::Socket::Create(protocol_and_mode->first, status);162 163  if (status.Fail())164    return status.takeError();165 166  if (uri->port && !uri->hostname.empty())167    status = sock->Connect(168        llvm::formatv("[{0}]:{1}", uri->hostname, *uri->port).str());169  else170    status = sock->Connect(uri->path);171  if (status.Fail())172    return status.takeError();173 174  IOObjectSP sock_sp = std::move(sock);175  auto input_handle = loop.RegisterReadObject(176      input_sp, std::bind(forwardIO, std::placeholders::_1, input_sp, sock_sp),177      status);178  if (status.Fail())179    return status.takeError();180 181  auto socket_handle = loop.RegisterReadObject(182      sock_sp, std::bind(forwardIO, std::placeholders::_1, sock_sp, output_sp),183      status);184  if (status.Fail())185    return status.takeError();186 187  return loop.Run().takeError();188}189 190llvm::ManagedStatic<lldb_private::SystemLifetimeManager> g_debugger_lifetime;191 192} // namespace193 194int main(int argc, char *argv[]) {195  llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);196#if !defined(__APPLE__)197  llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL198                        " and include the crash backtrace.\n");199#else200  llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL201                        " and include the crash report from "202                        "~/Library/Logs/DiagnosticReports/.\n");203#endif204 205#if defined(_WIN32)206  // Windows opens stdout and stdin in text mode which converts \n to 13,10207  // while the value is just 10 on Darwin/Linux. Setting the file mode to208  // binary fixes this.209  int result = _setmode(fileno(stdout), _O_BINARY);210  assert(result);211  result = _setmode(fileno(stdin), _O_BINARY);212  UNUSED_IF_ASSERT_DISABLED(result);213  assert(result);214#endif215 216  if (llvm::Error err = g_debugger_lifetime->Initialize(217          std::make_unique<lldb_private::SystemInitializerCommon>(nullptr)))218    exitWithError(std::move(err));219 220  auto cleanup = make_scope_exit([] { g_debugger_lifetime->Terminate(); });221 222  IOObjectSP input_sp = std::make_shared<NativeFile>(223      fileno(stdin), File::eOpenOptionReadOnly, NativeFile::Unowned);224 225  IOObjectSP output_sp = std::make_shared<NativeFile>(226      fileno(stdout), File::eOpenOptionWriteOnly, NativeFile::Unowned);227 228  Expected<ServerInfo> server_info = loadOrStart();229  if (!server_info)230    exitWithError(server_info.takeError());231 232  static MainLoop loop;233  sys::SetInterruptFunction([]() {234    loop.AddPendingCallback(235        [](MainLoopBase &loop) { loop.RequestTermination(); });236  });237 238  if (llvm::Error error =239          connectAndForwardIO(loop, *server_info, input_sp, output_sp))240    exitWithError(std::move(error));241 242  return EXIT_SUCCESS;243}244