[Lldb-commits] [lldb] [lldb-mcp] Auto connect to the first running lldb mcp instance. (PR #157503)

John Harrison via lldb-commits lldb-commits at lists.llvm.org
Tue Sep 9 09:47:39 PDT 2025


https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/157503

>From ec45d323014b15ce7a960fc519ef3782820a44d2 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Mon, 8 Sep 2025 09:05:15 -0700
Subject: [PATCH 1/2] [lldb-mcp] Auto connect to the first running lldb mcp
 instance.

This improves the flow by automatically connecting to an exisitng lldb instance, if one is found.

Future improvements include:

* Launching a binary if an instance isn't detected.
* Multiplexing if multiple instances are detected.
---
 lldb/include/lldb/Protocol/MCP/Server.h |   5 +
 lldb/source/Protocol/MCP/Server.cpp     |  78 +++++++++++++--
 lldb/tools/lldb-mcp/CMakeLists.txt      |   1 +
 lldb/tools/lldb-mcp/lldb-mcp.cpp        | 125 ++++++++++++++++++++----
 4 files changed, 184 insertions(+), 25 deletions(-)

diff --git a/lldb/include/lldb/Protocol/MCP/Server.h b/lldb/include/lldb/Protocol/MCP/Server.h
index 254b7d9680cd8..a65c310828daf 100644
--- a/lldb/include/lldb/Protocol/MCP/Server.h
+++ b/lldb/include/lldb/Protocol/MCP/Server.h
@@ -30,6 +30,11 @@ namespace lldb_protocol::mcp {
 struct ServerInfo {
   std::string connection_uri;
   lldb::pid_t pid;
+
+  /// Writes the server info into a unique file in `~/.lldb`.
+  static llvm::Error Write(const ServerInfo &);
+  /// Loads any server info saved in `~/.lldb`.
+  static llvm::Expected<std::vector<ServerInfo>> Load();
 };
 llvm::json::Value toJSON(const ServerInfo &);
 bool fromJSON(const llvm::json::Value &, ServerInfo &, llvm::json::Path);
diff --git a/lldb/source/Protocol/MCP/Server.cpp b/lldb/source/Protocol/MCP/Server.cpp
index 0381b7f745e98..3c2c246218774 100644
--- a/lldb/source/Protocol/MCP/Server.cpp
+++ b/lldb/source/Protocol/MCP/Server.cpp
@@ -7,25 +7,89 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/Protocol/MCP/Server.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/JSONTransport.h"
 #include "lldb/Protocol/MCP/MCPError.h"
 #include "lldb/Protocol/MCP/Protocol.h"
 #include "llvm/Support/JSON.h"
 
-using namespace lldb_protocol::mcp;
 using namespace llvm;
+using namespace lldb_private;
+using namespace lldb_protocol::mcp;
 
-llvm::json::Value lldb_protocol::mcp::toJSON(const ServerInfo &SM) {
-  return llvm::json::Object{{"connection_uri", SM.connection_uri},
-                            {"pid", SM.pid}};
+json::Value lldb_protocol::mcp::toJSON(const ServerInfo &SM) {
+  return json::Object{{"connection_uri", SM.connection_uri}, {"pid", SM.pid}};
 }
 
-bool lldb_protocol::mcp::fromJSON(const llvm::json::Value &V, ServerInfo &SM,
-                                  llvm::json::Path P) {
-  llvm::json::ObjectMapper O(V, P);
+bool lldb_protocol::mcp::fromJSON(const json::Value &V, ServerInfo &SM,
+                                  json::Path P) {
+  json::ObjectMapper O(V, P);
   return O && O.map("connection_uri", SM.connection_uri) &&
          O.map("pid", SM.pid);
 }
 
+llvm::Error ServerInfo::Write(const ServerInfo &info) {
+  std::string buf = formatv("{0}", toJSON(info)).str();
+  size_t num_bytes = buf.size();
+
+  FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
+
+  Status error(llvm::sys::fs::create_directory(user_lldb_dir.GetPath()));
+  if (error.Fail())
+    return error.takeError();
+
+  FileSpec mcp_registry_entry_path = user_lldb_dir.CopyByAppendingPathComponent(
+      formatv("lldb-mcp-{0}.json", getpid()).str());
+
+  const File::OpenOptions flags = File::eOpenOptionWriteOnly |
+                                  File::eOpenOptionCanCreate |
+                                  File::eOpenOptionTruncate;
+  llvm::Expected<lldb::FileUP> file = FileSystem::Instance().Open(
+      mcp_registry_entry_path, flags, lldb::eFilePermissionsFileDefault, false);
+  if (!file)
+    return file.takeError();
+  if (llvm::Error error = (*file)->Write(buf.data(), num_bytes).takeError())
+    return error;
+  return llvm::Error::success();
+}
+
+llvm::Expected<std::vector<ServerInfo>> ServerInfo::Load() {
+  FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
+  namespace path = llvm::sys::path;
+  FileSystem &fs = FileSystem::Instance();
+  std::error_code EC;
+  llvm::vfs::directory_iterator it = fs.DirBegin(user_lldb_dir, EC);
+  llvm::vfs::directory_iterator end;
+  std::vector<ServerInfo> infos;
+  for (; it != end && !EC; it.increment(EC)) {
+    auto &entry = *it;
+    auto name = path::filename(entry.path());
+    if (!name.starts_with("lldb-mcp-") || !name.ends_with(".json")) {
+      continue;
+    }
+
+    llvm::Expected<std::unique_ptr<File>> file =
+        fs.Open(FileSpec(entry.path()), File::eOpenOptionReadOnly);
+    if (!file)
+      return file.takeError();
+
+    char buf[1024] = {0};
+    size_t bytes_read = sizeof(buf);
+    if (llvm::Error error = (*file)->Read(buf, bytes_read).takeError())
+      return std::move(error);
+
+    auto info = json::parse<ServerInfo>(StringRef(buf, bytes_read));
+    if (!info)
+      return info.takeError();
+
+    infos.emplace_back(std::move(*info));
+  }
+
+  return infos;
+}
+
 Server::Server(std::string name, std::string version,
                std::unique_ptr<MCPTransport> transport_up,
                lldb_private::MainLoop &loop)
diff --git a/lldb/tools/lldb-mcp/CMakeLists.txt b/lldb/tools/lldb-mcp/CMakeLists.txt
index 7fe3301ab3081..5f61a1993cea3 100644
--- a/lldb/tools/lldb-mcp/CMakeLists.txt
+++ b/lldb/tools/lldb-mcp/CMakeLists.txt
@@ -6,6 +6,7 @@ add_lldb_tool(lldb-mcp
     Support
   LINK_LIBS
     liblldb
+    lldbInitialization
     lldbHost
     lldbProtocolMCP
   )
diff --git a/lldb/tools/lldb-mcp/lldb-mcp.cpp b/lldb/tools/lldb-mcp/lldb-mcp.cpp
index 6c4ebbaa5f230..498eaaaa8f383 100644
--- a/lldb/tools/lldb-mcp/lldb-mcp.cpp
+++ b/lldb/tools/lldb-mcp/lldb-mcp.cpp
@@ -10,17 +10,28 @@
 #include "lldb/Host/File.h"
 #include "lldb/Host/MainLoop.h"
 #include "lldb/Host/MainLoopBase.h"
-#include "lldb/Protocol/MCP/Protocol.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/Status.h"
+#include "lldb/Utility/UriParser.h"
+#include "lldb/lldb-forward.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/WithColor.h"
+#include <cstdlib>
+#include <memory>
 
 #if defined(_WIN32)
 #include <fcntl.h>
 #endif
 
+using namespace llvm;
+using namespace lldb;
 using namespace lldb_protocol::mcp;
 
 using lldb_private::File;
@@ -28,8 +39,78 @@ using lldb_private::MainLoop;
 using lldb_private::MainLoopBase;
 using lldb_private::NativeFile;
 
-static constexpr llvm::StringLiteral kName = "lldb-mcp";
-static constexpr llvm::StringLiteral kVersion = "0.1.0";
+namespace {
+
+inline void error(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;
+
+void forwardIO(lldb_private::MainLoopBase &loop, lldb::IOObjectSP &from,
+               lldb::IOObjectSP &to) {
+  char buf[kForwardIOBufferSize];
+  size_t num_bytes = sizeof(buf);
+
+  if (llvm::Error err = from->Read(buf, num_bytes).takeError())
+    error(std::move(err));
+
+  // EOF reached.
+  if (num_bytes == 0)
+    return loop.RequestTermination();
+
+  if (llvm::Error err = to->Write(buf, num_bytes).takeError())
+    error(std::move(err));
+}
+
+void connectAndForwardIO(lldb_private::MainLoop &loop, ServerInfo &info,
+                         IOObjectSP &input_sp, IOObjectSP &output_sp) {
+  auto uri = lldb_private::URI::Parse(info.connection_uri);
+  if (!uri)
+    error(createStringError("invalid connection_uri"));
+
+  std::optional<lldb_private::Socket::ProtocolModePair> protocol_and_mode =
+      lldb_private::Socket::GetProtocolAndMode(uri->scheme);
+
+  lldb_private::Status status;
+  std::unique_ptr<lldb_private::Socket> sock =
+      lldb_private::Socket::Create(protocol_and_mode->first, status);
+
+  if (status.Fail())
+    error(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())
+    error(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())
+    error(status.takeError());
+
+  auto socket_handle = loop.RegisterReadObject(
+      sock_sp, std::bind(forwardIO, std::placeholders::_1, sock_sp, output_sp),
+      status);
+  if (status.Fail())
+    error(status.takeError());
+
+  status = loop.Run();
+  if (status.Fail())
+    error(status.takeError());
+}
+
+llvm::ManagedStatic<lldb_private::SystemLifetimeManager> g_debugger_lifetime;
+
+} // namespace
 
 int main(int argc, char *argv[]) {
   llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
@@ -53,33 +134,41 @@ int main(int argc, char *argv[]) {
   assert(result);
 #endif
 
-  lldb::IOObjectSP input = std::make_shared<NativeFile>(
+  if (auto e = g_debugger_lifetime->Initialize(
+          std::make_unique<lldb_private::SystemInitializerCommon>(nullptr)))
+    error(std::move(e));
+
+  IOObjectSP input_sp = std::make_shared<NativeFile>(
       fileno(stdin), File::eOpenOptionReadOnly, NativeFile::Unowned);
 
-  lldb::IOObjectSP output = std::make_shared<NativeFile>(
+  IOObjectSP output_sp = std::make_shared<NativeFile>(
       fileno(stdout), File::eOpenOptionWriteOnly, NativeFile::Unowned);
 
-  constexpr llvm::StringLiteral client_name = "stdio";
   static MainLoop loop;
 
-  llvm::sys::SetInterruptFunction([]() {
+  sys::SetInterruptFunction([]() {
     loop.AddPendingCallback(
         [](MainLoopBase &loop) { loop.RequestTermination(); });
   });
 
-  auto transport_up = std::make_unique<lldb_protocol::mcp::Transport>(
-      input, output, [&](llvm::StringRef message) {
-        llvm::errs() << formatv("{0}: {1}", client_name, message) << '\n';
-      });
+  auto existing_servers = ServerInfo::Load();
+
+  if (!existing_servers)
+    error(existing_servers.takeError());
+
+  // FIXME: Launch `lldb -o 'protocol start MCP'`.
+  if (existing_servers->empty())
+    error(createStringError("No MCP servers running"));
+
+  // FIXME: Support selecting a specific server.
+  if (existing_servers->size() != 1)
+    error(createStringError("To many MCP servers running, picking a specific "
+                            "one is not yet implemented."));
 
-  auto instance_up = std::make_unique<lldb_protocol::mcp::Server>(
-      std::string(kName), std::string(kVersion), std::move(transport_up), loop);
+  ServerInfo &info = existing_servers->front();
+  connectAndForwardIO(loop, info, input_sp, output_sp);
 
-  if (llvm::Error error = instance_up->Run()) {
-    llvm::logAllUnhandledErrors(std::move(error), llvm::WithColor::error(),
-                                "MCP error: ");
-    return EXIT_FAILURE;
-  }
+  g_debugger_lifetime->Terminate();
 
   return EXIT_SUCCESS;
 }

>From a2c1a72975b97fb113ce81ca4c1be3e7b6b56e77 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 9 Sep 2025 09:46:53 -0700
Subject: [PATCH 2/2] Creating a handle to the written server info to ensure we
 cleanup the file correctly and applying reviewer feedback.

---
 lldb/include/lldb/Protocol/MCP/Server.h       | 53 ++++++++++----
 .../Protocol/MCP/ProtocolServerMCP.cpp        | 39 +++-------
 .../Plugins/Protocol/MCP/ProtocolServerMCP.h  |  2 +-
 lldb/source/Protocol/MCP/Server.cpp           | 73 ++++++++++++-------
 lldb/tools/lldb-mcp/lldb-mcp.cpp              | 36 ++++-----
 5 files changed, 115 insertions(+), 88 deletions(-)

diff --git a/lldb/include/lldb/Protocol/MCP/Server.h b/lldb/include/lldb/Protocol/MCP/Server.h
index a65c310828daf..b674d58159550 100644
--- a/lldb/include/lldb/Protocol/MCP/Server.h
+++ b/lldb/include/lldb/Protocol/MCP/Server.h
@@ -15,9 +15,12 @@
 #include "lldb/Protocol/MCP/Resource.h"
 #include "lldb/Protocol/MCP/Tool.h"
 #include "lldb/Protocol/MCP/Transport.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/JSON.h"
+#include "llvm/Support/Signals.h"
 #include <functional>
 #include <memory>
 #include <string>
@@ -25,20 +28,6 @@
 
 namespace lldb_protocol::mcp {
 
-/// Information about this instance of lldb's MCP server for lldb-mcp to use to
-/// coordinate connecting an lldb-mcp client.
-struct ServerInfo {
-  std::string connection_uri;
-  lldb::pid_t pid;
-
-  /// Writes the server info into a unique file in `~/.lldb`.
-  static llvm::Error Write(const ServerInfo &);
-  /// Loads any server info saved in `~/.lldb`.
-  static llvm::Expected<std::vector<ServerInfo>> Load();
-};
-llvm::json::Value toJSON(const ServerInfo &);
-bool fromJSON(const llvm::json::Value &, ServerInfo &, llvm::json::Path);
-
 class Server : public MCPTransport::MessageHandler {
 public:
   Server(std::string name, std::string version,
@@ -100,6 +89,42 @@ class Server : public MCPTransport::MessageHandler {
   llvm::StringMap<NotificationHandler> m_notification_handlers;
 };
 
+class ServerInfoHandle;
+
+/// Information about this instance of lldb's MCP server for lldb-mcp to use to
+/// coordinate connecting an lldb-mcp client.
+struct ServerInfo {
+  std::string connection_uri;
+
+  /// Writes the server info into a unique file in `~/.lldb`.
+  static llvm::Expected<ServerInfoHandle> Write(const ServerInfo &);
+  /// Loads any server info saved in `~/.lldb`.
+  static llvm::Expected<std::vector<ServerInfo>> Load();
+};
+llvm::json::Value toJSON(const ServerInfo &);
+bool fromJSON(const llvm::json::Value &, ServerInfo &, llvm::json::Path);
+
+/// A handle that tracks the server info on disk and cleans up the disk record
+/// once it is no longer referenced.
+class ServerInfoHandle {
+public:
+  ServerInfoHandle();
+  explicit ServerInfoHandle(llvm::StringRef filename);
+  ~ServerInfoHandle();
+
+  ServerInfoHandle(ServerInfoHandle &&other);
+  ServerInfoHandle &operator=(ServerInfoHandle &&other) noexcept;
+
+  /// ServerIinfoHandle is not copyable.
+  /// @{
+  ServerInfoHandle(const ServerInfoHandle &) = delete;
+  ServerInfoHandle &operator=(const ServerInfoHandle &) = delete;
+  /// @}
+
+private:
+  llvm::SmallString<128> m_filename;
+};
+
 } // namespace lldb_protocol::mcp
 
 #endif
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
index 2b004c19e88a6..dc18c8e06803a 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
@@ -113,34 +113,13 @@ llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) {
   std::string address =
       llvm::join(m_listener->GetListeningConnectionURI(), ", ");
 
-  FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
-
-  Status error(llvm::sys::fs::create_directory(user_lldb_dir.GetPath()));
-  if (error.Fail())
-    return error.takeError();
-
-  m_mcp_registry_entry_path = user_lldb_dir.CopyByAppendingPathComponent(
-      formatv("lldb-mcp-{0}.json", getpid()).str());
-
-  ServerInfo info;
-  info.connection_uri = listening_uris[0];
-  info.pid = getpid();
-
-  std::string buf = formatv("{0}", toJSON(info)).str();
-  size_t num_bytes = buf.size();
-
-  const File::OpenOptions flags = File::eOpenOptionWriteOnly |
-                                  File::eOpenOptionCanCreate |
-                                  File::eOpenOptionTruncate;
-  llvm::Expected<lldb::FileUP> file =
-      FileSystem::Instance().Open(m_mcp_registry_entry_path, flags,
-                                  lldb::eFilePermissionsFileDefault, false);
-  if (!file)
-    return file.takeError();
-  if (llvm::Error error = (*file)->Write(buf.data(), num_bytes).takeError())
-    return error;
+  ServerInfo info{listening_uris[0]};
+  llvm::Expected<ServerInfoHandle> handle = ServerInfo::Write(info);
+  if (!handle)
+    return handle.takeError();
 
   m_running = true;
+  m_server_info_handle = std::move(*handle);
   m_listen_handlers = std::move(*handles);
   m_loop_thread = std::thread([=] {
     llvm::set_thread_name("protocol-server.mcp");
@@ -158,10 +137,6 @@ llvm::Error ProtocolServerMCP::Stop() {
     m_running = false;
   }
 
-  if (!m_mcp_registry_entry_path.GetPath().empty())
-    FileSystem::Instance().RemoveFile(m_mcp_registry_entry_path);
-  m_mcp_registry_entry_path.Clear();
-
   // Stop the main loop.
   m_loop.AddPendingCallback(
       [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
@@ -170,5 +145,9 @@ llvm::Error ProtocolServerMCP::Stop() {
   if (m_loop_thread.joinable())
     m_loop_thread.join();
 
+  m_listen_handlers.clear();
+  m_server_info_handle = ServerInfoHandle();
+  m_instances.clear();
+
   return llvm::Error::success();
 }
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
index 004fa3c2d05a8..0251664a2acc4 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
@@ -48,7 +48,7 @@ class ProtocolServerMCP : public ProtocolServer {
 
   bool m_running = false;
 
-  FileSpec m_mcp_registry_entry_path;
+  lldb_protocol::mcp::ServerInfoHandle m_server_info_handle;
   lldb_private::MainLoop m_loop;
   std::thread m_loop_thread;
   std::mutex m_mutex;
diff --git a/lldb/source/Protocol/MCP/Server.cpp b/lldb/source/Protocol/MCP/Server.cpp
index 3c2c246218774..f3489c620832f 100644
--- a/lldb/source/Protocol/MCP/Server.cpp
+++ b/lldb/source/Protocol/MCP/Server.cpp
@@ -13,30 +13,60 @@
 #include "lldb/Host/JSONTransport.h"
 #include "lldb/Protocol/MCP/MCPError.h"
 #include "lldb/Protocol/MCP/Protocol.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/JSON.h"
+#include "llvm/Support/Signals.h"
 
 using namespace llvm;
 using namespace lldb_private;
 using namespace lldb_protocol::mcp;
 
+ServerInfoHandle::ServerInfoHandle() : ServerInfoHandle("") {}
+
+ServerInfoHandle::ServerInfoHandle(StringRef filename) : m_filename(filename) {
+  if (!m_filename.empty())
+    sys::RemoveFileOnSignal(m_filename);
+}
+
+ServerInfoHandle::~ServerInfoHandle() {
+  if (m_filename.empty())
+    return;
+
+  sys::fs::remove(m_filename);
+  sys::DontRemoveFileOnSignal(m_filename);
+  m_filename.clear();
+}
+
+ServerInfoHandle::ServerInfoHandle(ServerInfoHandle &&other)
+    : m_filename(other.m_filename) {
+  *this = std::move(other);
+}
+
+ServerInfoHandle &
+ServerInfoHandle::operator=(ServerInfoHandle &&other) noexcept {
+  m_filename = other.m_filename;
+  other.m_filename.clear();
+  return *this;
+}
+
 json::Value lldb_protocol::mcp::toJSON(const ServerInfo &SM) {
-  return json::Object{{"connection_uri", SM.connection_uri}, {"pid", SM.pid}};
+  return json::Object{{"connection_uri", SM.connection_uri}};
 }
 
 bool lldb_protocol::mcp::fromJSON(const json::Value &V, ServerInfo &SM,
                                   json::Path P) {
   json::ObjectMapper O(V, P);
-  return O && O.map("connection_uri", SM.connection_uri) &&
-         O.map("pid", SM.pid);
+  return O && O.map("connection_uri", SM.connection_uri);
 }
 
-llvm::Error ServerInfo::Write(const ServerInfo &info) {
+Expected<ServerInfoHandle> ServerInfo::Write(const ServerInfo &info) {
   std::string buf = formatv("{0}", toJSON(info)).str();
   size_t num_bytes = buf.size();
 
   FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
 
-  Status error(llvm::sys::fs::create_directory(user_lldb_dir.GetPath()));
+  Status error(sys::fs::create_directory(user_lldb_dir.GetPath()));
   if (error.Fail())
     return error.takeError();
 
@@ -46,41 +76,32 @@ llvm::Error ServerInfo::Write(const ServerInfo &info) {
   const File::OpenOptions flags = File::eOpenOptionWriteOnly |
                                   File::eOpenOptionCanCreate |
                                   File::eOpenOptionTruncate;
-  llvm::Expected<lldb::FileUP> file = FileSystem::Instance().Open(
-      mcp_registry_entry_path, flags, lldb::eFilePermissionsFileDefault, false);
+  Expected<lldb::FileUP> file =
+      FileSystem::Instance().Open(mcp_registry_entry_path, flags);
   if (!file)
     return file.takeError();
   if (llvm::Error error = (*file)->Write(buf.data(), num_bytes).takeError())
     return error;
-  return llvm::Error::success();
+  return ServerInfoHandle{mcp_registry_entry_path.GetPath()};
 }
 
-llvm::Expected<std::vector<ServerInfo>> ServerInfo::Load() {
-  FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
+Expected<std::vector<ServerInfo>> ServerInfo::Load() {
   namespace path = llvm::sys::path;
+  FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
   FileSystem &fs = FileSystem::Instance();
   std::error_code EC;
-  llvm::vfs::directory_iterator it = fs.DirBegin(user_lldb_dir, EC);
-  llvm::vfs::directory_iterator end;
+  vfs::directory_iterator it = fs.DirBegin(user_lldb_dir, EC);
+  vfs::directory_iterator end;
   std::vector<ServerInfo> infos;
   for (; it != end && !EC; it.increment(EC)) {
     auto &entry = *it;
-    auto name = path::filename(entry.path());
-    if (!name.starts_with("lldb-mcp-") || !name.ends_with(".json")) {
+    auto path = entry.path();
+    auto name = path::filename(path);
+    if (!name.starts_with("lldb-mcp-") || !name.ends_with(".json"))
       continue;
-    }
-
-    llvm::Expected<std::unique_ptr<File>> file =
-        fs.Open(FileSpec(entry.path()), File::eOpenOptionReadOnly);
-    if (!file)
-      return file.takeError();
-
-    char buf[1024] = {0};
-    size_t bytes_read = sizeof(buf);
-    if (llvm::Error error = (*file)->Read(buf, bytes_read).takeError())
-      return std::move(error);
 
-    auto info = json::parse<ServerInfo>(StringRef(buf, bytes_read));
+    auto buffer = fs.CreateDataBuffer(path);
+    auto info = json::parse<ServerInfo>(toStringRef(buffer->GetData()));
     if (!info)
       return info.takeError();
 
diff --git a/lldb/tools/lldb-mcp/lldb-mcp.cpp b/lldb/tools/lldb-mcp/lldb-mcp.cpp
index 498eaaaa8f383..12545dcf3a3cc 100644
--- a/lldb/tools/lldb-mcp/lldb-mcp.cpp
+++ b/lldb/tools/lldb-mcp/lldb-mcp.cpp
@@ -17,6 +17,7 @@
 #include "lldb/Utility/Status.h"
 #include "lldb/Utility/UriParser.h"
 #include "lldb/lldb-forward.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/InitLLVM.h"
@@ -41,7 +42,7 @@ using lldb_private::NativeFile;
 
 namespace {
 
-inline void error(llvm::Error Err, StringRef Prefix = "") {
+inline void exitWithError(llvm::Error Err, StringRef Prefix = "") {
   handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {
     WithColor::error(errs(), Prefix) << Info.message() << '\n';
   });
@@ -56,21 +57,21 @@ void forwardIO(lldb_private::MainLoopBase &loop, lldb::IOObjectSP &from,
   size_t num_bytes = sizeof(buf);
 
   if (llvm::Error err = from->Read(buf, num_bytes).takeError())
-    error(std::move(err));
+    exitWithError(std::move(err));
 
   // EOF reached.
   if (num_bytes == 0)
     return loop.RequestTermination();
 
   if (llvm::Error err = to->Write(buf, num_bytes).takeError())
-    error(std::move(err));
+    exitWithError(std::move(err));
 }
 
 void connectAndForwardIO(lldb_private::MainLoop &loop, ServerInfo &info,
                          IOObjectSP &input_sp, IOObjectSP &output_sp) {
   auto uri = lldb_private::URI::Parse(info.connection_uri);
   if (!uri)
-    error(createStringError("invalid connection_uri"));
+    exitWithError(createStringError("invalid connection_uri"));
 
   std::optional<lldb_private::Socket::ProtocolModePair> protocol_and_mode =
       lldb_private::Socket::GetProtocolAndMode(uri->scheme);
@@ -80,7 +81,7 @@ void connectAndForwardIO(lldb_private::MainLoop &loop, ServerInfo &info,
       lldb_private::Socket::Create(protocol_and_mode->first, status);
 
   if (status.Fail())
-    error(status.takeError());
+    exitWithError(status.takeError());
 
   if (uri->port && !uri->hostname.empty())
     status = sock->Connect(
@@ -88,24 +89,24 @@ void connectAndForwardIO(lldb_private::MainLoop &loop, ServerInfo &info,
   else
     status = sock->Connect(uri->path);
   if (status.Fail())
-    error(status.takeError());
+    exitWithError(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())
-    error(status.takeError());
+    exitWithError(status.takeError());
 
   auto socket_handle = loop.RegisterReadObject(
       sock_sp, std::bind(forwardIO, std::placeholders::_1, sock_sp, output_sp),
       status);
   if (status.Fail())
-    error(status.takeError());
+    exitWithError(status.takeError());
 
   status = loop.Run();
   if (status.Fail())
-    error(status.takeError());
+    exitWithError(status.takeError());
 }
 
 llvm::ManagedStatic<lldb_private::SystemLifetimeManager> g_debugger_lifetime;
@@ -134,9 +135,11 @@ int main(int argc, char *argv[]) {
   assert(result);
 #endif
 
-  if (auto e = g_debugger_lifetime->Initialize(
+  if (llvm::Error err = g_debugger_lifetime->Initialize(
           std::make_unique<lldb_private::SystemInitializerCommon>(nullptr)))
-    error(std::move(e));
+    exitWithError(std::move(err));
+
+  auto cleanup = make_scope_exit([] { g_debugger_lifetime->Terminate(); });
 
   IOObjectSP input_sp = std::make_shared<NativeFile>(
       fileno(stdin), File::eOpenOptionReadOnly, NativeFile::Unowned);
@@ -154,21 +157,20 @@ int main(int argc, char *argv[]) {
   auto existing_servers = ServerInfo::Load();
 
   if (!existing_servers)
-    error(existing_servers.takeError());
+    exitWithError(existing_servers.takeError());
 
   // FIXME: Launch `lldb -o 'protocol start MCP'`.
   if (existing_servers->empty())
-    error(createStringError("No MCP servers running"));
+    exitWithError(createStringError("No MCP servers running"));
 
   // FIXME: Support selecting a specific server.
   if (existing_servers->size() != 1)
-    error(createStringError("To many MCP servers running, picking a specific "
-                            "one is not yet implemented."));
+    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);
 
-  g_debugger_lifetime->Terminate();
-
   return EXIT_SUCCESS;
 }



More information about the lldb-commits mailing list