https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/171507
>From 417bf4882e9a6be920e6a728e5de1d62eedc9e38 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <[email protected]> Date: Mon, 8 Dec 2025 17:01:18 -0800 Subject: [PATCH 1/2] [lldb] Add WebAssembly platform This PR adds a platform for WebAssembly. Inspired by QemuUser, the platform lets you configure a WebAssembly runtime to run a Wasm binary. For example, the following configuration can be used to launch binaries under the WebAssembly Micro Runtime (WARM): ``` settings set -- platform.plugin.wasm.runtime-args --heap-size=1048576 settings set -- platform.plugin.wasm.port-arg -g=127.0.0.1: settings set -- platform.plugin.wasm.runtime-path /path/to/iwasm-2.4.0 ``` --- lldb/source/Plugins/Platform/CMakeLists.txt | 1 + .../Platform/WebAssembly/CMakeLists.txt | 23 ++ .../Platform/WebAssembly/PlatformWasm.cpp | 197 ++++++++++++++++++ .../Platform/WebAssembly/PlatformWasm.h | 79 +++++++ .../WebAssembly/PlatformWasmProperties.td | 21 ++ 5 files changed, 321 insertions(+) create mode 100644 lldb/source/Plugins/Platform/WebAssembly/CMakeLists.txt create mode 100644 lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.cpp create mode 100644 lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.h create mode 100644 lldb/source/Plugins/Platform/WebAssembly/PlatformWasmProperties.td diff --git a/lldb/source/Plugins/Platform/CMakeLists.txt b/lldb/source/Plugins/Platform/CMakeLists.txt index f4753ab47ce11..cc1432aa4754b 100644 --- a/lldb/source/Plugins/Platform/CMakeLists.txt +++ b/lldb/source/Plugins/Platform/CMakeLists.txt @@ -15,4 +15,5 @@ add_subdirectory(NetBSD) add_subdirectory(OpenBSD) add_subdirectory(POSIX) add_subdirectory(QemuUser) +add_subdirectory(WebAssembly) add_subdirectory(Windows) diff --git a/lldb/source/Plugins/Platform/WebAssembly/CMakeLists.txt b/lldb/source/Plugins/Platform/WebAssembly/CMakeLists.txt new file mode 100644 index 0000000000000..7fb17b295fbb8 --- /dev/null +++ b/lldb/source/Plugins/Platform/WebAssembly/CMakeLists.txt @@ -0,0 +1,23 @@ +lldb_tablegen(PlatformWasmProperties.inc -gen-lldb-property-defs + SOURCE PlatformWasmProperties.td + TARGET LLDBPluginPlatformWasmPropertiesGen) + +lldb_tablegen(PlatformWasmPropertiesEnum.inc -gen-lldb-property-enum-defs + SOURCE PlatformWasmProperties.td + TARGET LLDBPluginPlatformWasmPropertiesEnumGen) + +add_lldb_library(lldbPluginPlatformWasm PLUGIN + PlatformWasm.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbTarget + lldbUtility + LINK_COMPONENTS + Support + ) + +add_dependencies(lldbPluginPlatformWasm + LLDBPluginPlatformWasmPropertiesGen + LLDBPluginPlatformWasmPropertiesEnumGen) diff --git a/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.cpp b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.cpp new file mode 100644 index 0000000000000..fc00db50ffc0c --- /dev/null +++ b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.cpp @@ -0,0 +1,197 @@ +//===----------------------------------------------------------------------===// +// +// 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/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" + +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); + } + + 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::DebugProcess(ProcessLaunchInfo &launch_info, + Debugger &debugger, Target &target, + Status &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; + { + TCPSocket listen_socket(true); + error = listen_socket.Listen("localhost:0", 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); + launch_info.SetMonitorProcessCallback(ProcessLaunchInfo::NoOpMonitorCallback); + + LLDB_LOG(log, "{0}", get_arg_range(launch_info.GetArguments())); + + // 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}"); + + 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()) + return nullptr; + + if (launch_info.GetPTY().GetPrimaryFileDescriptor() != + PseudoTerminal::invalid_fd) + process_sp->SetSTDIOFileDescriptor( + launch_info.GetPTY().ReleasePrimaryFileDescriptor()); + + return process_sp; + + return {}; +} diff --git a/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.h b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.h new file mode 100644 index 0000000000000..cba4c7c549cb0 --- /dev/null +++ b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.h @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// 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_SOURCE_PLUGINS_PLATFORM_WASM_PLATFORMWASM_H +#define LLDB_SOURCE_PLUGINS_PLATFORM_WASM_PLATFORMWASM_H + +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Platform.h" + +namespace lldb_private { + +class PlatformWasm : public Platform { +public: + static void Initialize(); + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "wasm"; } + static llvm::StringRef GetPluginDescriptionStatic(); + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + llvm::StringRef GetDescription() override { + return GetPluginDescriptionStatic(); + } + + UserIDResolver &GetUserIDResolver() override { + return HostInfo::GetUserIDResolver(); + } + + std::vector<ArchSpec> + GetSupportedArchitectures(const ArchSpec &process_host_arch) override; + + lldb::ProcessSP DebugProcess(ProcessLaunchInfo &launch_info, + Debugger &debugger, Target &target, + Status &error) override; + + lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger, + Target *target, Status &status) override { + status = Status::FromErrorString("Not supported"); + return nullptr; + } + + uint32_t FindProcesses(const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &proc_infos) override { + return 0; + } + + bool GetProcessInfo(lldb::pid_t pid, + ProcessInstanceInfo &proc_info) override { + return false; + } + + bool IsConnected() const override { return true; } + + void CalculateTrapHandlerSymbolNames() override {} + + MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr, + lldb::addr_t length, unsigned prot, + unsigned flags, lldb::addr_t fd, + lldb::addr_t offset) override { + return Platform::GetHostPlatform()->GetMmapArgumentList( + arch, addr, length, prot, flags, fd, offset); + } + +private: + static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch); + static void DebuggerInitialize(Debugger &debugger); + + PlatformWasm() : Platform(/*is_host=*/true) {} +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PLATFORM_WASM_PLATFORMWASM_H diff --git a/lldb/source/Plugins/Platform/WebAssembly/PlatformWasmProperties.td b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasmProperties.td new file mode 100644 index 0000000000000..9fcd3fca121cb --- /dev/null +++ b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasmProperties.td @@ -0,0 +1,21 @@ +include "../../../../include/lldb/Core/PropertiesBase.td" + +let Definition = "platformwasm" in { + def RuntimePath + : Property<"runtime-path", "FileSpec">, + Global, + DefaultStringValue<"">, + Desc<"Path to the WebAssembly runtime binary. If the path does not " + "contain a directory separator, the filename is looked up in the " + "PATH environment variable. If empty, the filename is derived " + "from the architecture setting.">; + def RuntimeArgs : Property<"runtime-args", "Args">, + Global, + DefaultStringValue<"">, + Desc<"Extra arguments to pass to the WebAssembly runtime.">; + def PortArg : Property<"port-arg", "String">, + Global, + DefaultStringValue<"">, + Desc<"Argument to the WebAssembly runtime to specify the " + "GDB remote port.">; +} >From 1c6c87545f3eface721997d9d9f6be39b4054707 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <[email protected]> Date: Wed, 10 Dec 2025 11:26:13 -0800 Subject: [PATCH 2/2] Address David's feedback --- .../Platform/WebAssembly/PlatformWasm.cpp | 23 +++++++++++++++---- .../WebAssembly/PlatformWasmProperties.td | 16 ++++++------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.cpp b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.cpp index fc00db50ffc0c..3fe44d7604244 100644 --- a/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.cpp +++ b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.cpp @@ -158,15 +158,23 @@ lldb::ProcessSP PlatformWasm::DebugProcess(ProcessLaunchInfo &launch_info, launch_info.SetArguments(args, true); launch_info.SetLaunchInSeparateProcessGroup(true); launch_info.GetFlags().Clear(eLaunchFlagDebug); - launch_info.SetMonitorProcessCallback(ProcessLaunchInfo::NoOpMonitorCallback); - LLDB_LOG(log, "{0}", get_arg_range(launch_info.GetArguments())); + 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; @@ -183,8 +191,15 @@ lldb::ProcessSP PlatformWasm::DebugProcess(ProcessLaunchInfo &launch_info, error = process_sp->ConnectRemote( llvm::formatv("connect://localhost:{0}", port).str()); - if (error.Fail()) + 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::FromErrorStringWithFormatv( + "WebAssembly runtime exited with exit code {0}", **exit_code); + return nullptr; + } if (launch_info.GetPTY().GetPrimaryFileDescriptor() != PseudoTerminal::invalid_fd) @@ -192,6 +207,4 @@ lldb::ProcessSP PlatformWasm::DebugProcess(ProcessLaunchInfo &launch_info, launch_info.GetPTY().ReleasePrimaryFileDescriptor()); return process_sp; - - return {}; } diff --git a/lldb/source/Plugins/Platform/WebAssembly/PlatformWasmProperties.td b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasmProperties.td index 9fcd3fca121cb..36b4b39c05c88 100644 --- a/lldb/source/Plugins/Platform/WebAssembly/PlatformWasmProperties.td +++ b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasmProperties.td @@ -1,14 +1,10 @@ include "../../../../include/lldb/Core/PropertiesBase.td" let Definition = "platformwasm" in { - def RuntimePath - : Property<"runtime-path", "FileSpec">, - Global, - DefaultStringValue<"">, - Desc<"Path to the WebAssembly runtime binary. If the path does not " - "contain a directory separator, the filename is looked up in the " - "PATH environment variable. If empty, the filename is derived " - "from the architecture setting.">; + def RuntimePath : Property<"runtime-path", "FileSpec">, + Global, + DefaultStringValue<"">, + Desc<"Path to the WebAssembly runtime binary.">; def RuntimeArgs : Property<"runtime-args", "Args">, Global, DefaultStringValue<"">, @@ -17,5 +13,7 @@ let Definition = "platformwasm" in { Global, DefaultStringValue<"">, Desc<"Argument to the WebAssembly runtime to specify the " - "GDB remote port.">; + "GDB remote port. The port number chosen by LLDB will be " + "concatenated to this argument. For example: " + "-g=127.0.0.1: or --debugger-port.">; } _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
