Jonas Devlieghere 329c52c100
[lldb] Change the way the shlib directory helper is set (#183637)
This PR changes the way we set the shlib directory helper. Instead of
setting it while initializing the Host plugin, we register it when
initializing the Python plugin. The motivation is that the current
approach is incompatible with the dynamically linked script
interpreters, as they will not have been loaded at the time the Host
plugin is initialized.

The downside of the new approach is that we set the helper after having
initialized the Host plugin, which theoretically introduces a small
window where someone could query the helper before it has been set.
Fortunately the window is pretty small and limited to when we're
initializing plugins, but it's less "pure" than what we had previously.
That said, I think it balances out with removing the plugin include.
2026-02-27 15:27:02 -08:00

244 lines
7.6 KiB
C++

//===----------------------------------------------------------------------===//
//
// 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/FileSystem.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/MainLoop.h"
#include "lldb/Host/MainLoopBase.h"
#include "lldb/Host/ProcessLaunchInfo.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/FileSpec.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 <chrono>
#include <cstdlib>
#include <memory>
#include <thread>
#if defined(_WIN32)
#include <fcntl.h>
#endif
using namespace llvm;
using namespace lldb;
using namespace lldb_protocol::mcp;
using lldb_private::Environment;
using lldb_private::File;
using lldb_private::FileSpec;
using lldb_private::FileSystem;
using lldb_private::Host;
using lldb_private::MainLoop;
using lldb_private::MainLoopBase;
using lldb_private::NativeFile;
namespace {
#if defined(_WIN32)
constexpr StringLiteral kDriverName = "lldb.exe";
#else
constexpr StringLiteral kDriverName = "lldb";
#endif
constexpr size_t kForwardIOBufferSize = 1024;
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);
}
FileSpec driverPath() {
Environment host_env = Host::GetEnvironment();
// Check if an override for which lldb we're using exists, otherwise look next
// to the current binary.
std::string lldb_exe_path = host_env.lookup("LLDB_EXE_PATH");
auto &fs = FileSystem::Instance();
if (fs.Exists(lldb_exe_path))
return FileSpec(lldb_exe_path);
FileSpec lldb_exec_spec = lldb_private::HostInfo::GetProgramFileSpec();
lldb_exec_spec.SetFilename(kDriverName);
return lldb_exec_spec;
}
llvm::Error launch() {
FileSpec lldb_exec = driverPath();
lldb_private::ProcessLaunchInfo info;
info.SetMonitorProcessCallback(
&lldb_private::ProcessLaunchInfo::NoOpMonitorCallback);
info.SetExecutableFile(lldb_exec,
/*add_exe_file_as_first_arg=*/true);
info.GetArguments().AppendArgument("-O");
info.GetArguments().AppendArgument("protocol start MCP");
return Host::LaunchProcess(info).takeError();
}
Expected<ServerInfo> loadOrStart(
// FIXME: This should become a CLI arg.
lldb_private::Timeout<std::micro> timeout = std::chrono::seconds(30)) {
using namespace std::chrono;
bool started = false;
const auto deadline = steady_clock::now() + *timeout;
while (steady_clock::now() < deadline) {
Expected<std::vector<ServerInfo>> servers = ServerInfo::Load();
if (!servers)
return servers.takeError();
if (servers->empty()) {
if (!started) {
started = true;
if (llvm::Error err = launch())
return std::move(err);
}
// FIXME: Can we use MainLoop to watch the directory?
std::this_thread::sleep_for(microseconds(250));
continue;
}
// FIXME: Support selecting / multiplexing a specific lldb instance.
if (servers->size() > 1)
return createStringError("too many MCP servers running, picking a "
"specific one is not yet implemented");
return servers->front();
}
return createStringError("timed out waiting for MCP server to start");
}
void forwardIO(MainLoopBase &loop, IOObjectSP &from, 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));
}
llvm::Error connectAndForwardIO(lldb_private::MainLoop &loop, ServerInfo &info,
IOObjectSP &input_sp, IOObjectSP &output_sp) {
auto uri = lldb_private::URI::Parse(info.connection_uri);
if (!uri)
return createStringError("invalid connection_uri");
std::optional<lldb_private::Socket::ProtocolModePair> protocol_and_mode =
lldb_private::Socket::GetProtocolAndMode(uri->scheme);
if (!protocol_and_mode)
return createStringError("unknown protocol scheme");
lldb_private::Status status;
std::unique_ptr<lldb_private::Socket> sock =
lldb_private::Socket::Create(protocol_and_mode->first, status);
if (status.Fail())
return 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())
return 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())
return status.takeError();
auto socket_handle = loop.RegisterReadObject(
sock_sp, std::bind(forwardIO, std::placeholders::_1, sock_sp, output_sp),
status);
if (status.Fail())
return status.takeError();
return loop.Run().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>()))
exitWithError(std::move(err));
llvm::scope_exit cleanup([] { 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);
Expected<ServerInfo> server_info = loadOrStart();
if (!server_info)
exitWithError(server_info.takeError());
static MainLoop loop;
sys::SetInterruptFunction([]() {
loop.AddPendingCallback(
[](MainLoopBase &loop) { loop.RequestTermination(); });
});
if (llvm::Error error =
connectAndForwardIO(loop, *server_info, input_sp, output_sp))
exitWithError(std::move(error));
return EXIT_SUCCESS;
}