Nerixyz cdbe28887b
[LLDB] Set and verify paths of properties from tablegen (#179524)
In #168245, I attempted to dump the available settings to Markdown. That
required a full build of LLDB. However, to build the docs, only the swig
wrappers should need to be compiled. The comment was that we should be
able to use the definitions from the TableGen files.

Currently, the property definitions in don't have information about the
path where they will be available. They only contain a `Definition`
which groups properties, so they can be added to
`OptionValueProperties`.

With this PR, I'm adding the path for each property definition. For
example, `symbols.enable-external-lookup` would have `Name =
enable-external-lookup, Path = symbols`. In LLDB itself, we don't need
this path, we only need it for the documentation. To avoid mismatches
between the actual path and the declared one, I added a debug-only check
when a property group is added to a parent
(`OptionValueProperties::AppendProperty`).

The TableGen emitter for the properties now additionally emits
`g_{definition}_properties_def`, which includes both the array of
properties and the expected path. This constant has to be used to
initialize a `OptionValueProperties`.

I couldn't test this for everything (e.g. IntelPT or ProcessKDP), but
the necessary changes are simple: (1) set the `Path` in the TableGen
file, (2) update `initialize` to use `_def`.
2026-02-10 19:43:29 +01:00

242 lines
7.7 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 "Plugins/Platform/WebAssembly/PlatformWasm.h"
#include "Plugins/Platform/WebAssembly/PlatformWasmRemoteGDBServer.h"
#include "Plugins/Process/wasm/ProcessWasm.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/ProcessLaunchInfo.h"
#include "lldb/Host/common/TCPSocket.h"
#include "lldb/Interpreter/OptionValueProperties.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Listener.h"
#include "lldb/Utility/Log.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/ErrorExtras.h"
using namespace lldb;
using namespace lldb_private;
LLDB_PLUGIN_DEFINE(PlatformWasm)
namespace {
#define LLDB_PROPERTIES_platformwasm
#include "PlatformWasmProperties.inc"
enum {
#define LLDB_PROPERTIES_platformwasm
#include "PlatformWasmPropertiesEnum.inc"
};
class PluginProperties : public Properties {
public:
PluginProperties() {
m_collection_sp = std::make_shared<OptionValueProperties>(
PlatformWasm::GetPluginNameStatic());
m_collection_sp->Initialize(g_platformwasm_properties_def);
}
FileSpec GetRuntimePath() const {
return GetPropertyAtIndexAs<FileSpec>(ePropertyRuntimePath, {});
}
Args GetRuntimeArgs() const {
Args result;
m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyRuntimeArgs, result);
return result;
}
llvm::StringRef GetPortArg() const {
return GetPropertyAtIndexAs<llvm::StringRef>(ePropertyPortArg, {});
}
};
} // namespace
static PluginProperties &GetGlobalProperties() {
static PluginProperties g_settings;
return g_settings;
}
llvm::StringRef PlatformWasm::GetPluginDescriptionStatic() {
return "Platform for debugging Wasm";
}
void PlatformWasm::Initialize() {
PluginManager::RegisterPlugin(
GetPluginNameStatic(), GetPluginDescriptionStatic(),
PlatformWasm::CreateInstance, PlatformWasm::DebuggerInitialize);
}
void PlatformWasm::Terminate() {
PluginManager::UnregisterPlugin(PlatformWasm::CreateInstance);
}
void PlatformWasm::DebuggerInitialize(Debugger &debugger) {
if (!PluginManager::GetSettingForPlatformPlugin(debugger,
GetPluginNameStatic())) {
PluginManager::CreateSettingForPlatformPlugin(
debugger, GetGlobalProperties().GetValueProperties(),
"Properties for the wasm platform plugin.",
/*is_global_property=*/true);
}
}
PlatformSP PlatformWasm::CreateInstance(bool force, const ArchSpec *arch) {
Log *log = GetLog(LLDBLog::Platform);
LLDB_LOG(log, "force = {0}, arch = ({1}, {2})", force,
arch ? arch->GetArchitectureName() : "<null>",
arch ? arch->GetTriple().getTriple() : "<null>");
bool create = force;
if (!create && arch && arch->IsValid()) {
const llvm::Triple &triple = arch->GetTriple();
switch (triple.getArch()) {
case llvm::Triple::wasm32:
case llvm::Triple::wasm64:
create = true;
break;
default:
break;
}
}
LLDB_LOG(log, "create = {0}", create);
return create ? PlatformSP(new PlatformWasm()) : PlatformSP();
}
std::vector<ArchSpec>
PlatformWasm::GetSupportedArchitectures(const ArchSpec &process_host_arch) {
return {ArchSpec("wasm32-unknown-unknown-wasm"),
ArchSpec("wasm64-unknown-unknown-wasm")};
}
static auto get_arg_range(const Args &args) {
return llvm::make_range(args.GetArgumentArrayRef().begin(),
args.GetArgumentArrayRef().end());
}
lldb::ProcessSP PlatformWasm::Attach(ProcessAttachInfo &attach_info,
Debugger &debugger, Target *target,
Status &status) {
if (m_remote_platform_sp)
return m_remote_platform_sp->Attach(attach_info, debugger, target, status);
status = Status::FromErrorString(
"attaching is only supported when connected to a remote Wasm platform");
return nullptr;
}
lldb::ProcessSP PlatformWasm::DebugProcess(ProcessLaunchInfo &launch_info,
Debugger &debugger, Target &target,
Status &error) {
if (m_remote_platform_sp)
return m_remote_platform_sp->DebugProcess(launch_info, debugger, target,
error);
Log *log = GetLog(LLDBLog::Platform);
const PluginProperties &properties = GetGlobalProperties();
FileSpec runtime = properties.GetRuntimePath();
FileSystem::Instance().ResolveExecutableLocation(runtime);
if (!FileSystem::Instance().Exists(runtime)) {
error = Status::FromErrorStringWithFormatv(
"WebAssembly runtime does not exist: {0}", runtime.GetPath());
return nullptr;
}
uint16_t port = 0;
{
// Get the next available port by binding a socket to port 0.
TCPSocket listen_socket(true);
error = listen_socket.Listen("localhost:0", /*backlog=*/5);
if (error.Fail())
return nullptr;
port = listen_socket.GetLocalPortNumber();
}
if (error.Fail())
return nullptr;
Args args({runtime.GetPath(),
llvm::formatv("{0}{1}", properties.GetPortArg(), port).str()});
args.AppendArguments(properties.GetRuntimeArgs());
args.AppendArguments(launch_info.GetArguments());
launch_info.SetArguments(args, true);
launch_info.SetLaunchInSeparateProcessGroup(true);
launch_info.GetFlags().Clear(eLaunchFlagDebug);
auto exit_code = std::make_shared<std::optional<int>>();
launch_info.SetMonitorProcessCallback(
[=](lldb::pid_t pid, int signal, int status) {
LLDB_LOG(
log,
"WebAssembly runtime exited: pid = {0}, signal = {1}, status = {2}",
pid, signal, status);
exit_code->emplace(status);
});
// This is automatically done for host platform in
// Target::FinalizeFileActions, but we're not a host platform.
llvm::Error Err = launch_info.SetUpPtyRedirection();
LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}");
LLDB_LOG(log, "{0}", get_arg_range(launch_info.GetArguments()));
error = Host::LaunchProcess(launch_info);
if (error.Fail())
return nullptr;
ProcessSP process_sp = target.CreateProcess(
launch_info.GetListener(), wasm::ProcessWasm::GetPluginNameStatic(),
nullptr, true);
if (!process_sp) {
error = Status::FromErrorString("failed to create WebAssembly process");
return nullptr;
}
process_sp->HijackProcessEvents(launch_info.GetHijackListener());
error = process_sp->ConnectRemote(
llvm::formatv("connect://localhost:{0}", port).str());
if (error.Fail()) {
// If we know the runtime has exited, that's a better error message than
// failing to connect.
if (*exit_code)
error = Status::FromError(llvm::joinErrors(
llvm::createStringErrorV(
"WebAssembly runtime exited with exit code {0}", **exit_code),
error.takeError()));
return nullptr;
}
#ifndef _WIN32
if (launch_info.GetPTY().GetPrimaryFileDescriptor() !=
PseudoTerminal::invalid_fd)
process_sp->SetSTDIOFileDescriptor(
launch_info.GetPTY().ReleasePrimaryFileDescriptor());
#endif
return process_sp;
}
Status PlatformWasm::ConnectRemote(Args &args) {
if (IsHost())
return Status::FromErrorString(
"can't connect to the host platform, always connected");
if (!m_remote_platform_sp)
m_remote_platform_sp = PlatformSP(new PlatformWasmRemoteGDBServer());
return m_remote_platform_sp->ConnectRemote(args);
}