[Lldb-commits] [lldb] [lldb] Add WebAssembly platform (PR #171507)
Jonas Devlieghere via lldb-commits
lldb-commits at lists.llvm.org
Wed Dec 10 15:55:52 PST 2025
================
@@ -0,0 +1,210 @@
+//===----------------------------------------------------------------------===//
+//
+// 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);
+
+ 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::FromErrorStringWithFormatv(
+ "WebAssembly runtime exited with exit code {0}", **exit_code);
----------------
JDevlieghere wrote:
Funny you mention that, it's what I had originally. I ended up going with this because the runtime exiting is always more interesting:
```
(lldb) run
WebAssembly runtime exited with exit code 1
Timed out connecting to localhost:12345
```
But on the other hand it's not nice to drop an error message so I'll revive it.
https://github.com/llvm/llvm-project/pull/171507
More information about the lldb-commits
mailing list