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 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 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> 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 63 static llvm::Error createUnsupportedURIError(llvm::StringRef uri) { 64 return llvm::make_error<UnsupportedURI>(uri.str()); 65 } 66 67 protocol::Resource 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 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 101 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> 125 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> 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> 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