Skip to content

Commit 6aa67af

Browse files
authored
Merge pull request #11093 from swiftlang/mcp-cherrypicks
🍒 LLDB MCP Cherrypicks
2 parents 5568a88 + 8a845a6 commit 6aa67af

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+3639
-302
lines changed

lldb/cmake/modules/LLDBConfig.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ add_optional_dependency(LLDB_ENABLE_FBSDVMCORE "Enable libfbsdvmcore support in
7777

7878
option(LLDB_USE_ENTITLEMENTS "When codesigning, use entitlements if available" ON)
7979
option(LLDB_BUILD_FRAMEWORK "Build LLDB.framework (Darwin only)" OFF)
80+
option(LLDB_ENABLE_PROTOCOL_SERVERS "Enable protocol servers (e.g. MCP) in LLDB" ON)
8081
option(LLDB_NO_INSTALL_DEFAULT_RPATH "Disable default RPATH settings in binaries" OFF)
8182
option(LLDB_USE_SYSTEM_DEBUGSERVER "Use the system's debugserver for testing (Darwin only)." OFF)
8283
option(LLDB_SKIP_STRIP "Whether to skip stripping of binaries when installing lldb." OFF)

lldb/include/lldb/Core/Debugger.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
376376

377377
bool GetNotifyVoid() const;
378378

379-
const std::string &GetInstanceName() { return m_instance_name; }
379+
const std::string &GetInstanceName() const { return m_instance_name; }
380380

381381
bool GetShowInlineDiagnostics() const;
382382

lldb/include/lldb/Core/PluginManager.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,17 @@ class PluginManager {
255255
static void AutoCompleteProcessName(llvm::StringRef partial_name,
256256
CompletionRequest &request);
257257

258+
// Protocol
259+
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
260+
ProtocolServerCreateInstance create_callback);
261+
262+
static bool UnregisterPlugin(ProtocolServerCreateInstance create_callback);
263+
264+
static llvm::StringRef GetProtocolServerPluginNameAtIndex(uint32_t idx);
265+
266+
static ProtocolServerCreateInstance
267+
GetProtocolCreateCallbackForPluginName(llvm::StringRef name);
268+
258269
// Register Type Provider
259270
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
260271
RegisterTypeBuilderCreateInstance create_callback);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===-- ProtocolServer.h --------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_CORE_PROTOCOLSERVER_H
10+
#define LLDB_CORE_PROTOCOLSERVER_H
11+
12+
#include "lldb/Core/PluginInterface.h"
13+
#include "lldb/Host/Socket.h"
14+
#include "lldb/lldb-private-interfaces.h"
15+
16+
namespace lldb_private {
17+
18+
class ProtocolServer : public PluginInterface {
19+
public:
20+
ProtocolServer() = default;
21+
virtual ~ProtocolServer() = default;
22+
23+
static ProtocolServer *GetOrCreate(llvm::StringRef name);
24+
25+
static std::vector<llvm::StringRef> GetSupportedProtocols();
26+
27+
struct Connection {
28+
Socket::SocketProtocol protocol;
29+
std::string name;
30+
};
31+
32+
virtual llvm::Error Start(Connection connection) = 0;
33+
virtual llvm::Error Stop() = 0;
34+
35+
virtual Socket *GetSocket() const = 0;
36+
};
37+
38+
} // namespace lldb_private
39+
40+
#endif
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
//===-- JSONTransport.h ---------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Transport layer for encoding and decoding JSON protocol messages.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLDB_HOST_JSONTRANSPORT_H
14+
#define LLDB_HOST_JSONTRANSPORT_H
15+
16+
#include "lldb/lldb-forward.h"
17+
#include "llvm/ADT/StringRef.h"
18+
#include "llvm/Support/Error.h"
19+
#include "llvm/Support/FormatVariadic.h"
20+
#include "llvm/Support/JSON.h"
21+
#include <chrono>
22+
#include <system_error>
23+
24+
namespace lldb_private {
25+
26+
class TransportEOFError : public llvm::ErrorInfo<TransportEOFError> {
27+
public:
28+
static char ID;
29+
30+
TransportEOFError() = default;
31+
32+
void log(llvm::raw_ostream &OS) const override {
33+
OS << "transport end of file reached";
34+
}
35+
std::error_code convertToErrorCode() const override {
36+
return llvm::inconvertibleErrorCode();
37+
}
38+
};
39+
40+
class TransportTimeoutError : public llvm::ErrorInfo<TransportTimeoutError> {
41+
public:
42+
static char ID;
43+
44+
TransportTimeoutError() = default;
45+
46+
void log(llvm::raw_ostream &OS) const override {
47+
OS << "transport operation timed out";
48+
}
49+
std::error_code convertToErrorCode() const override {
50+
return std::make_error_code(std::errc::timed_out);
51+
}
52+
};
53+
54+
class TransportInvalidError : public llvm::ErrorInfo<TransportInvalidError> {
55+
public:
56+
static char ID;
57+
58+
TransportInvalidError() = default;
59+
60+
void log(llvm::raw_ostream &OS) const override {
61+
OS << "transport IO object invalid";
62+
}
63+
std::error_code convertToErrorCode() const override {
64+
return std::make_error_code(std::errc::not_connected);
65+
}
66+
};
67+
68+
/// A transport class that uses JSON for communication.
69+
class JSONTransport {
70+
public:
71+
JSONTransport(lldb::IOObjectSP input, lldb::IOObjectSP output);
72+
virtual ~JSONTransport() = default;
73+
74+
/// Transport is not copyable.
75+
/// @{
76+
JSONTransport(const JSONTransport &rhs) = delete;
77+
void operator=(const JSONTransport &rhs) = delete;
78+
/// @}
79+
80+
/// Writes a message to the output stream.
81+
template <typename T> llvm::Error Write(const T &t) {
82+
const std::string message = llvm::formatv("{0}", toJSON(t)).str();
83+
return WriteImpl(message);
84+
}
85+
86+
/// Reads the next message from the input stream.
87+
template <typename T>
88+
llvm::Expected<T> Read(const std::chrono::microseconds &timeout) {
89+
llvm::Expected<std::string> message = ReadImpl(timeout);
90+
if (!message)
91+
return message.takeError();
92+
return llvm::json::parse<T>(/*JSON=*/*message);
93+
}
94+
95+
protected:
96+
virtual void Log(llvm::StringRef message);
97+
98+
virtual llvm::Error WriteImpl(const std::string &message) = 0;
99+
virtual llvm::Expected<std::string>
100+
ReadImpl(const std::chrono::microseconds &timeout) = 0;
101+
102+
lldb::IOObjectSP m_input;
103+
lldb::IOObjectSP m_output;
104+
};
105+
106+
/// A transport class for JSON with a HTTP header.
107+
class HTTPDelimitedJSONTransport : public JSONTransport {
108+
public:
109+
HTTPDelimitedJSONTransport(lldb::IOObjectSP input, lldb::IOObjectSP output)
110+
: JSONTransport(input, output) {}
111+
virtual ~HTTPDelimitedJSONTransport() = default;
112+
113+
protected:
114+
virtual llvm::Error WriteImpl(const std::string &message) override;
115+
virtual llvm::Expected<std::string>
116+
ReadImpl(const std::chrono::microseconds &timeout) override;
117+
118+
// FIXME: Support any header.
119+
static constexpr llvm::StringLiteral kHeaderContentLength =
120+
"Content-Length: ";
121+
static constexpr llvm::StringLiteral kHeaderSeparator = "\r\n\r\n";
122+
};
123+
124+
/// A transport class for JSON RPC.
125+
class JSONRPCTransport : public JSONTransport {
126+
public:
127+
JSONRPCTransport(lldb::IOObjectSP input, lldb::IOObjectSP output)
128+
: JSONTransport(input, output) {}
129+
virtual ~JSONRPCTransport() = default;
130+
131+
protected:
132+
virtual llvm::Error WriteImpl(const std::string &message) override;
133+
virtual llvm::Expected<std::string>
134+
ReadImpl(const std::chrono::microseconds &timeout) override;
135+
136+
static constexpr llvm::StringLiteral kMessageSeparator = "\n";
137+
};
138+
139+
} // namespace lldb_private
140+
141+
#endif

