
This PR moves the MCP protocol code into its own library (`lldbProtocolMCP`) so the code can be shared between the `ProtocolServerMCP` plugin in LLDB as well as `lldb-mcp`. The goal is to do the same thing for DAP (which, for now, would be used exclusively from `lldb-dap`). To make it clear that it's neither part of the `lldb` nor the `lldb_private` namespace, I created a new `lldb_protocol` namespace. Depending on how much code would be reused by lldb-dap, we may move more code into the protocol library.
220 lines
7.3 KiB
C++
220 lines
7.3 KiB
C++
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Resource.h"
|
|
#include "MCPError.h"
|
|
#include "lldb/Core/Debugger.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Target/Platform.h"
|
|
|
|
using namespace lldb_private;
|
|
using namespace lldb_private::mcp;
|
|
|
|
namespace {
|
|
struct DebuggerResource {
|
|
uint64_t debugger_id = 0;
|
|
std::string name;
|
|
uint64_t num_targets = 0;
|
|
};
|
|
|
|
llvm::json::Value toJSON(const DebuggerResource &DR) {
|
|
llvm::json::Object Result{{"debugger_id", DR.debugger_id},
|
|
{"num_targets", DR.num_targets}};
|
|
if (!DR.name.empty())
|
|
Result.insert({"name", DR.name});
|
|
return Result;
|
|
}
|
|
|
|
struct TargetResource {
|
|
size_t debugger_id = 0;
|
|
size_t target_idx = 0;
|
|
bool selected = false;
|
|
bool dummy = false;
|
|
std::string arch;
|
|
std::string path;
|
|
std::string platform;
|
|
};
|
|
|
|
llvm::json::Value toJSON(const TargetResource &TR) {
|
|
llvm::json::Object Result{{"debugger_id", TR.debugger_id},
|
|
{"target_idx", TR.target_idx},
|
|
{"selected", TR.selected},
|
|
{"dummy", TR.dummy}};
|
|
if (!TR.arch.empty())
|
|
Result.insert({"arch", TR.arch});
|
|
if (!TR.path.empty())
|
|
Result.insert({"path", TR.path});
|
|
if (!TR.platform.empty())
|
|
Result.insert({"platform", TR.platform});
|
|
return Result;
|
|
}
|
|
} // namespace
|
|
|
|
static constexpr llvm::StringLiteral kMimeTypeJSON = "application/json";
|
|
|
|
template <typename... Args>
|
|
static llvm::Error createStringError(const char *format, Args &&...args) {
|
|
return llvm::createStringError(
|
|
llvm::formatv(format, std::forward<Args>(args)...).str());
|
|
}
|
|
|
|
static llvm::Error createUnsupportedURIError(llvm::StringRef uri) {
|
|
return llvm::make_error<UnsupportedURI>(uri.str());
|
|
}
|
|
|
|
lldb_protocol::mcp::Resource
|
|
DebuggerResourceProvider::GetDebuggerResource(Debugger &debugger) {
|
|
const lldb::user_id_t debugger_id = debugger.GetID();
|
|
|
|
lldb_protocol::mcp::Resource resource;
|
|
resource.uri = llvm::formatv("lldb://debugger/{0}", debugger_id);
|
|
resource.name = debugger.GetInstanceName();
|
|
resource.description =
|
|
llvm::formatv("Information about debugger instance {0}: {1}", debugger_id,
|
|
debugger.GetInstanceName());
|
|
resource.mimeType = kMimeTypeJSON;
|
|
return resource;
|
|
}
|
|
|
|
lldb_protocol::mcp::Resource
|
|
DebuggerResourceProvider::GetTargetResource(size_t target_idx, Target &target) {
|
|
const size_t debugger_id = target.GetDebugger().GetID();
|
|
|
|
std::string target_name = llvm::formatv("target {0}", target_idx);
|
|
|
|
if (Module *exe_module = target.GetExecutableModulePointer())
|
|
target_name = exe_module->GetFileSpec().GetFilename().GetString();
|
|
|
|
lldb_protocol::mcp::Resource resource;
|
|
resource.uri =
|
|
llvm::formatv("lldb://debugger/{0}/target/{1}", debugger_id, target_idx);
|
|
resource.name = target_name;
|
|
resource.description =
|
|
llvm::formatv("Information about target {0} in debugger instance {1}",
|
|
target_idx, debugger_id);
|
|
resource.mimeType = kMimeTypeJSON;
|
|
return resource;
|
|
}
|
|
|
|
std::vector<lldb_protocol::mcp::Resource>
|
|
DebuggerResourceProvider::GetResources() const {
|
|
std::vector<lldb_protocol::mcp::Resource> resources;
|
|
|
|
const size_t num_debuggers = Debugger::GetNumDebuggers();
|
|
for (size_t i = 0; i < num_debuggers; ++i) {
|
|
lldb::DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(i);
|
|
if (!debugger_sp)
|
|
continue;
|
|
resources.emplace_back(GetDebuggerResource(*debugger_sp));
|
|
|
|
TargetList &target_list = debugger_sp->GetTargetList();
|
|
const size_t num_targets = target_list.GetNumTargets();
|
|
for (size_t j = 0; j < num_targets; ++j) {
|
|
lldb::TargetSP target_sp = target_list.GetTargetAtIndex(j);
|
|
if (!target_sp)
|
|
continue;
|
|
resources.emplace_back(GetTargetResource(j, *target_sp));
|
|
}
|
|
}
|
|
|
|
return resources;
|
|
}
|
|
|
|
llvm::Expected<lldb_protocol::mcp::ResourceResult>
|
|
DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const {
|
|
|
|
auto [protocol, path] = uri.split("://");
|
|
|
|
if (protocol != "lldb")
|
|
return createUnsupportedURIError(uri);
|
|
|
|
llvm::SmallVector<llvm::StringRef, 4> components;
|
|
path.split(components, '/');
|
|
|
|
if (components.size() < 2)
|
|
return createUnsupportedURIError(uri);
|
|
|
|
if (components[0] != "debugger")
|
|
return createUnsupportedURIError(uri);
|
|
|
|
size_t debugger_idx;
|
|
if (components[1].getAsInteger(0, debugger_idx))
|
|
return createStringError("invalid debugger id '{0}': {1}", components[1],
|
|
path);
|
|
|
|
if (components.size() > 3) {
|
|
if (components[2] != "target")
|
|
return createUnsupportedURIError(uri);
|
|
|
|
size_t target_idx;
|
|
if (components[3].getAsInteger(0, target_idx))
|
|
return createStringError("invalid target id '{0}': {1}", components[3],
|
|
path);
|
|
|
|
return ReadTargetResource(uri, debugger_idx, target_idx);
|
|
}
|
|
|
|
return ReadDebuggerResource(uri, debugger_idx);
|
|
}
|
|
|
|
llvm::Expected<lldb_protocol::mcp::ResourceResult>
|
|
DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri,
|
|
lldb::user_id_t debugger_id) {
|
|
lldb::DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(debugger_id);
|
|
if (!debugger_sp)
|
|
return createStringError("invalid debugger id: {0}", debugger_id);
|
|
|
|
DebuggerResource debugger_resource;
|
|
debugger_resource.debugger_id = debugger_id;
|
|
debugger_resource.name = debugger_sp->GetInstanceName();
|
|
debugger_resource.num_targets = debugger_sp->GetTargetList().GetNumTargets();
|
|
|
|
lldb_protocol::mcp::ResourceContents contents;
|
|
contents.uri = uri;
|
|
contents.mimeType = kMimeTypeJSON;
|
|
contents.text = llvm::formatv("{0}", toJSON(debugger_resource));
|
|
|
|
lldb_protocol::mcp::ResourceResult result;
|
|
result.contents.push_back(contents);
|
|
return result;
|
|
}
|
|
|
|
llvm::Expected<lldb_protocol::mcp::ResourceResult>
|
|
DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri,
|
|
lldb::user_id_t debugger_id,
|
|
size_t target_idx) {
|
|
|
|
lldb::DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(debugger_id);
|
|
if (!debugger_sp)
|
|
return createStringError("invalid debugger id: {0}", debugger_id);
|
|
|
|
TargetList &target_list = debugger_sp->GetTargetList();
|
|
lldb::TargetSP target_sp = target_list.GetTargetAtIndex(target_idx);
|
|
if (!target_sp)
|
|
return createStringError("invalid target idx: {0}", target_idx);
|
|
|
|
TargetResource target_resource;
|
|
target_resource.debugger_id = debugger_id;
|
|
target_resource.target_idx = target_idx;
|
|
target_resource.arch = target_sp->GetArchitecture().GetTriple().str();
|
|
target_resource.dummy = target_sp->IsDummyTarget();
|
|
target_resource.selected = target_sp == debugger_sp->GetSelectedTarget();
|
|
|
|
if (Module *exe_module = target_sp->GetExecutableModulePointer())
|
|
target_resource.path = exe_module->GetFileSpec().GetPath();
|
|
if (lldb::PlatformSP platform_sp = target_sp->GetPlatform())
|
|
target_resource.platform = platform_sp->GetName();
|
|
|
|
lldb_protocol::mcp::ResourceContents contents;
|
|
contents.uri = uri;
|
|
contents.mimeType = kMimeTypeJSON;
|
|
contents.text = llvm::formatv("{0}", toJSON(target_resource));
|
|
|
|
lldb_protocol::mcp::ResourceResult result;
|
|
result.contents.push_back(contents);
|
|
return result;
|
|
}
|