brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.6 KiB · cb134b9 Raw
150 lines · cpp
1//===- Tool.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 "Tool.h"10#include "lldb/Core/Debugger.h"11#include "lldb/Interpreter/CommandInterpreter.h"12#include "lldb/Interpreter/CommandReturnObject.h"13#include "lldb/Protocol/MCP/Protocol.h"14#include "lldb/Utility/UriParser.h"15#include "llvm/ADT/StringRef.h"16#include "llvm/Support/Error.h"17#include <cstdint>18#include <optional>19 20using namespace lldb_private;21using namespace lldb_protocol;22using namespace lldb_private::mcp;23using namespace lldb;24using namespace llvm;25 26namespace {27 28static constexpr StringLiteral kSchemeAndHost = "lldb-mcp://debugger/";29 30struct CommandToolArguments {31  /// Either an id like '1' or a uri like 'lldb-mcp://debugger/1'.32  std::string debugger;33  std::string command;34};35 36bool fromJSON(const json::Value &V, CommandToolArguments &A, json::Path P) {37  json::ObjectMapper O(V, P);38  return O && O.mapOptional("debugger", A.debugger) &&39         O.mapOptional("command", A.command);40}41 42/// Helper function to create a CallToolResult from a string output.43static lldb_protocol::mcp::CallToolResult44createTextResult(std::string output, bool is_error = false) {45  lldb_protocol::mcp::CallToolResult text_result;46  text_result.content.emplace_back(47      lldb_protocol::mcp::TextContent{{std::move(output)}});48  text_result.isError = is_error;49  return text_result;50}51 52std::string to_uri(DebuggerSP debugger) {53  return (kSchemeAndHost + std::to_string(debugger->GetID())).str();54}55 56} // namespace57 58Expected<lldb_protocol::mcp::CallToolResult>59CommandTool::Call(const lldb_protocol::mcp::ToolArguments &args) {60  if (!std::holds_alternative<json::Value>(args))61    return createStringError("CommandTool requires arguments");62 63  json::Path::Root root;64 65  CommandToolArguments arguments;66  if (!fromJSON(std::get<json::Value>(args), arguments, root))67    return root.getError();68 69  lldb::DebuggerSP debugger_sp;70 71  if (!arguments.debugger.empty()) {72    llvm::StringRef debugger_specifier = arguments.debugger;73    debugger_specifier.consume_front(kSchemeAndHost);74    uint32_t debugger_id = 0;75    if (debugger_specifier.consumeInteger(10, debugger_id))76      return createStringError(77          formatv("malformed debugger specifier {0}", arguments.debugger));78 79    debugger_sp = Debugger::FindDebuggerWithID(debugger_id);80  } else {81    for (size_t i = 0; i < Debugger::GetNumDebuggers(); i++) {82      debugger_sp = Debugger::GetDebuggerAtIndex(i);83      if (debugger_sp)84        break;85    }86  }87 88  if (!debugger_sp)89    return createStringError("no debugger found");90 91  // FIXME: Disallow certain commands and their aliases.92  CommandReturnObject result(/*colors=*/false);93  debugger_sp->GetCommandInterpreter().HandleCommand(arguments.command.c_str(),94                                                     eLazyBoolYes, result);95 96  std::string output;97  StringRef output_str = result.GetOutputString();98  if (!output_str.empty())99    output += output_str.str();100 101  std::string err_str = result.GetErrorString();102  if (!err_str.empty()) {103    if (!output.empty())104      output += '\n';105    output += err_str;106  }107 108  return createTextResult(output, !result.Succeeded());109}110 111std::optional<json::Value> CommandTool::GetSchema() const {112  using namespace llvm::json;113  Object properties{114      {"debugger",115       Object{{"type", "string"},116              {"description",117               "The debugger ID or URI to a specific debug session. If not "118               "specified, the first debugger will be used."}}},119      {"command",120       Object{{"type", "string"}, {"description", "An lldb command to run."}}}};121  Object schema{{"type", "object"}, {"properties", std::move(properties)}};122  return schema;123}124 125Expected<lldb_protocol::mcp::CallToolResult>126DebuggerListTool::Call(const lldb_protocol::mcp::ToolArguments &args) {127  llvm::json::Path::Root root;128 129  // Return a nested Markdown list with debuggers and target.130  // Example output:131  //132  // - lldb-mcp://debugger/1133  // - lldb-mcp://debugger/2134  //135  // FIXME: Use Structured Content when we adopt protocol version 2025-06-18.136  std::string output;137  llvm::raw_string_ostream os(output);138 139  const size_t num_debuggers = Debugger::GetNumDebuggers();140  for (size_t i = 0; i < num_debuggers; ++i) {141    lldb::DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(i);142    if (!debugger_sp)143      continue;144 145    os << "- " << to_uri(debugger_sp) << '\n';146  }147 148  return createTextResult(output);149}150