Skip to content

🍒 LLDB MCP Cherrypicks #11093

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: stable/20240723
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lldb/cmake/modules/LLDBConfig.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ add_optional_dependency(LLDB_ENABLE_FBSDVMCORE "Enable libfbsdvmcore support in

option(LLDB_USE_ENTITLEMENTS "When codesigning, use entitlements if available" ON)
option(LLDB_BUILD_FRAMEWORK "Build LLDB.framework (Darwin only)" OFF)
option(LLDB_ENABLE_PROTOCOL_SERVERS "Enable protocol servers (e.g. MCP) in LLDB" ON)
option(LLDB_NO_INSTALL_DEFAULT_RPATH "Disable default RPATH settings in binaries" OFF)
option(LLDB_USE_SYSTEM_DEBUGSERVER "Use the system's debugserver for testing (Darwin only)." OFF)
option(LLDB_SKIP_STRIP "Whether to skip stripping of binaries when installing lldb." OFF)
Expand Down
2 changes: 1 addition & 1 deletion lldb/include/lldb/Core/Debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,

bool GetNotifyVoid() const;

const std::string &GetInstanceName() { return m_instance_name; }
const std::string &GetInstanceName() const { return m_instance_name; }

bool GetShowInlineDiagnostics() const;

Expand Down
11 changes: 11 additions & 0 deletions lldb/include/lldb/Core/PluginManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,17 @@ class PluginManager {
static void AutoCompleteProcessName(llvm::StringRef partial_name,
CompletionRequest &request);

// Protocol
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
ProtocolServerCreateInstance create_callback);

static bool UnregisterPlugin(ProtocolServerCreateInstance create_callback);

static llvm::StringRef GetProtocolServerPluginNameAtIndex(uint32_t idx);

static ProtocolServerCreateInstance
GetProtocolCreateCallbackForPluginName(llvm::StringRef name);

// Register Type Provider
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
RegisterTypeBuilderCreateInstance create_callback);
Expand Down
40 changes: 40 additions & 0 deletions lldb/include/lldb/Core/ProtocolServer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===-- ProtocolServer.h --------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_CORE_PROTOCOLSERVER_H
#define LLDB_CORE_PROTOCOLSERVER_H

#include "lldb/Core/PluginInterface.h"
#include "lldb/Host/Socket.h"
#include "lldb/lldb-private-interfaces.h"

namespace lldb_private {

class ProtocolServer : public PluginInterface {
public:
ProtocolServer() = default;
virtual ~ProtocolServer() = default;

static ProtocolServer *GetOrCreate(llvm::StringRef name);

static std::vector<llvm::StringRef> GetSupportedProtocols();

struct Connection {
Socket::SocketProtocol protocol;
std::string name;
};

virtual llvm::Error Start(Connection connection) = 0;
virtual llvm::Error Stop() = 0;

virtual Socket *GetSocket() const = 0;
};

} // namespace lldb_private

#endif
141 changes: 141 additions & 0 deletions lldb/include/lldb/Host/JSONTransport.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
//===-- JSONTransport.h ---------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Transport layer for encoding and decoding JSON protocol messages.
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_HOST_JSONTRANSPORT_H
#define LLDB_HOST_JSONTRANSPORT_H

#include "lldb/lldb-forward.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JSON.h"
#include <chrono>
#include <system_error>

namespace lldb_private {

class TransportEOFError : public llvm::ErrorInfo<TransportEOFError> {
public:
static char ID;

TransportEOFError() = default;

void log(llvm::raw_ostream &OS) const override {
OS << "transport end of file reached";
}
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}
};

class TransportTimeoutError : public llvm::ErrorInfo<TransportTimeoutError> {
public:
static char ID;

TransportTimeoutError() = default;

void log(llvm::raw_ostream &OS) const override {
OS << "transport operation timed out";
}
std::error_code convertToErrorCode() const override {
return std::make_error_code(std::errc::timed_out);
}
};

