1// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
2// See https://llvm.org/LICENSE.txt for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4//
5//===----------------------------------------------------------------------===//
6
7#include "Resource.h"
8#include "MCPError.h"
9#include "lldb/Core/Debugger.h"
10#include "lldb/Core/Module.h"
11#include "lldb/Target/Platform.h"
12
13using namespace lldb_private::mcp;
14
15namespace {
16struct DebuggerResource {
17 uint64_t debugger_id = 0;
18 std::string name;
19 uint64_t num_targets = 0;
20};
21
22llvm::json::Value toJSON(const DebuggerResource &DR) {
23 llvm::json::Object Result{{.K: "debugger_id", .V: DR.debugger_id},
24 {.K: "num_targets", .V: DR.num_targets}};
25 if (!DR.name.empty())
26 Result.insert(E: {.K: "name", .V: DR.name});
27 return Result;
28}
29
30struct TargetResource {
31 size_t debugger_id = 0;
32 size_t target_idx = 0;
33 bool selected = false;
34 bool dummy = false;
35 std::string arch;
36 std::string path;
37 std::string platform;
38};
39
40llvm::json::Value toJSON(const TargetResource &TR) {
41 llvm::json::Object Result{{.K: "debugger_id", .V: TR.debugger_id},
42 {.K: "target_idx", .V: TR.target_idx},
43 {.K: "selected", .V: TR.selected},
44 {.K: "dummy", .V: TR.dummy}};
45 if (!TR.arch.empty())
46 Result.insert(E: {.K: "arch", .V: TR.arch});
47 if (!TR.path.empty())
48 Result.insert(E: {.K: "path", .V: TR.path});
49 if (!TR.platform.empty())
50 Result.insert(E: {.K: "platform", .V: TR.platform});
51 return Result;
52}
53} // namespace
54
55static constexpr llvm::StringLiteral kMimeTypeJSON = "application/json";
56
57template <typename... Args>
58static llvm::Error createStringError(const char *format, Args &&...args) {
59 return llvm::createStringError(
60 llvm::formatv(format, std::forward<Args>(args)...).str());
61}
62
63static llvm::Error createUnsupportedURIError(llvm::StringRef uri) {
64 return llvm::make_error<UnsupportedURI>(Args: uri.str());
65}
66
67protocol::Resource
68DebuggerResourceProvider::GetDebuggerResource(Debugger &debugger) {
69 const lldb::user_id_t debugger_id = debugger.GetID();
70
71 protocol::Resource resource;
72 resource.uri = llvm::formatv(Fmt: "lldb://debugger/{0}", Vals: debugger_id);
73 resource.name = debugger.GetInstanceName();
74 resource.description =
75 llvm::formatv(Fmt: "Information about debugger instance {0}: {1}", Vals: debugger_id,
76 Vals: debugger.GetInstanceName());
77 resource.mimeType = kMimeTypeJSON;
78 return resource;
79}
80
81protocol::Resource
82DebuggerResourceProvider::GetTargetResource(size_t target_idx, Target &target) {
83 const size_t debugger_id = target.GetDebugger().GetID();
84
85 std::string target_name = llvm::formatv(Fmt: "target {0}", Vals&: target_idx);
86
87 if (Module *exe_module = target.GetExecutableModulePointer())
88 target_name = exe_module->GetFileSpec().GetFilename().GetString();
89
90 protocol::Resource resource;
91 resource.uri =
92 llvm::formatv(Fmt: "lldb://debugger/{0}/target/{1}", Vals: debugger_id, Vals&: target_idx);
93 resource.name = target_name;
94 resource.description =
95 llvm::formatv(Fmt: "Information about target {0} in debugger instance {1}",
96 Vals&: target_idx, Vals: debugger_id);
97 resource.mimeType = kMimeTypeJSON;
98 return resource;
99}
100
101std::vector<protocol::Resource> DebuggerResourceProvider::GetResources() const {
102 std::vector<protocol::Resource> resources;
103
104 const size_t num_debuggers = Debugger::GetNumDebuggers();
105 for (size_t i = 0; i < num_debuggers; ++i) {
106 lldb::DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(index: i);
107 if (!debugger_sp)
108 continue;
109 resources.emplace_back(args: GetDebuggerResource(debugger&: *debugger_sp));
110
111 TargetList &target_list = debugger_sp->GetTargetList();
112 const size_t num_targets = target_list.GetNumTargets();
113 for (size_t j = 0; j < num_targets; ++j) {
114 lldb::TargetSP target_sp = target_list.GetTargetAtIndex(index: j);
115 if (!target_sp)
116 continue;
117 resources.emplace_back(args: GetTargetResource(target_idx: j, target&: *target_sp));
118 }
119 }
120
121 return resources;
122}
123
124llvm::Expected<protocol::ResourceResult>
125DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const {
126
127 auto [protocol, path] = uri.split(Separator: "://");
128
129 if (protocol != "lldb")
130 return createUnsupportedURIError(uri);
131
132 llvm::SmallVector<llvm::StringRef, 4> components;
133 path.split(A&: components, Separator: '/');
134
135 if (components.size() < 2)
136 return createUnsupportedURIError(uri);
137
138 if (components[0] != "debugger")
139 return createUnsupportedURIError(uri);
140
141 size_t debugger_idx;
142 if (components[1].getAsInteger(Radix: 0, Result&: debugger_idx))
143 return createStringError(format: "invalid debugger id '{0}': {1}", args&: components[1],
144 args&: path);
145
146 if (components.size() > 3) {
147 if (components[2] != "target")
148 return createUnsupportedURIError(uri);
149
150 size_t target_idx;
151 if (components[3].getAsInteger(Radix: 0, Result&: target_idx))
152 return createStringError(format: "invalid target id '{0}': {1}", args&: components[3],
153 args&: path);
154
155 return ReadTargetResource(uri, debugger_id: debugger_idx, target_idx);
156 }
157
158 return ReadDebuggerResource(uri, debugger_id: debugger_idx);
159}
160
161llvm::Expected<protocol::ResourceResult>
162DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri,
163 lldb::user_id_t debugger_id) {
164 lldb::DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(id: debugger_id);
165 if (!debugger_sp)
166 return createStringError(format: "invalid debugger id: {0}", args&: debugger_id);
167
168 DebuggerResource debugger_resource;
169 debugger_resource.debugger_id = debugger_id;
170 debugger_resource.name = debugger_sp->GetInstanceName();
171 debugger_resource.num_targets = debugger_sp->GetTargetList().GetNumTargets();
172
173 protocol::ResourceContents contents;
174 contents.uri = uri;
175 contents.mimeType = kMimeTypeJSON;
176 contents.text = llvm::formatv(Fmt: "{0}", Vals: toJSON(DR: debugger_resource));
177
178 protocol::ResourceResult result;
179 result.contents.push_back(x: contents);
180 return result;
181}
182
183llvm::Expected<protocol::ResourceResult>
184DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri,
185 lldb::user_id_t debugger_id,
186 size_t target_idx) {
187
188 lldb::DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(id: debugger_id);
189 if (!debugger_sp)
190 return createStringError(format: "invalid debugger id: {0}", args&: debugger_id);
191
192 TargetList &target_list = debugger_sp->GetTargetList();
193 lldb::TargetSP target_sp = target_list.GetTargetAtIndex(index: target_idx);
194 if (!target_sp)
195 return createStringError(format: "invalid target idx: {0}", args&: target_idx);
196
197 TargetResource target_resource;
198 target_resource.debugger_id = debugger_id;
199 target_resource.target_idx = target_idx;
200 target_resource.arch = target_sp->GetArchitecture().GetTriple().str();
201 target_resource.dummy = target_sp->IsDummyTarget();
202 target_resource.selected = target_sp == debugger_sp->GetSelectedTarget();
203
204 if (Module *exe_module = target_sp->GetExecutableModulePointer())
205 target_resource.path = exe_module->GetFileSpec().GetPath();
206 if (lldb::PlatformSP platform_sp = target_sp->GetPlatform())
207 target_resource.platform = platform_sp->GetName();
208
209 protocol::ResourceContents contents;
210 contents.uri = uri;
211 contents.mimeType = kMimeTypeJSON;
212 contents.text = llvm::formatv(Fmt: "{0}", Vals: toJSON(TR: target_resource));
213
214 protocol::ResourceResult result;
215 result.contents.push_back(x: contents);
216 return result;
217}
218

source code of lldb/source/Plugins/Protocol/MCP/Resource.cpp