xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Protocol/MCP/Resource.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
13 using namespace lldb_private::mcp;
14 
15 namespace {
16 struct DebuggerResource {
17   uint64_t debugger_id = 0;
18   std::string name;
19   uint64_t num_targets = 0;
20 };
21 
toJSON(const DebuggerResource & DR)22 llvm::json::Value toJSON(const DebuggerResource &DR) {
23   llvm::json::Object Result{{"debugger_id", DR.debugger_id},
24                             {"num_targets", DR.num_targets}};
25   if (!DR.name.empty())
26     Result.insert({"name", DR.name});
27   return Result;
28 }
29 
30 struct 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 
toJSON(const TargetResource & TR)40 llvm::json::Value toJSON(const TargetResource &TR) {
41   llvm::json::Object Result{{"debugger_id", TR.debugger_id},
42                             {"target_idx", TR.target_idx},
43                             {"selected", TR.selected},
44                             {"dummy", TR.dummy}};
45   if (!TR.arch.empty())
46     Result.insert({"arch", TR.arch});
47   if (!TR.path.empty())
48     Result.insert({"path", TR.path});
49   if (!TR.platform.empty())
50     Result.insert({"platform", TR.platform});
51   return Result;
52 }
53 } // namespace
54 
55 static constexpr llvm::StringLiteral kMimeTypeJSON = "application/json";
56 
57 template <typename... Args>
createStringError(const char * format,Args &&...args)58 static llvm::Error createStringError(const char *format, Args &&...args) {
59   return llvm::createStringError(
60       llvm::formatv(format, std::forward<Args>(args)...).str());
61 }
62 
createUnsupportedURIError(llvm::StringRef uri)63 static llvm::Error createUnsupportedURIError(llvm::StringRef uri) {
64   return llvm::make_error<UnsupportedURI>(uri.str());
65 }
66 
67 protocol::Resource
GetDebuggerResource(Debugger & debugger)68 DebuggerResourceProvider::GetDebuggerResource(Debugger &debugger) {
69   const lldb::user_id_t debugger_id = debugger.GetID();
70 
71   protocol::Resource resource;
72   resource.uri = llvm::formatv("lldb://debugger/{0}", debugger_id);
73   resource.name = debugger.GetInstanceName();
74   resource.description =
75       llvm::formatv("Information about debugger instance {0}: {1}", debugger_id,
76                     debugger.GetInstanceName());
77   resource.mimeType = kMimeTypeJSON;
78   return resource;
79 }
80 
81 protocol::Resource
GetTargetResource(size_t target_idx,Target & target)82 DebuggerResourceProvider::GetTargetResource(size_t target_idx, Target &target) {
83   const size_t debugger_id = target.GetDebugger().GetID();
84 
85   std::string target_name = llvm::formatv("target {0}", 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("lldb://debugger/{0}/target/{1}", debugger_id, target_idx);
93   resource.name = target_name;
94   resource.description =
95       llvm::formatv("Information about target {0} in debugger instance {1}",
96                     target_idx, debugger_id);
97   resource.mimeType = kMimeTypeJSON;
98   return resource;
99 }
100 
GetResources() const101 std::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(i);
107     if (!debugger_sp)
108       continue;
109     resources.emplace_back(GetDebuggerResource(*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(j);
115       if (!target_sp)
116         continue;
117       resources.emplace_back(GetTargetResource(j, *target_sp));
118     }
119   }
120 
121   return resources;
122 }
123 
124 llvm::Expected<protocol::ResourceResult>
ReadResource(llvm::StringRef uri) const125 DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const {
126 
127   auto [protocol, path] = uri.split("://");
128 
129   if (protocol != "lldb")
130     return createUnsupportedURIError(uri);
131 
132   llvm::SmallVector<llvm::StringRef, 4> components;
133   path.split(components, '/');
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(0, debugger_idx))
143     return createStringError("invalid debugger id '{0}': {1}", components[1],
144                              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(0, target_idx))
152       return createStringError("invalid target id '{0}': {1}", components[3],
153                                path);
154 
155     return ReadTargetResource(uri, debugger_idx, target_idx);
156   }
157 
158   return ReadDebuggerResource(uri, debugger_idx);
159 }
160 
161 llvm::Expected<protocol::ResourceResult>
ReadDebuggerResource(llvm::StringRef uri,lldb::user_id_t debugger_id)162 DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri,
163                                                lldb::user_id_t debugger_id) {
164   lldb::DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(debugger_id);
165   if (!debugger_sp)
166     return createStringError("invalid debugger id: {0}", 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("{0}", toJSON(debugger_resource));
177 
178   protocol::ResourceResult result;
179   result.contents.push_back(contents);
180   return result;
181 }
182 
183 llvm::Expected<protocol::ResourceResult>
ReadTargetResource(llvm::StringRef uri,lldb::user_id_t debugger_id,size_t target_idx)184 DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri,
185                                              lldb::user_id_t debugger_id,
186                                              size_t target_idx) {
187 
188   lldb::DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(debugger_id);
189   if (!debugger_sp)
190     return createStringError("invalid debugger id: {0}", debugger_id);
191 
192   TargetList &target_list = debugger_sp->GetTargetList();
193   lldb::TargetSP target_sp = target_list.GetTargetAtIndex(target_idx);
194   if (!target_sp)
195     return createStringError("invalid target idx: {0}", 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("{0}", toJSON(target_resource));
213 
214   protocol::ResourceResult result;
215   result.contents.push_back(contents);
216   return result;
217 }
218