Skip to content

Commit af65dc6

Browse files
authored
[lldb-mcp] Auto connect to the first running lldb mcp instance. (#157503)
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.
1 parent b68ff08 commit af65dc6

File tree

6 files changed

+253
-67
lines changed

6 files changed

+253
-67
lines changed

lldb/include/lldb/Protocol/MCP/Server.h

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,19 @@
1515
#include "lldb/Protocol/MCP/Resource.h"
1616
#include "lldb/Protocol/MCP/Tool.h"
1717
#include "lldb/Protocol/MCP/Transport.h"
18+
#include "llvm/ADT/SmallString.h"
1819
#include "llvm/ADT/StringMap.h"
20+
#include "llvm/ADT/StringRef.h"
1921
#include "llvm/Support/Error.h"
2022
#include "llvm/Support/JSON.h"
23+
#include "llvm/Support/Signals.h"
2124
#include <functional>
2225
#include <memory>
2326
#include <string>
2427
#include <vector>
2528

2629
namespace lldb_protocol::mcp {
2730

28-
/// Information about this instance of lldb's MCP server for lldb-mcp to use to
29-
/// coordinate connecting an lldb-mcp client.
30-
struct ServerInfo {
31-
std::string connection_uri;
32-
lldb::pid_t pid;
33-
};
34-
llvm::json::Value toJSON(const ServerInfo &);
35-
bool fromJSON(const llvm::json::Value &, ServerInfo &, llvm::json::Path);
36-
3731
class Server : public MCPTransport::MessageHandler {
3832
public:
3933
Server(std::string name, std::string version,
@@ -95,6 +89,42 @@ class Server : public MCPTransport::MessageHandler {
9589
llvm::StringMap<NotificationHandler> m_notification_handlers;
9690
};
9791

92+
class ServerInfoHandle;
93+
94+
/// Information about this instance of lldb's MCP server for lldb-mcp to use to
95+
/// coordinate connecting an lldb-mcp client.
96+
struct ServerInfo {
97+
std::string connection_uri;
98+
99+
/// Writes the server info into a unique file in `~/.lldb`.
100+
static llvm::Expected<ServerInfoHandle> Write(const ServerInfo &);
101+
/// Loads any server info saved in `~/.lldb`.
102+
static llvm::Expected<std::vector<ServerInfo>> Load();
103+
};
104+
llvm::json::Value toJSON(const ServerInfo &);
105+
bool fromJSON(const llvm::json::Value &, ServerInfo &, llvm::json::Path);
106+
107+
/// A handle that tracks the server info on disk and cleans up the disk record
108+
/// once it is no longer referenced.
109+
class ServerInfoHandle {
110+
public:
111+
ServerInfoHandle();
112+
explicit ServerInfoHandle(llvm::StringRef filename);
113+
~ServerInfoHandle();
114+
115+
ServerInfoHandle(ServerInfoHandle &&other);
116+
ServerInfoHandle &operator=(ServerInfoHandle &&other) noexcept;
117+
118+
/// ServerIinfoHandle is not copyable.
119+
/// @{
120+
ServerInfoHandle(const ServerInfoHandle &) = delete;
121+
ServerInfoHandle &operator=(const ServerInfoHandle &) = delete;
122+
/// @}
123+
124+
private:
125+
llvm::SmallString<128> m_filename;
126+
};
127+
98128
} // namespace lldb_protocol::mcp
99129

100130
#endif

lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -113,34 +113,13 @@ llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) {
113113
std::string address =
114114
llvm::join(m_listener->GetListeningConnectionURI(), ", ");
115115

116-
FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
117-
118-
Status error(llvm::sys::fs::create_directory(user_lldb_dir.GetPath()));
119-
if (error.Fail())
120-
return error.takeError();
121-
122-
m_mcp_registry_entry_path = user_lldb_dir.CopyByAppendingPathComponent(
123-
formatv("lldb-mcp-{0}.json", getpid()).str());
124-
125-
ServerInfo info;
126-
info.connection_uri = listening_uris[0];
127-
info.pid = getpid();
128-
129-
std::string buf = formatv("{0}", toJSON(info)).str();
130-
size_t num_bytes = buf.size();
131-
132-
const File::OpenOptions flags = File::eOpenOptionWriteOnly |
133-
File::eOpenOptionCanCreate |
134-
File::eOpenOptionTruncate;
135-
llvm::Expected<lldb::FileUP> file =
136-
FileSystem::Instance().Open(m_mcp_registry_entry_path, flags,
137-
lldb::eFilePermissionsFileDefault, false);
138-
if (!file)
139-
return file.takeError();
140-
if (llvm::Error error = (*file)->Write(buf.data(), num_bytes).takeError())
141-
return error;
116+
ServerInfo info{listening_uris[0]};
117+
llvm::Expected<ServerInfoHandle> handle = ServerInfo::Write(info);
118+
if (!handle)
119+
return handle.takeError();
142120

143121
m_running = true;
122+
m_server_info_handle = std::move(*handle);
144123
m_listen_handlers = std::move(*handles);
145124
m_loop_thread = std::thread([=] {
146125
llvm::set_thread_name("protocol-server.mcp");
@@ -158,10 +137,6 @@ llvm::Error ProtocolServerMCP::Stop() {
158137
m_running = false;
159138
}
160139

161-
if (!m_mcp_registry_entry_path.GetPath().empty())
162-
FileSystem::Instance().RemoveFile(m_mcp_registry_entry_path);
163-
m_mcp_registry_entry_path.Clear();
164-
165140
// Stop the main loop.
166141
m_loop.AddPendingCallback(
167142
[](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
@@ -170,5 +145,9 @@ llvm::Error ProtocolServerMCP::Stop() {
170145
if (m_loop_thread.joinable())
171146
m_loop_thread.join();
172147

148+
m_listen_handlers.clear();
149+
m_server_info_handle = ServerInfoHandle();
150+
m_instances.clear();
151+
173152
return llvm::Error::success();
174153
}

lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class ProtocolServerMCP : public ProtocolServer {
4848

4949
bool m_running = false;
5050

51-
FileSpec m_mcp_registry_entry_path;
51+
lldb_protocol::mcp::ServerInfoHandle m_server_info_handle;
5252
lldb_private::MainLoop m_loop;
5353
std::thread m_loop_thread;
5454
std::mutex m_mutex;

lldb/source/Protocol/MCP/Server.cpp

Lines changed: 94 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,108 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "lldb/Protocol/MCP/Server.h"
10+
#include "lldb/Host/File.h"
11+
#include "lldb/Host/FileSystem.h"
12+
#include "lldb/Host/HostInfo.h"
13+
#include "lldb/Host/JSONTransport.h"
1014
#include "lldb/Protocol/MCP/MCPError.h"
1115
#include "lldb/Protocol/MCP/Protocol.h"
16+
#include "llvm/ADT/SmallString.h"
17+
#include "llvm/Support/FileSystem.h"
1218
#include "llvm/Support/JSON.h"
19+
#include "llvm/Support/Signals.h"
1320

14-
using namespace lldb_protocol::mcp;
1521
using namespace llvm;
22+
using namespace lldb_private;
23+
using namespace lldb_protocol::mcp;
24+
25+
ServerInfoHandle::ServerInfoHandle() : ServerInfoHandle("") {}
26+
27+
ServerInfoHandle::ServerInfoHandle(StringRef filename) : m_filename(filename) {
28+
if (!m_filename.empty())
29+
sys::RemoveFileOnSignal(m_filename);
30+
}
31+
32+
ServerInfoHandle::~ServerInfoHandle() {
33+
if (m_filename.empty())
34+
return;
35+
36+
sys::fs::remove(m_filename);
37+
sys::DontRemoveFileOnSignal(m_filename);
38+
m_filename.clear();
39+
}
40+
41+
ServerInfoHandle::ServerInfoHandle(ServerInfoHandle &&other)
42+
: m_filename(other.m_filename) {
43+
*this = std::move(other);
44+
}
1645

17-
llvm::json::Value lldb_protocol::mcp::toJSON(const ServerInfo &SM) {
18-
return llvm::json::Object{{"connection_uri", SM.connection_uri},
19-
{"pid", SM.pid}};
46+
ServerInfoHandle &
47+
ServerInfoHandle::operator=(ServerInfoHandle &&other) noexcept {
48+
m_filename = other.m_filename;
49+
other.m_filename.clear();
50+
return *this;
2051
}
2152

22-
bool lldb_protocol::mcp::fromJSON(const llvm::json::Value &V, ServerInfo &SM,
23-
llvm::json::Path P) {
24-
llvm::json::ObjectMapper O(V, P);
25-
return O && O.map("connection_uri", SM.connection_uri) &&
26-
O.map("pid", SM.pid);
53+
json::Value lldb_protocol::mcp::toJSON(const ServerInfo &SM) {
54+
return json::Object{{"connection_uri", SM.connection_uri}};
55+
}
56+
57+
bool lldb_protocol::mcp::fromJSON(const json::Value &V, ServerInfo &SM,
58+
json::Path P) {
59+
json::ObjectMapper O(V, P);
60+
return O && O.map("connection_uri", SM.connection_uri);
61+
}
62+
63+
Expected<ServerInfoHandle> ServerInfo::Write(const ServerInfo &info) {
64+
std::string buf = formatv("{0}", toJSON(info)).str();
65+
size_t num_bytes = buf.size();
66+
67+
FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
68+
69+
Status error(sys::fs::create_directory(user_lldb_dir.GetPath()));
70+
if (error.Fail())
71+
return error.takeError();
72+
73+
FileSpec mcp_registry_entry_path = user_lldb_dir.CopyByAppendingPathComponent(
74+
formatv("lldb-mcp-{0}.json", getpid()).str());
75+
76+
const File::OpenOptions flags = File::eOpenOptionWriteOnly |
77+
File::eOpenOptionCanCreate |
78+
File::eOpenOptionTruncate;
79+
Expected<lldb::FileUP> file =
80+
FileSystem::Instance().Open(mcp_registry_entry_path, flags);
81+
if (!file)
82+
return file.takeError();
83+
if (llvm::Error error = (*file)->Write(buf.data(), num_bytes).takeError())
84+
return error;
85+
return ServerInfoHandle{mcp_registry_entry_path.GetPath()};
86+
}
87+
88+
Expected<std::vector<ServerInfo>> ServerInfo::Load() {
89+
namespace path = llvm::sys::path;
90+
FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
91+
FileSystem &fs = FileSystem::Instance();
92+
std::error_code EC;
93+
vfs::directory_iterator it = fs.DirBegin(user_lldb_dir, EC);
94+
vfs::directory_iterator end;
95+
std::vector<ServerInfo> infos;
96+
for (; it != end && !EC; it.increment(EC)) {
97+
auto &entry = *it;
98+
auto path = entry.path();
99+
auto name = path::filename(path);
100+
if (!name.starts_with("lldb-mcp-") || !name.ends_with(".json"))
101+
continue;
102+
103+
auto buffer = fs.CreateDataBuffer(path);
104+
auto info = json::parse<ServerInfo>(toStringRef(buffer->GetData()));
105+
if (!info)
106+
return info.takeError();
107+
108+
infos.emplace_back(std::move(*info));
109+
}
110+
111+
return infos;
27112
}
28113

29114
Server::Server(std::string name, std::string version,

lldb/tools/lldb-mcp/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_lldb_tool(lldb-mcp
66
Support
77
LINK_LIBS
88
liblldb
9+
lldbInitialization
910
lldbHost
1011
lldbProtocolMCP
1112
)

0 commit comments

Comments
 (0)