8
8
9
9
#include " lldb/Host/Config.h"
10
10
#include " lldb/Host/File.h"
11
+ #include " lldb/Host/FileSystem.h"
12
+ #include " lldb/Host/Host.h"
11
13
#include " lldb/Host/MainLoop.h"
12
14
#include " lldb/Host/MainLoopBase.h"
15
+ #include " lldb/Host/ProcessLaunchInfo.h"
13
16
#include " lldb/Host/Socket.h"
14
17
#include " lldb/Initialization/SystemInitializerCommon.h"
15
18
#include " lldb/Initialization/SystemLifetimeManager.h"
16
19
#include " lldb/Protocol/MCP/Server.h"
20
+ #include " lldb/Utility/FileSpec.h"
17
21
#include " lldb/Utility/Status.h"
18
22
#include " lldb/Utility/UriParser.h"
19
23
#include " lldb/lldb-forward.h"
24
28
#include " llvm/Support/ManagedStatic.h"
25
29
#include " llvm/Support/Signals.h"
26
30
#include " llvm/Support/WithColor.h"
31
+ #include < chrono>
27
32
#include < cstdlib>
28
33
#include < memory>
34
+ #include < thread>
29
35
30
36
#if defined(_WIN32)
31
37
#include < fcntl.h>
@@ -35,24 +41,93 @@ using namespace llvm;
35
41
using namespace lldb ;
36
42
using namespace lldb_protocol ::mcp;
37
43
44
+ using lldb_private::Environment;
38
45
using lldb_private::File;
46
+ using lldb_private::FileSpec;
47
+ using lldb_private::FileSystem;
48
+ using lldb_private::Host;
39
49
using lldb_private::MainLoop;
40
50
using lldb_private::MainLoopBase;
41
51
using lldb_private::NativeFile;
42
52
43
53
namespace {
44
54
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
+
45
63
inline void exitWithError (llvm::Error Err, StringRef Prefix = " " ) {
46
64
handleAllErrors (std::move (Err), [&](ErrorInfoBase &Info) {
47
65
WithColor::error (errs (), Prefix) << Info.message () << ' \n ' ;
48
66
});
49
67
std::exit (EXIT_FAILURE);
50
68
}
51
69
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
+ }
53
129
54
- void forwardIO (lldb_private::MainLoopBase &loop, lldb::IOObjectSP &from,
55
- lldb::IOObjectSP &to) {
130
+ void forwardIO (MainLoopBase &loop, IOObjectSP &from, IOObjectSP &to) {
56
131
char buf[kForwardIOBufferSize ];
57
132
size_t num_bytes = sizeof (buf);
58
133
@@ -67,46 +142,47 @@ void forwardIO(lldb_private::MainLoopBase &loop, lldb::IOObjectSP &from,
67
142
exitWithError (std::move (err));
68
143
}
69
144
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) {
72
147
auto uri = lldb_private::URI::Parse (info.connection_uri );
73
148
if (!uri)
74
- exitWithError ( createStringError (" invalid connection_uri" ) );
149
+ return createStringError (" invalid connection_uri" );
75
150
76
151
std::optional<lldb_private::Socket::ProtocolModePair> protocol_and_mode =
77
152
lldb_private::Socket::GetProtocolAndMode (uri->scheme );
78
153
154
+ if (!protocol_and_mode)
155
+ return createStringError (" unknown protocol scheme" );
156
+
79
157
lldb_private::Status status;
80
158
std::unique_ptr<lldb_private::Socket> sock =
81
159
lldb_private::Socket::Create (protocol_and_mode->first , status);
82
160
83
161
if (status.Fail ())
84
- exitWithError ( status.takeError () );
162
+ return status.takeError ();
85
163
86
164
if (uri->port && !uri->hostname .empty ())
87
165
status = sock->Connect (
88
166
llvm::formatv (" [{0}]:{1}" , uri->hostname , *uri->port ).str ());
89
167
else
90
168
status = sock->Connect (uri->path );
91
169
if (status.Fail ())
92
- exitWithError ( status.takeError () );
170
+ return status.takeError ();
93
171
94
172
IOObjectSP sock_sp = std::move (sock);
95
173
auto input_handle = loop.RegisterReadObject (
96
174
input_sp, std::bind (forwardIO, std::placeholders::_1, input_sp, sock_sp),
97
175
status);
98
176
if (status.Fail ())
99
- exitWithError ( status.takeError () );
177
+ return status.takeError ();
100
178
101
179
auto socket_handle = loop.RegisterReadObject (
102
180
sock_sp, std::bind (forwardIO, std::placeholders::_1, sock_sp, output_sp),
103
181
status);
104
182
if (status.Fail ())
105
- exitWithError ( status.takeError () );
183
+ return status.takeError ();
106
184
107
- status = loop.Run ();
108
- if (status.Fail ())
109
- exitWithError (status.takeError ());
185
+ return loop.Run ().takeError ();
110
186
}
111
187
112
188
llvm::ManagedStatic<lldb_private::SystemLifetimeManager> g_debugger_lifetime;
@@ -147,30 +223,19 @@ int main(int argc, char *argv[]) {
147
223
IOObjectSP output_sp = std::make_shared<NativeFile>(
148
224
fileno (stdout), File::eOpenOptionWriteOnly, NativeFile::Unowned);
149
225
150
- static MainLoop loop;
226
+ Expected<ServerInfo> server_info = loadOrStart ();
227
+ if (!server_info)
228
+ exitWithError (server_info.takeError ());
151
229
230
+ static MainLoop loop;
152
231
sys::SetInterruptFunction ([]() {
153
232
loop.AddPendingCallback (
154
233
[](MainLoopBase &loop) { loop.RequestTermination (); });
155
234
});
156
235
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));
174
239
175
240
return EXIT_SUCCESS;
176
241
}
0 commit comments