class TransportInvalidError : public llvm::ErrorInfo<TransportInvalidError> {
public:
static char ID;

TransportInvalidError() = default;

void log(llvm::raw_ostream &OS) const override {
OS << "transport IO object invalid";
}
std::error_code convertToErrorCode() const override {
return std::make_error_code(std::errc::not_connected);
}
};

/// A transport class that uses JSON for communication.
class JSONTransport {
public:
JSONTransport(lldb::IOObjectSP input, lldb::IOObjectSP output);
virtual ~JSONTransport() = default;

/// Transport is not copyable.
/// @{
JSONTransport(const JSONTransport &rhs) = delete;
void operator=(const JSONTransport &rhs) = delete;
/// @}

/// Writes a message to the output stream.
template <typename T> llvm::Error Write(const T &t) {
const std::string message = llvm::formatv("{0}", toJSON(t)).str();
return WriteImpl(message);
}

/// Reads the next message from the input stream.
template <typename T>
llvm::Expected<T> Read(const std::chrono::microseconds &timeout) {
llvm::Expected<std::string> message = ReadImpl(timeout);
if (!message)
return message.takeError();
return llvm::json::parse<T>(/*JSON=*/*message);
}

protected:
virtual void Log(llvm::StringRef message);

virtual llvm::Error WriteImpl(const std::string &message) = 0;
virtual llvm::Expected<std::string>
ReadImpl(const std::chrono::microseconds &timeout) = 0;

lldb::IOObjectSP m_input;
lldb::IOObjectSP m_output;
};

/// A transport class for JSON with a HTTP header.
class HTTPDelimitedJSONTransport : public JSONTransport {
public:
HTTPDelimitedJSONTransport(lldb::IOObjectSP input, lldb::IOObjectSP output)
: JSONTransport(input, output) {}
virtual ~HTTPDelimitedJSONTransport() = default;

protected:
virtual llvm::Error WriteImpl(const std::string &message) override;
virtual llvm::Expected<std::string>
ReadImpl(const std::chrono::microseconds &timeout) override;

// FIXME: Support any header.
static constexpr llvm::StringLiteral kHeaderContentLength =
"Content-Length: ";
static constexpr llvm::StringLiteral kHeaderSeparator = "\r\n\r\n";
};

/// A transport class for JSON RPC.
class JSONRPCTransport : public JSONTransport {
public:
JSONRPCTransport(lldb::IOObjectSP input, lldb::IOObjectSP output)
: JSONTransport(input, output) {}
virtual ~JSONRPCTransport() = default;

protected:
virtual llvm::Error WriteImpl(const std::string &message) override;
virtual llvm::Expected<std::string>
ReadImpl(const std::chrono::microseconds &timeout) override;

static constexpr llvm::StringLiteral kMessageSeparator = "\n";
};

} // namespace lldb_private

#endif
27 changes: 12 additions & 15 deletions lldb/include/lldb/Host/PipeBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@
#ifndef LLDB_HOST_PIPEBASE_H
#define LLDB_HOST_PIPEBASE_H

#include <chrono>
#include <string>

#include "lldb/Utility/Status.h"
#include "lldb/Utility/Timeout.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"

namespace lldb_private {
class PipeBase {
Expand All @@ -32,10 +31,9 @@ class PipeBase {
virtual Status OpenAsReader(llvm::StringRef name,
bool child_process_inherit) = 0;

Status OpenAsWriter(llvm::StringRef name, bool child_process_inherit);
virtual Status
OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit,
const std::chrono::microseconds &timeout) = 0;
virtual llvm::Error OpenAsWriter(llvm::StringRef name,
bool child_process_inherit,
const Timeout<std::micro> &timeout) = 0;

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

virtual Status WriteWithTimeout(const void *buf, size_t size,
const std::chrono::microseconds &timeout,
size_t &bytes_written) = 0;
Status Write(const void *buf, size_t size, size_t &bytes_written);
virtual Status ReadWithTimeout(void *buf, size_t size,
const std::chrono::microseconds &timeout,
size_t &bytes_read) = 0;
Status Read(void *buf, size_t size, size_t &bytes_read);
virtual llvm::Expected<size_t>
Write(const void *buf, size_t size,
const Timeout<std::micro> &timeout = std::nullopt) = 0;

virtual llvm::Expected<size_t>
Read(void *buf, size_t size,
const Timeout<std::micro> &timeout = std::nullopt) = 0;
};
}

