https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/158701
>From 39b27ffc60fe30e88e42918f2c3382369f06f3df Mon Sep 17 00:00:00 2001 From: John Harrison <[email protected]> Date: Mon, 15 Sep 2025 10:30:04 -0700 Subject: [PATCH 1/3] [lldb-mcp] Launch lldb on demand, if needed. Adding support for launching lldb with `-O protocol start MCP` if a valid ~/.lldb/lldb-mcp-*.json` file is not found. --- lldb/source/Host/common/Socket.cpp | 2 +- lldb/tools/lldb-mcp/lldb-mcp.cpp | 122 +++++++++++++++++++++-------- 2 files changed, 92 insertions(+), 32 deletions(-) diff --git a/lldb/source/Host/common/Socket.cpp b/lldb/source/Host/common/Socket.cpp index 3511cde8bb36f..bc3d849c5c6c6 100644 --- a/lldb/source/Host/common/Socket.cpp +++ b/lldb/source/Host/common/Socket.cpp @@ -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, diff --git a/lldb/tools/lldb-mcp/lldb-mcp.cpp b/lldb/tools/lldb-mcp/lldb-mcp.cpp index 12545dcf3a3cc..42e82709dd9df 100644 --- a/lldb/tools/lldb-mcp/lldb-mcp.cpp +++ b/lldb/tools/lldb-mcp/lldb-mcp.cpp @@ -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" @@ -24,7 +28,9 @@ #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Signals.h" #include "llvm/Support/WithColor.h" +#include <chrono> #include <cstdlib> +#include <future> #include <memory> #if defined(_WIN32) @@ -35,13 +41,19 @@ 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'; @@ -49,10 +61,68 @@ inline void exitWithError(llvm::Error Err, StringRef Prefix = "") { 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"); + std::promise<int> exit_status; + info.SetMonitorProcessCallback([&](lldb::pid_t pid, int signal, int status) { + exit_status.set_value(status); + }); + + return Host::LaunchProcess(info).takeError(); +} + +Expected<ServerInfo> loadOrStart( + lldb_private::Timeout<std::micro> timeout = std::chrono::seconds(30)) { + using namespace std::chrono; + bool started = false; + + auto deadline = steady_clock::now() + *timeout; + while (steady_clock::now() < deadline) { + auto servers = ServerInfo::Load(); + if (!servers) + return servers.takeError(); + + if (servers->empty()) { + if (!started) { + started = true; + if (llvm::Error err = launch()) + return std::move(err); + } + std::this_thread::sleep_for(std::chrono::microseconds(250)); + continue; + } + + 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); @@ -67,21 +137,24 @@ 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( @@ -89,24 +162,22 @@ void connectAndForwardIO(lldb_private::MainLoop &loop, ServerInfo &info, 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; @@ -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; } >From b0c9485ccb04896b7fe8d89506c220692fa8f29b Mon Sep 17 00:00:00 2001 From: John Harrison <[email protected]> Date: Mon, 15 Sep 2025 11:02:43 -0700 Subject: [PATCH 2/3] Adding some comments and removing some dead code. --- lldb/tools/lldb-mcp/lldb-mcp.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lldb/tools/lldb-mcp/lldb-mcp.cpp b/lldb/tools/lldb-mcp/lldb-mcp.cpp index 42e82709dd9df..6c306ad40e306 100644 --- a/lldb/tools/lldb-mcp/lldb-mcp.cpp +++ b/lldb/tools/lldb-mcp/lldb-mcp.cpp @@ -30,8 +30,8 @@ #include "llvm/Support/WithColor.h" #include <chrono> #include <cstdlib> -#include <future> #include <memory> +#include <thread> #if defined(_WIN32) #include <fcntl.h> @@ -71,6 +71,7 @@ FileSpec driverPath() { 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; @@ -83,22 +84,18 @@ llvm::Error launch() { /*add_exe_file_as_first_arg=*/true); info.GetArguments().AppendArgument("-O"); info.GetArguments().AppendArgument("protocol start MCP"); - std::promise<int> exit_status; - info.SetMonitorProcessCallback([&](lldb::pid_t pid, int signal, int status) { - exit_status.set_value(status); - }); - 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; - auto deadline = steady_clock::now() + *timeout; + const auto deadline = steady_clock::now() + *timeout; while (steady_clock::now() < deadline) { - auto servers = ServerInfo::Load(); + Expected<std::vector<ServerInfo>> servers = ServerInfo::Load(); if (!servers) return servers.takeError(); @@ -108,10 +105,13 @@ Expected<ServerInfo> loadOrStart( if (llvm::Error err = launch()) return std::move(err); } - std::this_thread::sleep_for(std::chrono::microseconds(250)); + + // 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"); >From 2c4f00353c81edbecce5058a7e9510b8742b2932 Mon Sep 17 00:00:00 2001 From: John Harrison <[email protected]> Date: Tue, 16 Sep 2025 09:00:13 -0700 Subject: [PATCH 3/3] Update lldb/tools/lldb-mcp/lldb-mcp.cpp Co-authored-by: Jonas Devlieghere <[email protected]> --- lldb/tools/lldb-mcp/lldb-mcp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/tools/lldb-mcp/lldb-mcp.cpp b/lldb/tools/lldb-mcp/lldb-mcp.cpp index 6c306ad40e306..775e86b8067eb 100644 --- a/lldb/tools/lldb-mcp/lldb-mcp.cpp +++ b/lldb/tools/lldb-mcp/lldb-mcp.cpp @@ -113,7 +113,7 @@ Expected<ServerInfo> loadOrStart( // FIXME: Support selecting / multiplexing a specific lldb instance. if (servers->size() > 1) - return createStringError("To many MCP servers running, picking a " + return createStringError("too many MCP servers running, picking a " "specific one is not yet implemented"); return servers->front(); _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
