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