Skip to content

Commit ba5ca37

Browse files
ashgtiJDevlieghere
andauthored
[lldb-mcp] Launch lldb on demand, if needed. (#158701)
Adding support for launching lldb with `-O protocol start MCP` if a valid ~/.lldb/lldb-mcp-*.json` file is not found. --------- Co-authored-by: Jonas Devlieghere <[email protected]>
1 parent 4be1099 commit ba5ca37

File tree

2 files changed

+97
-32
lines changed

2 files changed

+97
-32
lines changed

lldb/source/Host/common/Socket.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ Socket::GetProtocolAndMode(llvm::StringRef scheme) {
506506
.Case("unix-abstract-accept",
507507
ProtocolModePair{SocketProtocol::ProtocolUnixAbstract,
508508
SocketMode::ModeAccept})
509-
.Cases("connect", "tcp-connect",
509+
.Cases("connect", "tcp-connect", "connection",
510510
ProtocolModePair{SocketProtocol::ProtocolTcp,
511511
SocketMode::ModeConnect})
512512
.Case("udp", ProtocolModePair{SocketProtocol::ProtocolTcp,

lldb/tools/lldb-mcp/lldb-mcp.cpp

Lines changed: 96 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88

99
#include "lldb/Host/Config.h"
1010
#include "lldb/Host/File.h"
11+
#include "lldb/Host/FileSystem.h"
12+
#include "lldb/Host/Host.h"
1113
#include "lldb/Host/MainLoop.h"
1214
#include "lldb/Host/MainLoopBase.h"
15+
#include "lldb/Host/ProcessLaunchInfo.h"
1316
#include "lldb/Host/Socket.h"
1417
#include "lldb/Initialization/SystemInitializerCommon.h"
1518
#include "lldb/Initialization/SystemLifetimeManager.h"
1619
#include "lldb/Protocol/MCP/Server.h"
20+
#include "lldb/Utility/FileSpec.h"
1721
#include "lldb/Utility/Status.h"
1822
#include "lldb/Utility/UriParser.h"
1923
#include "lldb/lldb-forward.h"
@@ -24,8 +28,10 @@
2428
#include "llvm/Support/ManagedStatic.h"
2529
#include "llvm/Support/Signals.h"
2630
#include "llvm/Support/WithColor.h"
31+
#include <chrono>
2732
#include <cstdlib>
2833
#include <memory>
34+
#include <thread>
2935

3036
#if defined(_WIN32)
3137
#include <fcntl.h>
@@ -35,24 +41,93 @@ using namespace llvm;
3541
using namespace lldb;
3642
using namespace lldb_protocol::mcp;
3743

44+
using lldb_private::Environment;
3845
using lldb_private::File;
46+
using lldb_private::FileSpec;
47+
using lldb_private::FileSystem;
48+
using lldb_private::Host;
3949
using lldb_private::MainLoop;
4050
using lldb_private::MainLoopBase;
4151
using lldb_private::NativeFile;
4252

4353
namespace {
4454

55+
#if defined(_WIN32)
56+
constexpr StringLiteral kDriverName = "lldb.exe";
57+
#else
58+
constexpr StringLiteral kDriverName = "lldb";
59+
#endif
60+
61+
constexpr size_t kForwardIOBufferSize = 1024;
62+
4563
inline void exitWithError(llvm::Error Err, StringRef Prefix = "") {
4664
handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {
4765
WithColor::error(errs(), Prefix) << Info.message() << '\n';
4866
});
4967
std::exit(EXIT_FAILURE);
5068
}
5169

52-
constexpr size_t kForwardIOBufferSize = 1024;
70+
FileSpec driverPath() {
71+
Environment host_env = Host::GetEnvironment();
72+
73+
// Check if an override for which lldb we're using exists, otherwise look next
74+
// to the current binary.
75+
std::string lldb_exe_path = host_env.lookup("LLDB_EXE_PATH");
76+
auto &fs = FileSystem::Instance();
77+
if (fs.Exists(lldb_exe_path))
78+
return FileSpec(lldb_exe_path);
79+
80+
FileSpec lldb_exec_spec = lldb_private::HostInfo::GetProgramFileSpec();
81+
lldb_exec_spec.SetFilename(kDriverName);
82+
return lldb_exec_spec;
83+
}
84+
85+
llvm::Error launch() {
86+
FileSpec lldb_exec = driverPath();
87+
lldb_private::ProcessLaunchInfo info;
88+
info.SetExecutableFile(lldb_exec,
89+
/*add_exe_file_as_first_arg=*/true);
90+
info.GetArguments().AppendArgument("-O");
91+
info.GetArguments().AppendArgument("protocol start MCP");
92+
return Host::LaunchProcess(info).takeError();
93+
}
94+
95+
Expected<ServerInfo> loadOrStart(
96+
// FIXME: This should become a CLI arg.
97+
lldb_private::Timeout<std::micro> timeout = std::chrono::seconds(30)) {
98+
using namespace std::chrono;
99+
bool started = false;
100+
101+
const auto deadline = steady_clock::now() + *timeout;
102+
while (steady_clock::now() < deadline) {
103+
Expected<std::vector<ServerInfo>> servers = ServerInfo::Load();
104+
if (!servers)
105+
return servers.takeError();
106+
107+
if (servers->empty()) {
108+
if (!started) {
109+
started = true;
110+
if (llvm::Error err = launch())
111+
return std::move(err);
112+
}
113+
114+
// FIXME: Can we use MainLoop to watch the directory?
115+
std::this_thread::sleep_for(microseconds(250));
116+
continue;
117+
}
118+
119+
// FIXME: Support selecting / multiplexing a specific lldb instance.
120+
if (servers->size() > 1)
121+
return createStringError("too many MCP servers running, picking a "
122+
"specific one is not yet implemented");
123+
124+
return servers->front();
125+
}
126+
127+
return createStringError("timed out waiting for MCP server to start");
128+
}
53129

54-
void forwardIO(lldb_private::MainLoopBase &loop, lldb::IOObjectSP &from,
55-
lldb::IOObjectSP &to) {
130+
void forwardIO(MainLoopBase &loop, IOObjectSP &from, IOObjectSP &to) {
56131
char buf[kForwardIOBufferSize];
57132
size_t num_bytes = sizeof(buf);
58133

@@ -67,46 +142,47 @@ void forwardIO(lldb_private::MainLoopBase &loop, lldb::IOObjectSP &from,
67142
exitWithError(std::move(err));
68143
}
69144

70-
void connectAndForwardIO(lldb_private::MainLoop &loop, ServerInfo &info,
71-
IOObjectSP &input_sp, IOObjectSP &output_sp) {
145+
llvm::Error connectAndForwardIO(lldb_private::MainLoop &loop, ServerInfo &info,
146+
IOObjectSP &input_sp, IOObjectSP &output_sp) {
72147
auto uri = lldb_private::URI::Parse(info.connection_uri);
73148
if (!uri)
74-
exitWithError(createStringError("invalid connection_uri"));
149+
return createStringError("invalid connection_uri");
75150

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

154+
if (!protocol_and_mode)
155+
return createStringError("unknown protocol scheme");
156+
79157
lldb_private::Status status;
80158
std::unique_ptr<lldb_private::Socket> sock =
81159
lldb_private::Socket::Create(protocol_and_mode->first, status);
82160

83161
if (status.Fail())
84-
exitWithError(status.takeError());
162+
return status.takeError();
85163

86164
if (uri->port && !uri->hostname.empty())
87165
status = sock->Connect(
88166
llvm::formatv("[{0}]:{1}", uri->hostname, *uri->port).str());
89167
else
90168
status = sock->Connect(uri->path);
91169
if (status.Fail())
92-
exitWithError(status.takeError());
170+
return status.takeError();
93171

94172
IOObjectSP sock_sp = std::move(sock);
95173
auto input_handle = loop.RegisterReadObject(
96174
input_sp, std::bind(forwardIO, std::placeholders::_1, input_sp, sock_sp),
97175
status);
98176
if (status.Fail())
99-
exitWithError(status.takeError());
177+
return status.takeError();
100178

101179
auto socket_handle = loop.RegisterReadObject(
102180
sock_sp, std::bind(forwardIO, std::placeholders::_1, sock_sp, output_sp),
103181
status);
104182
if (status.Fail())
105-
exitWithError(status.takeError());
183+
return status.takeError();
106184

107-
status = loop.Run();
108-
if (status.Fail())
109-
exitWithError(status.takeError());
185+
return loop.Run().takeError();
110186
}
111187

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

150-
static MainLoop loop;
226+
Expected<ServerInfo> server_info = loadOrStart();
227+
if (!server_info)
228+
exitWithError(server_info.takeError());
151229

230+
static MainLoop loop;
152231
sys::SetInterruptFunction([]() {
153232
loop.AddPendingCallback(
154233
[](MainLoopBase &loop) { loop.RequestTermination(); });
155234
});
156235

157-
auto existing_servers = ServerInfo::Load();
158-
159-
if (!existing_servers)
160-
exitWithError(existing_servers.takeError());
161-
162-
// FIXME: Launch `lldb -o 'protocol start MCP'`.
163-
if (existing_servers->empty())
164-
exitWithError(createStringError("No MCP servers running"));
165-
166-
// FIXME: Support selecting a specific server.
167-
if (existing_servers->size() != 1)
168-
exitWithError(
169-
createStringError("To many MCP servers running, picking a specific "
170-
"one is not yet implemented."));
171-
172-
ServerInfo &info = existing_servers->front();
173-
connectAndForwardIO(loop, info, input_sp, output_sp);
236+
if (llvm::Error error =
237+
connectAndForwardIO(loop, *server_info, input_sp, output_sp))
238+
exitWithError(std::move(error));
174239

175240
return EXIT_SUCCESS;
176241
}

0 commit comments

Comments
 (0)