Expand Down
28 changes: 27 additions & 1 deletion lldb/include/lldb/Host/Socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

#include <memory>
#include <string>
#include <vector>

#include "lldb/Host/MainLoopBase.h"
#include "lldb/lldb-private.h"

#include "lldb/Host/SocketAddress.h"
Expand Down Expand Up @@ -71,6 +73,11 @@ class Socket : public IOObject {
ProtocolUnixAbstract
};

enum SocketMode {
ModeAccept,
ModeConnect,
};

struct HostAndPort {
std::string hostname;
uint16_t port;
Expand All @@ -80,6 +87,10 @@ class Socket : public IOObject {
}
};

using ProtocolModePair = std::pair<SocketProtocol, SocketMode>;
static std::optional<ProtocolModePair>
GetProtocolAndMode(llvm::StringRef scheme);

static const NativeSocket kInvalidSocketValue;

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

virtual Status Connect(llvm::StringRef name) = 0;
virtual Status Listen(llvm::StringRef name, int backlog) = 0;
virtual Status Accept(Socket *&socket) = 0;

// Use the provided main loop instance to accept new connections. The callback
// will be called (from MainLoop::Run) for each new connection. This function
// does not block.
virtual llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>>
Accept(MainLoopBase &loop,
std::function<void(std::unique_ptr<Socket> socket)> sock_cb) = 0;

// Accept a single connection and "return" it in the pointer argument. This
// function blocks until the connection arrives.
virtual Status Accept(Socket *&socket);

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

// If the Socket is listening then return the URI for clients to connect.
virtual std::vector<std::string> GetListeningConnectionURI() const {
return {};
}

protected:
Socket(SocketProtocol protocol, bool should_close,
bool m_child_process_inherit);
Expand Down
14 changes: 6 additions & 8 deletions lldb/include/lldb/Host/common/TCPSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "lldb/Host/Socket.h"
#include "lldb/Host/SocketAddress.h"
#include <map>
#include <string>
#include <vector>

namespace lldb_private {
class TCPSocket : public Socket {
Expand Down Expand Up @@ -42,23 +44,19 @@ class TCPSocket : public Socket {
Status Connect(llvm::StringRef name) override;
Status Listen(llvm::StringRef name, int backlog) override;

// Use the provided main loop instance to accept new connections. The callback
// will be called (from MainLoop::Run) for each new connection. This function
// does not block.
using Socket::Accept;
llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>>
Accept(MainLoopBase &loop,
std::function<void(std::unique_ptr<TCPSocket> socket)> sock_cb);

// Accept a single connection and "return" it in the pointer argument. This
// function blocks until the connection arrives.
Status Accept(Socket *&conn_socket) override;
std::function<void(std::unique_ptr<Socket> socket)> sock_cb) override;

Status CreateSocket(int domain);

bool IsValid() const override;

std::string GetRemoteConnectionURI() const override;

std::vector<std::string> GetListeningConnectionURI() const override;

private:
TCPSocket(NativeSocket socket, const TCPSocket &listen_socket);

Expand Down
8 changes: 7 additions & 1 deletion lldb/include/lldb/Host/common/UDPSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ class UDPSocket : public Socket {
size_t Send(const void *buf, const size_t num_bytes) override;
Status Connect(llvm::StringRef name) override;
Status Listen(llvm::StringRef name, int backlog) override;
Status Accept(Socket *&socket) override;

llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>>
Accept(MainLoopBase &loop,
std::function<void(std::unique_ptr<Socket> socket)> sock_cb) override {
return llvm::errorCodeToError(
std::make_error_code(std::errc::operation_not_supported));
}

SocketAddress m_sockaddr;
};
Expand Down
Loading