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`.
242 lines
7.7 KiB
C++
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);
|
|
}
|