xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Protocol/MCP/Tool.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Tool.h"
10 #include "lldb/Core/Module.h"
11 #include "lldb/Interpreter/CommandInterpreter.h"
12 #include "lldb/Interpreter/CommandReturnObject.h"
13 
14 using namespace lldb_private::mcp;
15 using namespace llvm;
16 
17 namespace {
18 struct CommandToolArguments {
19   uint64_t debugger_id;
20   std::string arguments;
21 };
22 
fromJSON(const llvm::json::Value & V,CommandToolArguments & A,llvm::json::Path P)23 bool fromJSON(const llvm::json::Value &V, CommandToolArguments &A,
24               llvm::json::Path P) {
25   llvm::json::ObjectMapper O(V, P);
26   return O && O.map("debugger_id", A.debugger_id) &&
27          O.mapOptional("arguments", A.arguments);
28 }
29 
30 /// Helper function to create a TextResult from a string output.
31 static lldb_private::mcp::protocol::TextResult
createTextResult(std::string output,bool is_error=false)32 createTextResult(std::string output, bool is_error = false) {
33   lldb_private::mcp::protocol::TextResult text_result;
34   text_result.content.emplace_back(
35       lldb_private::mcp::protocol::TextContent{{std::move(output)}});
36   text_result.isError = is_error;
37   return text_result;
38 }
39 
40 } // namespace
41 
Tool(std::string name,std::string description)42 Tool::Tool(std::string name, std::string description)
43     : m_name(std::move(name)), m_description(std::move(description)) {}
44 
GetDefinition() const45 protocol::ToolDefinition Tool::GetDefinition() const {
46   protocol::ToolDefinition definition;
47   definition.name = m_name;
48   definition.description = m_description;
49 
50   if (std::optional<llvm::json::Value> input_schema = GetSchema())
51     definition.inputSchema = *input_schema;
52 
53   return definition;
54 }
55 
56 llvm::Expected<protocol::TextResult>
Call(const protocol::ToolArguments & args)57 CommandTool::Call(const protocol::ToolArguments &args) {
58   if (!std::holds_alternative<json::Value>(args))
59     return createStringError("CommandTool requires arguments");
60 
61   json::Path::Root root;
62 
63   CommandToolArguments arguments;
64   if (!fromJSON(std::get<json::Value>(args), arguments, root))
65     return root.getError();
66 
67   lldb::DebuggerSP debugger_sp =
68       Debugger::FindDebuggerWithID(arguments.debugger_id);
69   if (!debugger_sp)
70     return createStringError(
71         llvm::formatv("no debugger with id {0}", arguments.debugger_id));
72 
73   // FIXME: Disallow certain commands and their aliases.
74   CommandReturnObject result(/*colors=*/false);
75   debugger_sp->GetCommandInterpreter().HandleCommand(
76       arguments.arguments.c_str(), eLazyBoolYes, result);
77 
78   std::string output;
79   llvm::StringRef output_str = result.GetOutputString();
80   if (!output_str.empty())
81     output += output_str.str();
82 
83   std::string err_str = result.GetErrorString();
84   if (!err_str.empty()) {
85     if (!output.empty())
86       output += '\n';
87     output += err_str;
88   }
89 
90   return createTextResult(output, !result.Succeeded());
91 }
92 
GetSchema() const93 std::optional<llvm::json::Value> CommandTool::GetSchema() const {
94   llvm::json::Object id_type{{"type", "number"}};
95   llvm::json::Object str_type{{"type", "string"}};
96   llvm::json::Object properties{{"debugger_id", std::move(id_type)},
97                                 {"arguments", std::move(str_type)}};
98   llvm::json::Array required{"debugger_id"};
99   llvm::json::Object schema{{"type", "object"},
100                             {"properties", std::move(properties)},
101                             {"required", std::move(required)}};
102   return schema;
103 }
104