Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lldb/source/Host/common/Socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ Socket::GetProtocolAndMode(llvm::StringRef scheme) {
.Case("unix-abstract-accept",
ProtocolModePair{SocketProtocol::ProtocolUnixAbstract,
SocketMode::ModeAccept})
.Cases("connect", "tcp-connect",
.Cases("connect", "tcp-connect", "connection",
ProtocolModePair{SocketProtocol::ProtocolTcp,
SocketMode::ModeConnect})
.Case("udp", ProtocolModePair{SocketProtocol::ProtocolTcp,
Expand Down
122 changes: 91 additions & 31 deletions lldb/tools/lldb-mcp/lldb-mcp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@

#include "lldb/Host/Config.h"
#include "lldb/Host/File.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/MainLoop.h"
#include "lldb/Host/MainLoopBase.h"
#include "lldb/Host/ProcessLaunchInfo.h"
#include "lldb/Host/Socket.h"
#include "lldb/Initialization/SystemInitializerCommon.h"
#include "lldb/Initialization/SystemLifetimeManager.h"
#include "lldb/Protocol/MCP/Server.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/UriParser.h"
#include "lldb/lldb-forward.h"
Expand All @@ -24,8 +28,10 @@
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/WithColor.h"
#include <chrono>
#include <cstdlib>
#include <memory>
#include <thread>

#if defined(_WIN32)
#include <fcntl.h>
Expand All @@ -35,24 +41,88 @@ using namespace llvm;
using namespace lldb;
using namespace lldb_protocol::mcp;

using lldb_private::Environment;
using lldb_private::File;
using lldb_private::FileSpec;
using lldb_private::FileSystem;
using lldb_private::Host;
using lldb_private::MainLoop;
using lldb_private::MainLoopBase;
using lldb_private::NativeFile;

namespace {

constexpr size_t kForwardIOBufferSize = 1024;

inline void exitWithError(llvm::Error Err, StringRef Prefix = "") {
handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {
WithColor::error(errs(), Prefix) << Info.message() << '\n';
});
std::exit(EXIT_FAILURE);
}

constexpr size_t kForwardIOBufferSize = 1024;
FileSpec driverPath() {
Environment host_env = Host::GetEnvironment();

// Check if an override for which lldb we're using exists, otherwise look next
// to the current binary.
std::string lldb_exe_path = host_env.lookup("LLDB_EXE_PATH");
auto &fs = FileSystem::Instance();
if (fs.Exists(lldb_exe_path)) {
return FileSpec(lldb_exe_path);
}

FileSpec lldb_exec_spec = lldb_private::HostInfo::GetProgramFileSpec();
lldb_exec_spec.SetFilename("lldb");
return lldb_exec_spec;
}

llvm::Error launch() {
FileSpec lldb_exec = driverPath();
lldb_private::ProcessLaunchInfo info;
info.SetExecutableFile(lldb_exec,
/*add_exe_file_as_first_arg=*/true);
info.GetArguments().AppendArgument("-O");
info.GetArguments().AppendArgument("protocol start MCP");
return Host::LaunchProcess(info).takeError();
}

Expected<ServerInfo> loadOrStart(
// FIXME: This should become a CLI arg.
lldb_private::Timeout<std::micro> timeout = std::chrono::seconds(30)) {
using namespace std::chrono;
bool started = false;

const auto deadline = steady_clock::now() + *timeout;
while (steady_clock::now() < deadline) {
Expected<std::vector<ServerInfo>> servers = ServerInfo::Load();
if (!servers)
return servers.takeError();

if (servers->empty()) {
if (!started) {
started = true;
if (llvm::Error err = launch())
return std::move(err);
}

// FIXME: Can we use MainLoop to watch the directory?
std::this_thread::sleep_for(microseconds(250));
continue;
}

// FIXME: Support selecting / multiplexing a specific lldb instance.
if (servers->size() > 1)
return createStringError("To many MCP servers running, picking a "
"specific one is not yet implemented");

return servers->front();
}

return createStringError("timed out waiting for MCP server to start");
}

void forwardIO(lldb_private::MainLoopBase &loop, lldb::IOObjectSP &from,
lldb::IOObjectSP &to) {
void forwardIO(MainLoopBase &loop, IOObjectSP &from, IOObjectSP &to) {
char buf[kForwardIOBufferSize];
size_t num_bytes = sizeof(buf);

Expand All @@ -67,46 +137,47 @@ void forwardIO(lldb_private::MainLoopBase &loop, lldb::IOObjectSP &from,
exitWithError(std::move(err));
}

void connectAndForwardIO(lldb_private::MainLoop &loop, ServerInfo &info,
IOObjectSP &input_sp, IOObjectSP &output_sp) {
llvm::Error connectAndForwardIO(lldb_private::MainLoop &loop, ServerInfo &info,
IOObjectSP &input_sp, IOObjectSP &output_sp) {
auto uri = lldb_private::URI::Parse(info.connection_uri);
if (!uri)
exitWithError(createStringError("invalid connection_uri"));
return createStringError("invalid connection_uri");

std::optional<lldb_private::Socket::ProtocolModePair> protocol_and_mode =
lldb_private::Socket::GetProtocolAndMode(uri->scheme);

if (!protocol_and_mode)
return createStringError("unknown protocol scheme");

lldb_private::Status status;
std::unique_ptr<lldb_private::Socket> sock =
lldb_private::Socket::Create(protocol_and_mode->first, status);

if (status.Fail())
exitWithError(status.takeError());
return status.takeError();

if (uri->port && !uri->hostname.empty())
status = sock->Connect(
llvm::formatv("[{0}]:{1}", uri->hostname, *uri->port).str());
else
status = sock->Connect(uri->path);
if (status.Fail())
exitWithError(status.takeError());
return status.takeError();

IOObjectSP sock_sp = std::move(sock);
auto input_handle = loop.RegisterReadObject(
input_sp, std::bind(forwardIO, std::placeholders::_1, input_sp, sock_sp),
status);
if (status.Fail())
exitWithError(status.takeError());
return status.takeError();

auto socket_handle = loop.RegisterReadObject(
sock_sp, std::bind(forwardIO, std::placeholders::_1, sock_sp, output_sp),
status);
if (status.Fail())
exitWithError(status.takeError());
return status.takeError();

status = loop.Run();
if (status.Fail())
exitWithError(status.takeError());
return loop.Run().takeError();
}

llvm::ManagedStatic<lldb_private::SystemLifetimeManager> g_debugger_lifetime;
Expand Down Expand Up @@ -147,30 +218,19 @@ int main(int argc, char *argv[]) {
IOObjectSP output_sp = std::make_shared<NativeFile>(
fileno(stdout), File::eOpenOptionWriteOnly, NativeFile::Unowned);

static MainLoop loop;
Expected<ServerInfo> server_info = loadOrStart();
if (!server_info)
exitWithError(server_info.takeError());

static MainLoop loop;
sys::SetInterruptFunction([]() {
loop.AddPendingCallback(
[](MainLoopBase &loop) { loop.RequestTermination(); });
});

auto existing_servers = ServerInfo::Load();

if (!existing_servers)
exitWithError(existing_servers.takeError());

// FIXME: Launch `lldb -o 'protocol start MCP'`.
if (existing_servers->empty())
exitWithError(createStringError("No MCP servers running"));

// FIXME: Support selecting a specific server.
if (existing_servers->size() != 1)
exitWithError(
createStringError("To many MCP servers running, picking a specific "
"one is not yet implemented."));

ServerInfo &info = existing_servers->front();
connectAndForwardIO(loop, info, input_sp, output_sp);
if (llvm::Error error =
connectAndForwardIO(loop, *server_info, input_sp, output_sp))
exitWithError(std::move(error));

return EXIT_SUCCESS;
}
Loading