-
Notifications
You must be signed in to change notification settings - Fork 14.6k
Backport Wasm Debugging changes to the LLVM 21.x Release #151559
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
base: release/21.x
Are you sure you want to change the base?
Backport Wasm Debugging changes to the LLVM 21.x Release #151559
Conversation
Extend support in LLDB for WebAssembly. This PR adds a new Process plugin (ProcessWasm) that extends ProcessGDBRemote for WebAssembly targets. It adds support for WebAssembly's memory model with separate address spaces, and the ability to fetch the call stack from the WebAssembly runtime. I have tested this change with the WebAssembly Micro Runtime (WAMR, https://github.com/bytecodealliance/wasm-micro-runtime) which implements a GDB debug stub and supports the qWasmCallStack packet. ``` (lldb) process connect --plugin wasm connect://localhost:4567 Process 1 stopped * thread llvm#1, name = 'nobody', stop reason = trace frame #0: 0x40000000000001ad wasm32_args.wasm`main: -> 0x40000000000001ad <+3>: global.get 0 0x40000000000001b3 <+9>: i32.const 16 0x40000000000001b5 <+11>: i32.sub 0x40000000000001b6 <+12>: local.set 0 (lldb) b add Breakpoint 1: where = wasm32_args.wasm`add + 28 at test.c:4:12, address = 0x400000000000019c (lldb) c Process 1 resuming Process 1 stopped * thread llvm#1, name = 'nobody', stop reason = breakpoint 1.1 frame #0: 0x400000000000019c wasm32_args.wasm`add(a=<unavailable>, b=<unavailable>) at test.c:4:12 1 int 2 add(int a, int b) 3 { -> 4 return a + b; 5 } 6 7 int (lldb) bt * thread llvm#1, name = 'nobody', stop reason = breakpoint 1.1 * frame #0: 0x400000000000019c wasm32_args.wasm`add(a=<unavailable>, b=<unavailable>) at test.c:4:12 frame llvm#1: 0x40000000000001e5 wasm32_args.wasm`main at test.c:12:12 frame llvm#2: 0x40000000000001fe wasm32_args.wasm ``` This PR is based on an unmerged patch from Paolo Severini: https://reviews.llvm.org/D78801. I intentionally stuck to the foundations to keep this PR small. I have more PRs in the pipeline to support the other features/packets. My motivation for supporting Wasm is to support debugging Swift compiled to WebAssembly: https://www.swift.org/documentation/articles/wasm-getting-started.html (cherry picked from commit a28e7f1)
Add support for DW_OP_WASM_location in DWARFExpression. This PR rebases at https://yurydelendik.github.io/webassembly-dwarf/ and supported by LLVM-based toolchains such as Clang, Swift, Emscripten, and Rust. (cherry picked from commit c2548a8)
This PR implements a register context for Wasm, which uses virtual registers to resolve Wasm local, globals and stack values. The registers are used to implement supprot for `DW_OP_WASM_location` in the DWARF expression evaluator (llvm#151010). This also adds a more comprehensive test, showing that we can use this to show local variables. (cherry picked from commit 1c70fa8)
(cherry picked from commit 6c589fe)
@llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere) Changes#151439 Patch is 99.21 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/151559.diff 34 Files Affected:
diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md
index 80c68091ecd07..36b95f1073ebc 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -2463,3 +2463,70 @@ omitting them will work fine; these numbers are always base 16.
The length of the payload is not provided. A reliable, 8-bit clean,
transport layer is assumed.
+
+## Wasm Packets
+
+The packet below are supported by the
+[WAMR](https://github.com/bytecodealliance/wasm-micro-runtime) and
+[V8](https://v8.dev) Wasm runtimes.
+
+
+### qWasmCallStack
+
+Get the Wasm call stack for the given thread id. This returns a hex-encoded
+list of PC values, one for each frame of the call stack. To match the Wasm
+specification, the addresses are encoded in little endian byte order, even if
+the endian of the Wasm runtime's host is not little endian.
+
+```
+send packet: $qWasmCallStack:202dbe040#08
+read packet: $9c01000000000040e501000000000040fe01000000000040#
+```
+
+**Priority to Implement:** Only required for Wasm support. Necessary to show
+stack traces.
+
+### qWasmGlobal
+
+Get the value of a Wasm global variable for the given frame index at the given
+variable index. The indexes are encoded as base 10. The result is a hex-encoded
+address from where to read the value.
+
+```
+send packet: $qWasmGlobal:0;2#cb
+read packet: $e0030100#b9
+```
+
+**Priority to Implement:** Only required for Wasm support. Necessary to show
+variables.
+
+
+### qWasmLocal
+
+Get the value of a Wasm function argument or local variable for the given frame
+index at the given variable index. The indexes are encoded as base 10. The
+result is a hex-encoded address from where to read the value.
+
+
+```
+send packet: $qWasmLocal:0;2#cb
+read packet: $e0030100#b9
+```
+
+**Priority to Implement:** Only required for Wasm support. Necessary to show
+variables.
+
+
+### qWasmStackValue
+
+Get the value of a Wasm local variable from the Wasm operand stack, for the
+given frame index at the given variable index. The indexes are encoded as base
+10. The result is a hex-encoded address from where to read value.
+
+```
+send packet: $qWasmStackValue:0;2#cb
+read packet: $e0030100#b9
+```
+
+**Priority to Implement:** Only required for Wasm support. Necessary to show
+variables.
diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h
index 37853c0b5a8fc..8fcc5d37b91c9 100644
--- a/lldb/include/lldb/Expression/DWARFExpression.h
+++ b/lldb/include/lldb/Expression/DWARFExpression.h
@@ -52,10 +52,10 @@ class DWARFExpression {
GetVendorDWARFOpcodeSize(const DataExtractor &data,
const lldb::offset_t data_offset,
const uint8_t op) const = 0;
- virtual bool ParseVendorDWARFOpcode(uint8_t op,
- const DataExtractor &opcodes,
- lldb::offset_t &offset,
- Stack &stack) const = 0;
+ virtual bool
+ ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes,
+ lldb::offset_t &offset, RegisterContext *reg_ctx,
+ lldb::RegisterKind reg_kind, Stack &stack) const = 0;
Delegate(const Delegate &) = delete;
Delegate &operator=(const Delegate &) = delete;
@@ -163,6 +163,10 @@ class DWARFExpression {
bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op) const;
+ static llvm::Error ReadRegisterValueAsScalar(RegisterContext *reg_ctx,
+ lldb::RegisterKind reg_kind,
+ uint32_t reg_num, Value &value);
+
private:
/// A data extractor capable of reading opcode bytes
DataExtractor m_data;
diff --git a/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py b/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py
index 459460b84fbae..599f7878e6edb 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py
@@ -45,7 +45,7 @@ def createTarget(self, yaml_path):
self.yaml2obj(yaml_path, obj_path)
return self.dbg.CreateTarget(obj_path)
- def connect(self, target):
+ def connect(self, target, plugin="gdb-remote"):
"""
Create a process by connecting to the mock GDB server.
@@ -54,7 +54,7 @@ def connect(self, target):
listener = self.dbg.GetListener()
error = lldb.SBError()
process = target.ConnectRemote(
- listener, self.server.get_connect_url(), "gdb-remote", error
+ listener, self.server.get_connect_url(), plugin, error
)
self.assertTrue(error.Success(), error.description)
self.assertTrue(process, PROCESS_IS_VALID)
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index 52891fcefd68b..f6e3af2fe38ff 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -91,9 +91,10 @@ void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) {
m_reg_kind = reg_kind;
}
-static llvm::Error ReadRegisterValueAsScalar(RegisterContext *reg_ctx,
- lldb::RegisterKind reg_kind,
- uint32_t reg_num, Value &value) {
+llvm::Error
+DWARFExpression::ReadRegisterValueAsScalar(RegisterContext *reg_ctx,
+ lldb::RegisterKind reg_kind,
+ uint32_t reg_num, Value &value) {
if (reg_ctx == nullptr)
return llvm::createStringError("no register context in frame");
@@ -2302,7 +2303,8 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
default:
if (dwarf_cu) {
- if (dwarf_cu->ParseVendorDWARFOpcode(op, opcodes, offset, stack)) {
+ if (dwarf_cu->ParseVendorDWARFOpcode(op, opcodes, offset, reg_ctx,
+ reg_kind, stack)) {
break;
}
}
diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
index 67963a790a4fe..b1efd25949379 100644
--- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
+++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
@@ -376,9 +376,13 @@ DataExtractor ObjectFileWasm::ReadImageData(offset_t offset, uint32_t size) {
DataBufferSP buffer_sp(data_up.release());
data.SetData(buffer_sp, 0, buffer_sp->GetByteSize());
}
+ } else if (offset < m_data.GetByteSize()) {
+ size =
+ std::min(static_cast<uint64_t>(size), m_data.GetByteSize() - offset);
+ return DataExtractor(m_data.GetDataStart() + offset, size, GetByteOrder(),
+ GetAddressByteSize());
}
}
-
data.SetByteOrder(GetByteOrder());
return data;
}
diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt
index bd9b1b86dbf13..3413360e975fb 100644
--- a/lldb/source/Plugins/Process/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/CMakeLists.txt
@@ -29,3 +29,4 @@ add_subdirectory(elf-core)
add_subdirectory(mach-core)
add_subdirectory(minidump)
add_subdirectory(FreeBSDKernel)
+add_subdirectory(wasm)
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index a2c34ddfc252e..14dfdec6a6f62 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -323,6 +323,11 @@ ProcessGDBRemote::~ProcessGDBRemote() {
KillDebugserverProcess();
}
+std::shared_ptr<ThreadGDBRemote>
+ProcessGDBRemote::CreateThread(lldb::tid_t tid) {
+ return std::make_shared<ThreadGDBRemote>(*this, tid);
+}
+
bool ProcessGDBRemote::ParsePythonTargetDefinition(
const FileSpec &target_definition_fspec) {
ScriptInterpreter *interpreter =
@@ -1594,7 +1599,7 @@ bool ProcessGDBRemote::DoUpdateThreadList(ThreadList &old_thread_list,
ThreadSP thread_sp(
old_thread_list_copy.RemoveThreadByProtocolID(tid, false));
if (!thread_sp) {
- thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid);
+ thread_sp = CreateThread(tid);
LLDB_LOGV(log, "Making new thread: {0} for thread ID: {1:x}.",
thread_sp.get(), thread_sp->GetID());
} else {
@@ -1726,7 +1731,7 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
if (!thread_sp) {
// Create the thread if we need to
- thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid);
+ thread_sp = CreateThread(tid);
m_thread_list_real.AddThread(thread_sp);
}
}
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 7ae33837fd067..7c3dfb179a4b3 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -246,6 +246,8 @@ class ProcessGDBRemote : public Process,
ProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp);
+ virtual std::shared_ptr<ThreadGDBRemote> CreateThread(lldb::tid_t tid);
+
bool SupportsMemoryTagging() override;
/// Broadcaster event bits definitions.
diff --git a/lldb/source/Plugins/Process/wasm/CMakeLists.txt b/lldb/source/Plugins/Process/wasm/CMakeLists.txt
new file mode 100644
index 0000000000000..779b97ec90d08
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_lldb_library(lldbPluginProcessWasm PLUGIN
+ ProcessWasm.cpp
+ RegisterContextWasm.cpp
+ ThreadWasm.cpp
+ UnwindWasm.cpp
+
+ LINK_LIBS
+ lldbCore
+ LINK_COMPONENTS
+ Support
+ )
diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
new file mode 100644
index 0000000000000..4482677336ca4
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
@@ -0,0 +1,166 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "ProcessWasm.h"
+#include "ThreadWasm.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Utility/DataBufferHeap.h"
+
+#include "lldb/Target/UnixSignals.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+using namespace lldb_private::wasm;
+
+LLDB_PLUGIN_DEFINE(ProcessWasm)
+
+ProcessWasm::ProcessWasm(lldb::TargetSP target_sp, ListenerSP listener_sp)
+ : ProcessGDBRemote(target_sp, listener_sp) {
+ assert(target_sp);
+ // Wasm doesn't have any Unix-like signals as a platform concept, but pretend
+ // like it does to appease LLDB.
+ m_unix_signals_sp = UnixSignals::Create(target_sp->GetArchitecture());
+}
+
+void ProcessWasm::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+ });
+}
+
+void ProcessWasm::DebuggerInitialize(Debugger &debugger) {
+ ProcessGDBRemote::DebuggerInitialize(debugger);
+}
+
+llvm::StringRef ProcessWasm::GetPluginName() { return GetPluginNameStatic(); }
+
+llvm::StringRef ProcessWasm::GetPluginNameStatic() { return "wasm"; }
+
+llvm::StringRef ProcessWasm::GetPluginDescriptionStatic() {
+ return "GDB Remote protocol based WebAssembly debugging plug-in.";
+}
+
+void ProcessWasm::Terminate() {
+ PluginManager::UnregisterPlugin(ProcessWasm::CreateInstance);
+}
+
+lldb::ProcessSP ProcessWasm::CreateInstance(lldb::TargetSP target_sp,
+ ListenerSP listener_sp,
+ const FileSpec *crash_file_path,
+ bool can_connect) {
+ if (crash_file_path == nullptr)
+ return std::make_shared<ProcessWasm>(target_sp, listener_sp);
+ return {};
+}
+
+bool ProcessWasm::CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) {
+ if (plugin_specified_by_name)
+ return true;
+
+ if (Module *exe_module = target_sp->GetExecutableModulePointer()) {
+ if (ObjectFile *exe_objfile = exe_module->GetObjectFile())
+ return exe_objfile->GetArchitecture().GetMachine() ==
+ llvm::Triple::wasm32;
+ }
+
+ // However, if there is no wasm module, we return false, otherwise,
+ // we might use ProcessWasm to attach gdb remote.
+ return false;
+}
+
+std::shared_ptr<ThreadGDBRemote> ProcessWasm::CreateThread(lldb::tid_t tid) {
+ return std::make_shared<ThreadWasm>(*this, tid);
+}
+
+size_t ProcessWasm::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ Status &error) {
+ wasm_addr_t wasm_addr(vm_addr);
+
+ switch (wasm_addr.GetType()) {
+ case WasmAddressType::Memory:
+ case WasmAddressType::Object:
+ return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error);
+ case WasmAddressType::Invalid:
+ error.FromErrorStringWithFormat(
+ "Wasm read failed for invalid address 0x%" PRIx64, vm_addr);
+ return 0;
+ }
+}
+
+llvm::Expected<std::vector<lldb::addr_t>>
+ProcessWasm::GetWasmCallStack(lldb::tid_t tid) {
+ StreamString packet;
+ packet.Printf("qWasmCallStack:");
+ packet.Printf("%llx", tid);
+
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=
+ GDBRemoteCommunication::PacketResult::Success)
+ return llvm::createStringError("failed to send qWasmCallStack");
+
+ if (!response.IsNormalResponse())
+ return llvm::createStringError("failed to get response for qWasmCallStack");
+
+ WritableDataBufferSP data_buffer_sp =
+ std::make_shared<DataBufferHeap>(response.GetStringRef().size() / 2, 0);
+ const size_t bytes = response.GetHexBytes(data_buffer_sp->GetData(), '\xcc');
+ if (bytes == 0 || bytes % sizeof(uint64_t) != 0)
+ return llvm::createStringError("invalid response for qWasmCallStack");
+
+ // To match the Wasm specification, the addresses are encoded in little endian
+ // byte order.
+ DataExtractor data(data_buffer_sp, lldb::eByteOrderLittle,
+ GetAddressByteSize());
+ lldb::offset_t offset = 0;
+ std::vector<lldb::addr_t> call_stack_pcs;
+ while (offset < bytes)
+ call_stack_pcs.push_back(data.GetU64(&offset));
+
+ return call_stack_pcs;
+}
+
+llvm::Expected<lldb::DataBufferSP>
+ProcessWasm::GetWasmVariable(WasmVirtualRegisterKinds kind, int frame_index,
+ int index) {
+ StreamString packet;
+ switch (kind) {
+ case eWasmTagLocal:
+ packet.Printf("qWasmLocal:");
+ break;
+ case eWasmTagGlobal:
+ packet.Printf("qWasmGlobal:");
+ break;
+ case eWasmTagOperandStack:
+ packet.PutCString("qWasmStackValue:");
+ break;
+ case eWasmTagNotAWasmLocation:
+ return llvm::createStringError("not a Wasm location");
+ }
+ packet.Printf("%d;%d", frame_index, index);
+
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=
+ GDBRemoteCommunication::PacketResult::Success)
+ return llvm::createStringError("failed to send Wasm variable");
+
+ if (!response.IsNormalResponse())
+ return llvm::createStringError("failed to get response for Wasm variable");
+
+ WritableDataBufferSP buffer_sp(
+ new DataBufferHeap(response.GetStringRef().size() / 2, 0));
+ response.GetHexBytes(buffer_sp->GetData(), '\xcc');
+ return buffer_sp;
+}
diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.h b/lldb/source/Plugins/Process/wasm/ProcessWasm.h
new file mode 100644
index 0000000000000..22effe7340ed7
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_PROCESS_WASM_PROCESSWASM_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H
+
+#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
+#include "Utility/WasmVirtualRegisters.h"
+
+namespace lldb_private {
+namespace wasm {
+
+/// Each WebAssembly module has separated address spaces for Code and Memory.
+/// A WebAssembly module also has a Data section which, when the module is
+/// loaded, gets mapped into a region in the module Memory.
+enum WasmAddressType : uint8_t { Memory = 0x00, Object = 0x01, Invalid = 0xff };
+
+/// For the purpose of debugging, we can represent all these separated 32-bit
+/// address spaces with a single virtual 64-bit address space. The
+/// wasm_addr_t provides this encoding using bitfields.
+struct wasm_addr_t {
+ uint64_t offset : 32;
+ uint64_t module_id : 30;
+ uint64_t type : 2;
+
+ wasm_addr_t(lldb::addr_t addr)
+ : offset(addr & 0x00000000ffffffff),
+ module_id((addr & 0x00ffffff00000000) >> 32), type(addr >> 62) {}
+
+ wasm_addr_t(WasmAddressType type, uint32_t module_id, uint32_t offset)
+ : offset(offset), module_id(module_id), type(type) {}
+
+ WasmAddressType GetType() { return static_cast<WasmAddressType>(type); }
+
+ operator lldb::addr_t() { return *(uint64_t *)this; }
+};
+
+static_assert(sizeof(wasm_addr_t) == 8, "");
+
+/// ProcessWasm provides the access to the Wasm program state
+/// retrieved from the Wasm engine.
+class ProcessWasm : public process_gdb_remote::ProcessGDBRemote {
+public:
+ ProcessWasm(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp);
+ ~ProcessWasm() override = default;
+
+ static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec *crash_file_path,
+ bool can_connect);
+
+ static void Initialize();
+ static void DebuggerInitialize(Debugger &debugger);
+ static void Terminate();
+
+ static llvm::StringRef GetPluginNameStatic();
+ static llvm::StringRef GetPluginDescriptionStatic();
+
+ llvm::StringRef GetPluginName() override;
+
+ size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ Status &error) override;
+
+ bool CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) override;
+
+ /// Retrieve the current call stack from the WebAssembly remote process.
+ llvm::Expected<std::vector<lldb::addr_t>> GetWasmCallStack(lldb::tid_t tid);
+
+ /// Query the value of a WebAssembly variable from the WebAssembly
+ /// remote process.
+ llvm::Expected<lldb::DataBufferSP>
+ GetWasmVariable(WasmVirtualRegisterKinds kind, int frame_index, int index);
+
+protected:
+ std::shared_ptr<process_gdb_remote::ThreadGDBRemote>
+ CreateThread(lldb::tid_t tid) override;
+
+private:
+ friend class UnwindWasm;
+ friend class ThreadWasm;
+
+ process_gdb_remote::GDBRemoteDynamicRegisterInfoSP &GetRegisterInfo() {
+ return m_register_info_sp;
+ }
+
+ ProcessWasm(const ProcessWasm &);
+ const ProcessWasm &operator=(const ProcessWasm &) = delete;
+};
+
+} // namespace wasm
+} // namespace lldb_private
+
+#endif
diff --git a/lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp
new file mode 100644
index 0000000000000..b4681718cb688
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for li...
[truncated]
|
Who can review this @JDevlieghere ? |
@@ -326,6 +326,8 @@ Changes to LLDB | |||
RISC-V code with `disassemble`'s `--byte` option. | |||
* LLDB added native support for the Model Context Protocol (MCP). An MCP | |||
server can be started with the `protocol-server start MCP` command. | |||
* LLDB can now set breakpoints, show backtraces, and display variables when | |||
debugging Wasm with supported runtimes (WAMR and V8). |
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.
Should have mentioned this before, but, I think it would be good to add the connect command here. So that people know they have to select the wasm plugin explicitly.
I reviewed a lot of the changes on main but was not involved in writing them or the work that they are being written for (Swift compiled to WASM, I believe). The release note is the best place to start to understand it:
We had some wasm support before but it was not usable as I understand it. These changes are not full support of wasm, but it will now do something useful with the runtimes mentioned. It's new features, but with a limited scope. So for backport risk, there are:
Is this a relatively large set of new features? Yes. On that basis I'm ok with backporting this. If we want a second opinion, @labath did not review these changes on main, so might have a different take. |
Great write-up @DavidSpickett. For transparency, I'm totally fine not taking these for the release. We will take them for the next Swift stable branch, which is based on the LLVM 21 release branch, so LLVM deserves the right of first refusal. |
#151439