lldb/include/lldb/Host/PipeBase.h

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@
1010
#ifndef LLDB_HOST_PIPEBASE_H
1111
#define LLDB_HOST_PIPEBASE_H
1212

13-
#include <chrono>
14-
#include <string>
15-
1613
#include "lldb/Utility/Status.h"
14+
#include "lldb/Utility/Timeout.h"
1715
#include "llvm/ADT/SmallVector.h"
1816
#include "llvm/ADT/StringRef.h"
17+
#include "llvm/Support/Error.h"
1918

2019
namespace lldb_private {
2120
class PipeBase {
@@ -32,10 +31,9 @@ class PipeBase {
3231
virtual Status OpenAsReader(llvm::StringRef name,
3332
bool child_process_inherit) = 0;
3433

35-
Status OpenAsWriter(llvm::StringRef name, bool child_process_inherit);
36-
virtual Status
37-
OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit,
38-
const std::chrono::microseconds &timeout) = 0;
34+
virtual llvm::Error OpenAsWriter(llvm::StringRef name,
35+
bool child_process_inherit,
36+
const Timeout<std::micro> &timeout) = 0;
3937

4038
virtual bool CanRead() const = 0;
4139
virtual bool CanWrite() const = 0;
@@ -56,14 +54,13 @@ class PipeBase {
5654
// Delete named pipe.
5755
virtual Status Delete(llvm::StringRef name) = 0;
5856

59-
virtual Status WriteWithTimeout(const void *buf, size_t size,
60-
const std::chrono::microseconds &timeout,
61-
size_t &bytes_written) = 0;
62-
Status Write(const void *buf, size_t size, size_t &bytes_written);
63-
virtual Status ReadWithTimeout(void *buf, size_t size,
64-
const std::chrono::microseconds &timeout,
65-
size_t &bytes_read) = 0;
66-
Status Read(void *buf, size_t size, size_t &bytes_read);
57+
virtual llvm::Expected<size_t>
58+
Write(const void *buf, size_t size,
59+
const Timeout<std::micro> &timeout = std::nullopt) = 0;
60+
61+
virtual llvm::Expected<size_t>
62+
Read(void *buf, size_t size,
63+
const Timeout<std::micro> &timeout = std::nullopt) = 0;
6764
};
6865
}
6966

lldb/include/lldb/Host/Socket.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212
#include <memory>
1313
#include <string>
14+
#include <vector>
1415

16+
#include "lldb/Host/MainLoopBase.h"
1517
#include "lldb/lldb-private.h"
1618

1719
#include "lldb/Host/SocketAddress.h"
@@ -71,6 +73,11 @@ class Socket : public IOObject {
7173
ProtocolUnixAbstract
7274
};
7375

76+
enum SocketMode {
77+
ModeAccept,
78+
ModeConnect,
79+
};
80+
7481
struct HostAndPort {
7582
std::string hostname;
7683
uint16_t port;
@@ -80,6 +87,10 @@ class Socket : public IOObject {
8087
}
8188
};
8289

90+
using ProtocolModePair = std::pair<SocketProtocol, SocketMode>;
91+
static std::optional<ProtocolModePair>
92+
GetProtocolAndMode(llvm::StringRef scheme);
93+
8394
static const NativeSocket kInvalidSocketValue;
8495

8596
~Socket() override;
@@ -97,7 +108,17 @@ class Socket : public IOObject {
97108

98109
virtual Status Connect(llvm::StringRef name) = 0;
99110
virtual Status Listen(llvm::StringRef name, int backlog) = 0;
100-
virtual Status Accept(Socket *&socket) = 0;
111+
112+
// Use the provided main loop instance to accept new connections. The callback
113+
// will be called (from MainLoop::Run) for each new connection. This function
114+
// does not block.
115+
virtual llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>>
116+
Accept(MainLoopBase &loop,
117+
std::function<void(std::unique_ptr<Socket> socket)> sock_cb) = 0;
118+
119+
// Accept a single connection and "return" it in the pointer argument. This
120+
// function blocks until the connection arrives.
121+
virtual Status Accept(Socket *&socket);
101122

102123
// Initialize a Tcp Socket object in listening mode. listen and accept are
103124
// implemented separately because the caller may wish to manipulate or query
@@ -132,6 +153,11 @@ class Socket : public IOObject {
132153
// If this Socket is connected then return the URI used to connect.
133154
virtual std::string GetRemoteConnectionURI() const { return ""; };
134155

156+
// If the Socket is listening then return the URI for clients to connect.
157+
virtual std::vector<std::string> GetListeningConnectionURI() const {
158+
return {};
159+
}
160+
135161
protected:
136162
Socket(SocketProtocol protocol, bool should_close,
137163
bool m_child_process_inherit);

lldb/include/lldb/Host/common/TCPSocket.h

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "lldb/Host/Socket.h"
1414
#include "lldb/Host/SocketAddress.h"
1515
#include <map>
16+
#include <string>
17+
#include <vector>
1618

1719
namespace lldb_private {
1820
class TCPSocket : public Socket {
@@ -42,23 +44,19 @@ class TCPSocket : public Socket {
4244
Status Connect(llvm::StringRef name) override;
4345
Status Listen(llvm::StringRef name, int backlog) override;
4446

45-
// Use the provided main loop instance to accept new connections. The callback
46-
// will be called (from MainLoop::Run) for each new connection. This function
47-
// does not block.
47+
using Socket::Accept;
4848
llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>>
4949
Accept(MainLoopBase &loop,
50-
std::function<void(std::unique_ptr<TCPSocket> socket)> sock_cb);
51-
52-
// Accept a single connection and "return" it in the pointer argument. This
53-
// function blocks until the connection arrives.
54-
Status Accept(Socket *&conn_socket) override;
50+
std::function<void(std::unique_ptr<Socket> socket)> sock_cb) override;
5551

5652
Status CreateSocket(int domain);
5753

5854
bool IsValid() const override;
5955

6056
std::string GetRemoteConnectionURI() const override;
6157

58+
std::vector<std::string> GetListeningConnectionURI() const override;
59+
6260
private:
6361
TCPSocket(NativeSocket socket, const TCPSocket &listen_socket);
6462

lldb/include/lldb/Host/common/UDPSocket.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ class UDPSocket : public Socket {
2727
size_t Send(const void *buf, const size_t num_bytes) override;
2828
Status Connect(llvm::StringRef name) override;
2929
Status Listen(llvm::StringRef name, int backlog) override;
30-
Status Accept(Socket *&socket) override;
30+
31+
llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>>
32+
Accept(MainLoopBase &loop,
33+
std::function<void(std::unique_ptr<Socket> socket)> sock_cb) override {
34+
return llvm::errorCodeToError(
35+
std::make_error_code(std::errc::operation_not_supported));
36+
}
3137

3238
SocketAddress m_sockaddr;
3339
};

0 commit comments

Comments
 (0)