[Lldb-commits] [lldb] [lldb-mcp] Launch lldb on demand, if needed. (PR #158701)

John Harrison via lldb-commits lldb-commits at lists.llvm.org
Tue Sep 16 09:33:29 PDT 2025


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 <harjohn at google.com>
Date: Mon, 15 Sep 2025 10:30:04 -0700
Subject: [PATCH 1/5] [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 <harjohn at google.com>
Date: Mon, 15 Sep 2025 11:02:43 -0700
Subject: [PATCH 2/5] 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 <ash at greaterthaninfinity.com>
Date: Tue, 16 Sep 2025 09:00:13 -0700
Subject: [PATCH 3/5] Update lldb/tools/lldb-mcp/lldb-mcp.cpp

Co-authored-by: Jonas Devlieghere <jonas at devlieghere.com>
---
 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();

>From 089c985de9ff62c23f31baa143f238bf645291ec Mon Sep 17 00:00:00 2001
From: John Harrison <ash at greaterthaninfinity.com>
Date: Tue, 16 Sep 2025 09:00:23 -0700
Subject: [PATCH 4/5] Update lldb/tools/lldb-mcp/lldb-mcp.cpp

Co-authored-by: Jonas Devlieghere <jonas at devlieghere.com>
---
 lldb/tools/lldb-mcp/lldb-mcp.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/lldb/tools/lldb-mcp/lldb-mcp.cpp b/lldb/tools/lldb-mcp/lldb-mcp.cpp
index 775e86b8067eb..af8424d2b39a1 100644
--- a/lldb/tools/lldb-mcp/lldb-mcp.cpp
+++ b/lldb/tools/lldb-mcp/lldb-mcp.cpp
@@ -68,9 +68,8 @@ FileSpec driverPath() {
   // 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)) {
+  if (fs.Exists(lldb_exe_path)) 
     return FileSpec(lldb_exe_path);
-  }
 
   FileSpec lldb_exec_spec = lldb_private::HostInfo::GetProgramFileSpec();
   lldb_exec_spec.SetFilename("lldb");

>From 37597b4472096e08ab896e5afd842b4f6c27efd2 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 16 Sep 2025 09:33:12 -0700
Subject: [PATCH 5/5] Adding in the Windows name of the driver executable.

---
 lldb/tools/lldb-mcp/lldb-mcp.cpp | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/lldb/tools/lldb-mcp/lldb-mcp.cpp b/lldb/tools/lldb-mcp/lldb-mcp.cpp
index af8424d2b39a1..68e987237cc69 100644
--- a/lldb/tools/lldb-mcp/lldb-mcp.cpp
+++ b/lldb/tools/lldb-mcp/lldb-mcp.cpp
@@ -52,6 +52,12 @@ using lldb_private::NativeFile;
 
 namespace {
 
+#if defined(_WIN32)
+constexpr StringLiteral kDriverName = "lldb.exe";
+#else
+constexpr StringLiteral kDriverName = "lldb";
+#endif
+
 constexpr size_t kForwardIOBufferSize = 1024;
 
 inline void exitWithError(llvm::Error Err, StringRef Prefix = "") {
@@ -68,11 +74,11 @@ FileSpec driverPath() {
   // 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)) 
+  if (fs.Exists(lldb_exe_path))
     return FileSpec(lldb_exe_path);
 
   FileSpec lldb_exec_spec = lldb_private::HostInfo::GetProgramFileSpec();
-  lldb_exec_spec.SetFilename("lldb");
+  lldb_exec_spec.SetFilename(kDriverName);
   return lldb_exec_spec;
 }
 



More information about the lldb-commits mailing list