summaryrefslogtreecommitdiff
path: root/lldb/tools/lldb-mcp/lldb-mcp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/tools/lldb-mcp/lldb-mcp.cpp')
-rw-r--r--lldb/tools/lldb-mcp/lldb-mcp.cpp176
1 files changed, 176 insertions, 0 deletions
diff --git a/lldb/tools/lldb-mcp/lldb-mcp.cpp b/lldb/tools/lldb-mcp/lldb-mcp.cpp
new file mode 100644
index 000000000000..12545dcf3a3c
--- /dev/null
+++ b/lldb/tools/lldb-mcp/lldb-mcp.cpp
@@ -0,0 +1,176 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Host/MainLoopBase.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Initialization/SystemInitializerCommon.h"
+#include "lldb/Initialization/SystemLifetimeManager.h"
+#include "lldb/Protocol/MCP/Server.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/UriParser.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/WithColor.h"
+#include <cstdlib>
+#include <memory>
+
+#if defined(_WIN32)
+#include <fcntl.h>
+#endif
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_protocol::mcp;
+
+using lldb_private::File;
+using lldb_private::MainLoop;
+using lldb_private::MainLoopBase;
+using lldb_private::NativeFile;
+
+namespace {
+
+inline void exitWithError(llvm::Error Err, StringRef Prefix = "") {
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {
+ WithColor::error(errs(), Prefix) << Info.message() << '\n';
+ });
+ std::exit(EXIT_FAILURE);
+}
+
+constexpr size_t kForwardIOBufferSize = 1024;
+
+void forwardIO(lldb_private::MainLoopBase &loop, lldb::IOObjectSP &from,
+ lldb::IOObjectSP &to) {
+ char buf[kForwardIOBufferSize];
+ size_t num_bytes = sizeof(buf);
+
+ if (llvm::Error err = from->Read(buf, num_bytes).takeError())
+ exitWithError(std::move(err));
+
+ // EOF reached.
+ if (num_bytes == 0)
+ return loop.RequestTermination();
+
+ if (llvm::Error err = to->Write(buf, num_bytes).takeError())
+ exitWithError(std::move(err));
+}
+
+void connectAndForwardIO(lldb_private::MainLoop &loop, ServerInfo &info,
+ IOObjectSP &input_sp, IOObjectSP &output_sp) {
+ auto uri = lldb_private::URI::Parse(info.connection_uri);
+ if (!uri)
+ exitWithError(createStringError("invalid connection_uri"));
+
+ std::optional<lldb_private::Socket::ProtocolModePair> protocol_and_mode =
+ lldb_private::Socket::GetProtocolAndMode(uri->scheme);
+
+ lldb_private::Status status;
+ std::unique_ptr<lldb_private::Socket> sock =
+ lldb_private::Socket::Create(protocol_and_mode->first, status);
+
+ if (status.Fail())
+ exitWithError(status.takeError());
+
+ if (uri->port && !uri->hostname.empty())
+ status = sock->Connect(
+ llvm::formatv("[{0}]:{1}", uri->hostname, *uri->port).str());
+ else
+ status = sock->Connect(uri->path);
+ if (status.Fail())
+ exitWithError(status.takeError());
+
+ IOObjectSP sock_sp = std::move(sock);
+ auto input_handle = loop.RegisterReadObject(
+ input_sp, std::bind(forwardIO, std::placeholders::_1, input_sp, sock_sp),
+ status);
+ if (status.Fail())
+ exitWithError(status.takeError());
+
+ auto socket_handle = loop.RegisterReadObject(
+ sock_sp, std::bind(forwardIO, std::placeholders::_1, sock_sp, output_sp),
+ status);
+ if (status.Fail())
+ exitWithError(status.takeError());
+
+ status = loop.Run();
+ if (status.Fail())
+ exitWithError(status.takeError());
+}
+
+llvm::ManagedStatic<lldb_private::SystemLifetimeManager> g_debugger_lifetime;
+
+} // namespace
+
+int main(int argc, char *argv[]) {
+ llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
+#if !defined(__APPLE__)
+ llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL
+ " and include the crash backtrace.\n");
+#else
+ llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL
+ " and include the crash report from "
+ "~/Library/Logs/DiagnosticReports/.\n");
+#endif
+
+#if defined(_WIN32)
+ // Windows opens stdout and stdin in text mode which converts \n to 13,10
+ // while the value is just 10 on Darwin/Linux. Setting the file mode to
+ // binary fixes this.
+ int result = _setmode(fileno(stdout), _O_BINARY);
+ assert(result);
+ result = _setmode(fileno(stdin), _O_BINARY);
+ UNUSED_IF_ASSERT_DISABLED(result);
+ assert(result);
+#endif
+
+ if (llvm::Error err = g_debugger_lifetime->Initialize(
+ std::make_unique<lldb_private::SystemInitializerCommon>(nullptr)))
+ exitWithError(std::move(err));
+
+ auto cleanup = make_scope_exit([] { g_debugger_lifetime->Terminate(); });
+
+ IOObjectSP input_sp = std::make_shared<NativeFile>(
+ fileno(stdin), File::eOpenOptionReadOnly, NativeFile::Unowned);
+
+ IOObjectSP output_sp = std::make_shared<NativeFile>(
+ fileno(stdout), File::eOpenOptionWriteOnly, NativeFile::Unowned);
+
+ static MainLoop loop;
+
+ sys::SetInterruptFunction([]() {
+ loop.AddPendingCallback(
+ [](MainLoopBase &loop) { loop.RequestTermination(); });
+ });
+
+ auto existing_servers = ServerInfo::Load();
+
+ if (!existing_servers)
+ exitWithError(existing_servers.takeError());
+
+ // FIXME: Launch `lldb -o 'protocol start MCP'`.
+ if (existing_servers->empty())
+ exitWithError(createStringError("No MCP servers running"));
+
+ // FIXME: Support selecting a specific server.
+ if (existing_servers->size() != 1)
+ exitWithError(
+ createStringError("To many MCP servers running, picking a specific "
+ "one is not yet implemented."));
+
+ ServerInfo &info = existing_servers->front();
+ connectAndForwardIO(loop, info, input_sp, output_sp);
+
+ return EXIT_SUCCESS;
+}