-
Notifications
You must be signed in to change notification settings - Fork 15.5k
[lldb] Add WebAssembly platform #171507
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[lldb] Add WebAssembly platform #171507
Conversation
|
@llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere) ChangesThis PR adds a platform for WebAssembly. Heavily inspired by Pavel's 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): 5 Files Affected:
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..1ade07c11f85d
--- /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..d68f52dd32974
--- /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<"-p">,
+ Desc<"Argument to the WebAssembly runtime to specify the "
+ "GDB remote port.">;
+}
|
|
@MaxDesiatov is working on debugging support in WasmKit, which is another runtime we're planning to target with this. For that the configuration would look like this: To generate an invocation like this: |
🐧 Linux x64 Test Results
✅ The build succeeded and all tests passed. |
3990955 to
b126750
Compare
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 ```
b126750 to
417bf48
Compare
DavidSpickett
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the PR description please include example commands to select and start the platform, along with the settings you've already shown.
(to contrast this with the "manual" gdb-remote etc etc way of doing this)
Also, too early for a release note? You're going to shake out some bugs here by running the test suite I guess, so we can draw attention to it after that's done.
lldb/source/Plugins/Platform/WebAssembly/PlatformWasmProperties.td
Outdated
Show resolved
Hide resolved
| Global, | ||
| DefaultStringValue<"">, | ||
| Desc<"Argument to the WebAssembly runtime to specify the " | ||
| "GDB remote port.">; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs an example. The "Args" above is so to speak "full" like "-number-of-things 99 -animal giraffe" which is what most people would assume. Whereas the port argument is intended to be only the first part and lldb puts the port number in itself.
Which is a good idea to make it flexible, but saying that lldb will append a port number and showing an example would explain that better.
So that lldb doesn't attempt to run stuff like simulator --port 127.0.0.1:1234:1234.
Also when that does happen, could you check how the error is shown to the user? If they see that command somewhere in the error then great, they can eventually figure it out. Failing that, something in a log channel would allow us to help them at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added example to clarify the port argument. I also improved the error reporting for when the runtime fails to launch. Previously, that went unnoticed because we had NOOP callback. Now, we check the exit code if we failed to connect (should've been plenty of time). It's still possible the runtime went away later, in which case it's logged to the platform log channel.
|
I'm tempted to say all this should be in some "run this simulator with these arguments" generic thing but there's too many small details around the edges to bother doing that I think. It's not like the code is so radically different that it'd be difficult to do later. Edit: and by that point maybe this has grown some uniquely wasm features anyway. With a runtime like that I'm sure there's some cool stuff we can do. |
|
Thanks for the review David!
Exactly, plus I have more logic coming to this platform that's more Wasm specific. |
| uint16_t port = 0; | ||
| { | ||
| TCPSocket listen_socket(true); | ||
| error = listen_socket.Listen("localhost:0", 5); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does the :0 and the 5 mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:0 means pick a port (which is the whole purpose of this thingy). The 5 is the backlog, I'll add an inline comment.
DavidSpickett
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pedantry on the settings text but otherwise LGTM.
You may want to revise the existing release note for WASM debug. Perhaps adding to it "LLDB can also start a runtime for you...".
lldb/source/Plugins/Platform/WebAssembly/PlatformWasmProperties.td
Outdated
Show resolved
Hide resolved
lldb/source/Plugins/Platform/WebAssembly/PlatformWasmProperties.td
Outdated
Show resolved
Hide resolved
lldb/source/Plugins/Platform/WebAssembly/PlatformWasmProperties.td
Outdated
Show resolved
Hide resolved
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/113/builds/10039 Here is the relevant piece of the build log for the reference |
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/151/builds/7735 Here is the relevant piece of the build log for the reference |
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/80/builds/18540 Here is the relevant piece of the build log for the reference |
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/119/builds/7658 Here is the relevant piece of the build log for the reference |
|
This PR adds a platform for WebAssembly. Heavily inspired by Pavel's 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):
With the settings above, you can now launch a binary directly under WAMR: