Author: Jonas Devlieghere
Date: 2025-07-30T19:51:09-07:00
New Revision: f62370290a66f8d3a47a4b25c3896983424f97bd

URL: 
https://github.com/llvm/llvm-project/commit/f62370290a66f8d3a47a4b25c3896983424f97bd
DIFF: 
https://github.com/llvm/llvm-project/commit/f62370290a66f8d3a47a4b25c3896983424f97bd.diff

LOG: [lldb] Implement RegisterContextWasm (#151056)

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 support for `DW_OP_WASM_location` in the DWARF
expression evaluator (#151010). This also adds a more comprehensive
test, showing that we can use this to show local variables.

Added: 
    lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp
    lldb/source/Plugins/Process/wasm/RegisterContextWasm.h
    lldb/test/API/functionalities/gdb_remote_client/simple.c
    lldb/test/API/functionalities/gdb_remote_client/simple.yaml

Modified: 
    lldb/docs/resources/lldbgdbremote.md
    lldb/source/Plugins/Process/wasm/CMakeLists.txt
    lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
    lldb/source/Plugins/Process/wasm/ProcessWasm.h
    lldb/source/Plugins/Process/wasm/ThreadWasm.cpp
    lldb/source/Plugins/Process/wasm/ThreadWasm.h
    lldb/test/API/functionalities/gdb_remote_client/TestWasm.py

Removed: 
    


################################################################################
diff  --git a/lldb/docs/resources/lldbgdbremote.md 
b/lldb/docs/resources/lldbgdbremote.md
index 41628cffcc566..36b95f1073ebc 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -1998,22 +1998,6 @@ threads (live system debug) / cores (JTAG) in your 
program have
 stopped and allows LLDB to display and control your program
 correctly.
 
-## 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. This packed is
-supported by the [WAMR](https://github.com/bytecodealliance/wasm-micro-runtime)
-and [V8](https://v8.dev) Wasm runtimes.
-
 ## qWatchpointSupportInfo
 
 Get the number of hardware watchpoints available on the remote target.
@@ -2479,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/source/Plugins/Process/wasm/CMakeLists.txt 
b/lldb/source/Plugins/Process/wasm/CMakeLists.txt
index ff8a3c792ad53..779b97ec90d08 100644
--- a/lldb/source/Plugins/Process/wasm/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/wasm/CMakeLists.txt
@@ -1,5 +1,6 @@
 add_lldb_library(lldbPluginProcessWasm PLUGIN
   ProcessWasm.cpp
+  RegisterContextWasm.cpp
   ThreadWasm.cpp
   UnwindWasm.cpp
 

diff  --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp 
b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
index 5eeabec086552..580e8c1d9cfa4 100644
--- a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
@@ -131,3 +131,36 @@ ProcessWasm::GetWasmCallStack(lldb::tid_t tid) {
 
   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
index bab14a8be7dbe..22effe7340ed7 100644
--- a/lldb/source/Plugins/Process/wasm/ProcessWasm.h
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h
@@ -10,6 +10,7 @@
 #define LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H
 
 #include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
+#include "Utility/WasmVirtualRegisters.h"
 
 namespace lldb_private {
 namespace wasm {
@@ -71,12 +72,19 @@ class ProcessWasm : public 
process_gdb_remote::ProcessGDBRemote {
   /// 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;
   }

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 license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextWasm.h"
+#include "Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h"
+#include "ProcessWasm.h"
+#include "ThreadWasm.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+using namespace lldb_private::wasm;
+
+RegisterContextWasm::RegisterContextWasm(
+    wasm::ThreadWasm &thread, uint32_t concrete_frame_idx,
+    GDBRemoteDynamicRegisterInfoSP reg_info_sp)
+    : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info_sp, false,
+                               false) {}
+
+RegisterContextWasm::~RegisterContextWasm() = default;
+
+uint32_t RegisterContextWasm::ConvertRegisterKindToRegisterNumber(
+    lldb::RegisterKind kind, uint32_t num) {
+  return num;
+}
+
+size_t RegisterContextWasm::GetRegisterCount() {
+  // Wasm has no registers.
+  return 0;
+}
+
+const RegisterInfo *RegisterContextWasm::GetRegisterInfoAtIndex(size_t reg) {
+  uint32_t tag = GetWasmVirtualRegisterTag(reg);
+  if (tag == eWasmTagNotAWasmLocation)
+    return m_reg_info_sp->GetRegisterInfoAtIndex(
+        GetWasmVirtualRegisterIndex(reg));
+
+  auto it = m_register_map.find(reg);
+  if (it == m_register_map.end()) {
+    WasmVirtualRegisterKinds kind = static_cast<WasmVirtualRegisterKinds>(tag);
+    std::tie(it, std::ignore) = m_register_map.insert(
+        {reg, std::make_unique<WasmVirtualRegisterInfo>(
+                  kind, GetWasmVirtualRegisterIndex(reg))});
+  }
+  return it->second.get();
+}
+
+size_t RegisterContextWasm::GetRegisterSetCount() { return 0; }
+
+const RegisterSet *RegisterContextWasm::GetRegisterSet(size_t reg_set) {
+  // Wasm has no registers.
+  return nullptr;
+}
+
+bool RegisterContextWasm::ReadRegister(const RegisterInfo *reg_info,
+                                       RegisterValue &value) {
+  // The only real registers is the PC.
+  if (reg_info->name)
+    return GDBRemoteRegisterContext::ReadRegister(reg_info, value);
+
+  // Read the virtual registers.
+  ThreadWasm *thread = static_cast<ThreadWasm *>(&GetThread());
+  ProcessWasm *process = static_cast<ProcessWasm 
*>(thread->GetProcess().get());
+  if (!thread)
+    return false;
+
+  uint32_t frame_index = m_concrete_frame_idx;
+  WasmVirtualRegisterInfo *wasm_reg_info =
+      static_cast<WasmVirtualRegisterInfo *>(
+          const_cast<RegisterInfo *>(reg_info));
+
+  llvm::Expected<DataBufferSP> maybe_buffer = process->GetWasmVariable(
+      wasm_reg_info->kind, frame_index, wasm_reg_info->index);
+  if (!maybe_buffer) {
+    LLDB_LOG_ERROR(GetLog(LLDBLog::Process), maybe_buffer.takeError(),
+                   "Failed to read Wasm local: {0}");
+    return false;
+  }
+
+  DataBufferSP buffer_sp = *maybe_buffer;
+  DataExtractor reg_data(buffer_sp, process->GetByteOrder(),
+                         process->GetAddressByteSize());
+  wasm_reg_info->byte_size = buffer_sp->GetByteSize();
+  wasm_reg_info->encoding = lldb::eEncodingUint;
+
+  Status error = value.SetValueFromData(
+      *reg_info, reg_data, reg_info->byte_offset, /*partial_data_ok=*/false);
+  return error.Success();
+}
+
+void RegisterContextWasm::InvalidateAllRegisters() {}
+
+bool RegisterContextWasm::WriteRegister(const RegisterInfo *reg_info,
+                                        const RegisterValue &value) {
+  // The only real registers is the PC.
+  if (reg_info->name)
+    return GDBRemoteRegisterContext::WriteRegister(reg_info, value);
+  return false;
+}

diff  --git a/lldb/source/Plugins/Process/wasm/RegisterContextWasm.h 
b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.h
new file mode 100644
index 0000000000000..7e63eb85bc75c
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.h
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_REGISTERCONTEXTWASM_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_REGISTERCONTEXTWASM_H
+
+#include "Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h"
+#include "ThreadWasm.h"
+#include "Utility/WasmVirtualRegisters.h"
+#include "lldb/lldb-private-types.h"
+#include <unordered_map>
+
+namespace lldb_private {
+namespace wasm {
+
+class RegisterContextWasm;
+
+typedef std::shared_ptr<RegisterContextWasm> RegisterContextWasmSP;
+
+struct WasmVirtualRegisterInfo : public RegisterInfo {
+  WasmVirtualRegisterKinds kind;
+  uint32_t index;
+
+  WasmVirtualRegisterInfo(WasmVirtualRegisterKinds kind, uint32_t index)
+      : RegisterInfo(), kind(kind), index(index) {}
+};
+
+class RegisterContextWasm
+    : public process_gdb_remote::GDBRemoteRegisterContext {
+public:
+  RegisterContextWasm(
+      wasm::ThreadWasm &thread, uint32_t concrete_frame_idx,
+      process_gdb_remote::GDBRemoteDynamicRegisterInfoSP reg_info_sp);
+
+  ~RegisterContextWasm() override;
+
+  uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+                                               uint32_t num) override;
+
+  void InvalidateAllRegisters() override;
+
+  size_t GetRegisterCount() override;
+
+  const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+  size_t GetRegisterSetCount() override;
+
+  const RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+  bool ReadRegister(const RegisterInfo *reg_info,
+                    RegisterValue &value) override;
+
+  bool WriteRegister(const RegisterInfo *reg_info,
+                     const RegisterValue &value) override;
+
+private:
+  std::unordered_map<size_t, std::unique_ptr<WasmVirtualRegisterInfo>>
+      m_register_map;
+};
+
+} // namespace wasm
+} // namespace lldb_private
+
+#endif

diff  --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp 
b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp
index a6553ffffedaa..0666b75d4afe0 100644
--- a/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp
+++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp
@@ -9,6 +9,7 @@
 #include "ThreadWasm.h"
 
 #include "ProcessWasm.h"
+#include "RegisterContextWasm.h"
 #include "UnwindWasm.h"
 #include "lldb/Target/Target.h"
 
@@ -32,3 +33,19 @@ llvm::Expected<std::vector<lldb::addr_t>> 
ThreadWasm::GetWasmCallStack() {
   }
   return llvm::createStringError("no process");
 }
+
+lldb::RegisterContextSP
+ThreadWasm::CreateRegisterContextForFrame(StackFrame *frame) {
+  uint32_t concrete_frame_idx = 0;
+  ProcessSP process_sp(GetProcess());
+  ProcessWasm *wasm_process = static_cast<ProcessWasm *>(process_sp.get());
+
+  if (frame)
+    concrete_frame_idx = frame->GetConcreteFrameIndex();
+
+  if (concrete_frame_idx == 0)
+    return std::make_shared<RegisterContextWasm>(
+        *this, concrete_frame_idx, wasm_process->GetRegisterInfo());
+
+  return GetUnwinder().CreateRegisterContextForFrame(frame);
+}

diff  --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.h 
b/lldb/source/Plugins/Process/wasm/ThreadWasm.h
index 1c90f58767bc8..c2f5762b30484 100644
--- a/lldb/source/Plugins/Process/wasm/ThreadWasm.h
+++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.h
@@ -25,6 +25,9 @@ class ThreadWasm : public process_gdb_remote::ThreadGDBRemote 
{
   /// Retrieve the current call stack from the WebAssembly remote process.
   llvm::Expected<std::vector<lldb::addr_t>> GetWasmCallStack();
 
+  lldb::RegisterContextSP
+  CreateRegisterContextForFrame(StackFrame *frame) override;
+
 protected:
   Unwind &GetUnwinder() override;
 

diff  --git a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py 
b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py
index 445f4222e2179..73c81efb79347 100644
--- a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py
+++ b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py
@@ -1,12 +1,15 @@
 import lldb
+import os
 import binascii
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test.decorators import *
 from lldbsuite.test.gdbclientutils import *
 from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
 
-LLDB_INVALID_ADDRESS = lldb.LLDB_INVALID_ADDRESS
-load_address = 0x400000000
+MODULE_ID = 4
+LOAD_ADDRESS = MODULE_ID << 32
+WASM_LOCAL_ADDR = 0x103E0
+
 
 def format_register_value(val):
     """
@@ -23,12 +26,59 @@ def format_register_value(val):
     return result
 
 
+class WasmStackFrame:
+    def __init__(self, address):
+        self._address = address
+
+    def __str__(self):
+        return format_register_value(LOAD_ADDRESS | self._address)
+
+
+class WasmCallStack:
+    def __init__(self, wasm_stack_frames):
+        self._wasm_stack_frames = wasm_stack_frames
+
+    def __str__(self):
+        result = ""
+        for frame in self._wasm_stack_frames:
+            result += str(frame)
+        return result
+
+
+class FakeMemory:
+    def __init__(self, start_addr, end_addr):
+        self._base_addr = start_addr
+        self._memory = bytearray(end_addr - start_addr)
+        self._memoryview = memoryview(self._memory)
+
+    def store_bytes(self, addr, bytes_obj):
+        assert addr > self._base_addr
+        assert addr < self._base_addr + len(self._memoryview)
+        offset = addr - self._base_addr
+        chunk = self._memoryview[offset : offset + len(bytes_obj)]
+        for i in range(len(bytes_obj)):
+            chunk[i] = bytes_obj[i]
+
+    def get_bytes(self, addr, length):
+        assert addr > self._base_addr
+        assert addr < self._base_addr + len(self._memoryview)
+
+        offset = addr - self._base_addr
+        return self._memoryview[offset : offset + length]
+
+    def contains(self, addr):
+        return addr - self._base_addr < len(self._memoryview)
+
+
 class MyResponder(MockGDBServerResponder):
-    current_pc = load_address + 0x0A
+    current_pc = LOAD_ADDRESS | 0x01AD
 
-    def __init__(self, obj_path, module_name=""):
+    def __init__(self, obj_path, module_name="", wasm_call_stacks=[], 
memory=None):
         self._obj_path = obj_path
         self._module_name = module_name or obj_path
+        self._wasm_call_stacks = wasm_call_stacks
+        self._call_stack_request_count = 0
+        self._memory = memory
         MockGDBServerResponder.__init__(self)
 
     def respond(self, packet):
@@ -36,6 +86,8 @@ def respond(self, packet):
             return self.qRegisterInfo(packet[13:])
         if packet.startswith("qWasmCallStack"):
             return self.qWasmCallStack()
+        if packet.startswith("qWasmLocal"):
+            return self.qWasmLocal(packet)
         return MockGDBServerResponder.respond(self, packet)
 
     def qSupported(self, client_supported):
@@ -71,28 +123,61 @@ def qXferRead(self, obj, annex, offset, length):
         if obj == "libraries":
             xml = (
                 '<library-list><library name="%s"><section 
address="%d"/></library></library-list>'
-                % (self._module_name, load_address)
+                % (self._module_name, LOAD_ADDRESS)
             )
             return xml, False
         else:
             return None, False
 
     def readMemory(self, addr, length):
-        if addr < load_address:
+        if self._memory and self._memory.contains(addr):
+            chunk = self._memory.get_bytes(addr, length)
+            return chunk.hex()
+        if addr < LOAD_ADDRESS:
             return "E02"
         result = ""
         with open(self._obj_path, mode="rb") as file:
             file_content = bytearray(file.read())
-            addr_from = addr - load_address
+            if addr >= LOAD_ADDRESS + len(file_content):
+                return "E03"
+            addr_from = addr - LOAD_ADDRESS
             addr_to = addr_from + min(length, len(file_content) - addr_from)
             for i in range(addr_from, addr_to):
                 result += format(file_content[i], "02x")
             file.close()
         return result
 
+    def setBreakpoint(self, packet):
+        bp_data = packet[1:].split(",")
+        self._bp_address = bp_data[1]
+        return "OK"
+
+    def qfThreadInfo(self):
+        return "m1"
+
+    def cont(self):
+        # Continue execution. Simulates running the Wasm engine until a 
breakpoint is hit.
+        return (
+            "T05thread-pcs:"
+            + format(int(self._bp_address, 16) & 0x3FFFFFFFFFFFFFFF, "x")
+            + ";thread:1"
+        )
+
     def qWasmCallStack(self):
-        # Return two 64-bit addresses: 0x40000000000001B3, 0x40000000000001FE
-        return "b301000000000040fe01000000000040"
+        if len(self._wasm_call_stacks) == 0:
+            return ""
+        result = str(self._wasm_call_stacks[self._call_stack_request_count])
+        self._call_stack_request_count += 1
+        return result
+
+    def qWasmLocal(self, packet):
+        # Format: qWasmLocal:frame_index;index
+        data = packet.split(":")
+        data = data[1].split(";")
+        frame_index, local_index = data
+        if frame_index == "0" and local_index == "2":
+            return format_register_value(WASM_LOCAL_ADDR)
+        return "E03"
 
 
 class TestWasm(GDBRemoteTestBase):
@@ -124,35 +209,35 @@ def 
test_load_module_with_embedded_symbols_from_remote(self):
         code_section = module.GetSectionAtIndex(0)
         self.assertEqual("code", code_section.GetName())
         self.assertEqual(
-            load_address | code_section.GetFileOffset(),
+            LOAD_ADDRESS | code_section.GetFileOffset(),
             code_section.GetLoadAddress(target),
         )
 
         debug_info_section = module.GetSectionAtIndex(1)
         self.assertEqual(".debug_info", debug_info_section.GetName())
         self.assertEqual(
-            load_address | debug_info_section.GetFileOffset(),
+            LOAD_ADDRESS | debug_info_section.GetFileOffset(),
             debug_info_section.GetLoadAddress(target),
         )
 
         debug_abbrev_section = module.GetSectionAtIndex(2)
         self.assertEqual(".debug_abbrev", debug_abbrev_section.GetName())
         self.assertEqual(
-            load_address | debug_abbrev_section.GetFileOffset(),
+            LOAD_ADDRESS | debug_abbrev_section.GetFileOffset(),
             debug_abbrev_section.GetLoadAddress(target),
         )
 
         debug_line_section = module.GetSectionAtIndex(3)
         self.assertEqual(".debug_line", debug_line_section.GetName())
         self.assertEqual(
-            load_address | debug_line_section.GetFileOffset(),
+            LOAD_ADDRESS | debug_line_section.GetFileOffset(),
             debug_line_section.GetLoadAddress(target),
         )
 
         debug_str_section = module.GetSectionAtIndex(4)
         self.assertEqual(".debug_str", debug_str_section.GetName())
         self.assertEqual(
-            load_address | debug_line_section.GetFileOffset(),
+            LOAD_ADDRESS | debug_line_section.GetFileOffset(),
             debug_line_section.GetLoadAddress(target),
         )
 
@@ -194,97 +279,103 @@ def 
test_load_module_with_stripped_symbols_from_remote(self):
         code_section = module.GetSectionAtIndex(0)
         self.assertEqual("code", code_section.GetName())
         self.assertEqual(
-            load_address | code_section.GetFileOffset(),
+            LOAD_ADDRESS | code_section.GetFileOffset(),
             code_section.GetLoadAddress(target),
         )
 
         debug_info_section = module.GetSectionAtIndex(1)
         self.assertEqual(".debug_info", debug_info_section.GetName())
         self.assertEqual(
-            LLDB_INVALID_ADDRESS, debug_info_section.GetLoadAddress(target)
+            lldb.LLDB_INVALID_ADDRESS, 
debug_info_section.GetLoadAddress(target)
         )
 
         debug_abbrev_section = module.GetSectionAtIndex(2)
         self.assertEqual(".debug_abbrev", debug_abbrev_section.GetName())
         self.assertEqual(
-            LLDB_INVALID_ADDRESS, debug_abbrev_section.GetLoadAddress(target)
+            lldb.LLDB_INVALID_ADDRESS, 
debug_abbrev_section.GetLoadAddress(target)
         )
 
         debug_line_section = module.GetSectionAtIndex(3)
         self.assertEqual(".debug_line", debug_line_section.GetName())
         self.assertEqual(
-            LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target)
+            lldb.LLDB_INVALID_ADDRESS, 
debug_line_section.GetLoadAddress(target)
         )
 
         debug_str_section = module.GetSectionAtIndex(4)
         self.assertEqual(".debug_str", debug_str_section.GetName())
         self.assertEqual(
-            LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target)
+            lldb.LLDB_INVALID_ADDRESS, 
debug_line_section.GetLoadAddress(target)
         )
 
     @skipIfAsan
     @skipIfXmlSupportMissing
-    def test_load_module_from_file(self):
-        """Test connecting to a WebAssembly engine via GDB-remote and loading 
a Wasm module from a file"""
-
-        yaml_path = "test_wasm_embedded_debug_sections.yaml"
-        yaml_base, ext = os.path.splitext(yaml_path)
+    def test_simple_wasm_debugging_session(self):
+        """Test connecting to a WebAssembly engine via GDB-remote, loading a
+        Wasm module with embedded DWARF symbols, setting a breakpoint and
+        checking the debuggee state"""
+
+        # simple.yaml was created by compiling simple.c to wasm and using
+        # obj2yaml on the output.
+        #
+        # $ clang -target wasm32 -nostdlib -Wl,--no-entry -Wl,--export-all -O0 
-g -o simple.wasm simple.c
+        # $ obj2yaml simple.wasm -o simple.yaml
+        yaml_path = "simple.yaml"
+        yaml_base, _ = os.path.splitext(yaml_path)
         obj_path = self.getBuildArtifact(yaml_base)
         self.yaml2obj(yaml_path, obj_path)
 
-        self.server.responder = MyResponder(obj_path)
+        # Create a fake call stack.
+        call_stacks = [
+            WasmCallStack(
+                [WasmStackFrame(0x019C), WasmStackFrame(0x01E5), 
WasmStackFrame(0x01FE)]
+            ),
+        ]
+
+        # Create fake memory for our wasm locals.
+        self.memory = FakeMemory(0x10000, 0x20000)
+        self.memory.store_bytes(
+            WASM_LOCAL_ADDR,
+            bytes.fromhex(
+                
"0000000000000000020000000100000000000000020000000100000000000000"
+            ),
+        )
+
+        self.server.responder = MyResponder(
+            obj_path, "test_wasm", call_stacks, self.memory
+        )
 
         target = self.dbg.CreateTarget("")
+        breakpoint = target.BreakpointCreateByName("add")
         process = self.connect(target, "wasm")
         lldbutil.expect_state_changes(
             self, self.dbg.GetListener(), process, [lldb.eStateStopped]
         )
 
+        location = breakpoint.GetLocationAtIndex(0)
+        self.assertTrue(location and location.IsEnabled(), 
VALID_BREAKPOINT_LOCATION)
+
         num_modules = target.GetNumModules()
         self.assertEqual(1, num_modules)
 
-        module = target.GetModuleAtIndex(0)
-        num_sections = module.GetNumSections()
-        self.assertEqual(5, num_sections)
-
-        code_section = module.GetSectionAtIndex(0)
-        self.assertEqual("code", code_section.GetName())
-        self.assertEqual(
-            load_address | code_section.GetFileOffset(),
-            code_section.GetLoadAddress(target),
-        )
-
-        debug_info_section = module.GetSectionAtIndex(1)
-        self.assertEqual(".debug_info", debug_info_section.GetName())
-        self.assertEqual(
-            LLDB_INVALID_ADDRESS, debug_info_section.GetLoadAddress(target)
-        )
-
-        debug_abbrev_section = module.GetSectionAtIndex(2)
-        self.assertEqual(".debug_abbrev", debug_abbrev_section.GetName())
-        self.assertEqual(
-            LLDB_INVALID_ADDRESS, debug_abbrev_section.GetLoadAddress(target)
-        )
-
-        debug_line_section = module.GetSectionAtIndex(3)
-        self.assertEqual(".debug_line", debug_line_section.GetName())
-        self.assertEqual(
-            LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target)
-        )
-
-        debug_str_section = module.GetSectionAtIndex(4)
-        self.assertEqual(".debug_str", debug_str_section.GetName())
-        self.assertEqual(
-            LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target)
-        )
-
         thread = process.GetThreadAtIndex(0)
         self.assertTrue(thread.IsValid())
 
-        frame = thread.GetFrameAtIndex(0)
-        self.assertTrue(frame.IsValid())
-        self.assertEqual(frame.GetPC(), 0x40000000000001B3)
-
-        frame = thread.GetFrameAtIndex(1)
-        self.assertTrue(frame.IsValid())
-        self.assertEqual(frame.GetPC(), 0x40000000000001FE)
+        # Check that our frames match our fake call stack.
+        frame0 = thread.GetFrameAtIndex(0)
+        self.assertTrue(frame0.IsValid())
+        self.assertEqual(frame0.GetPC(), LOAD_ADDRESS | 0x019C)
+        self.assertIn("add", frame0.GetFunctionName())
+
+        frame1 = thread.GetFrameAtIndex(1)
+        self.assertTrue(frame1.IsValid())
+        self.assertEqual(frame1.GetPC(), LOAD_ADDRESS | 0x01E5)
+        self.assertIn("main", frame1.GetFunctionName())
+
+        # Check that we can resolve local variables.
+        a = frame0.FindVariable("a")
+        self.assertTrue(a.IsValid())
+        self.assertEqual(a.GetValueAsUnsigned(), 1)
+
+        b = frame0.FindVariable("b")
+        self.assertTrue(b.IsValid())
+        self.assertEqual(b.GetValueAsUnsigned(), 2)

diff  --git a/lldb/test/API/functionalities/gdb_remote_client/simple.c 
b/lldb/test/API/functionalities/gdb_remote_client/simple.c
new file mode 100644
index 0000000000000..62ca1fe4d0190
--- /dev/null
+++ b/lldb/test/API/functionalities/gdb_remote_client/simple.c
@@ -0,0 +1,10 @@
+int add(int a, int b) {
+  // Break here
+  return a + b;
+}
+
+int main() {
+  int i = 1;
+  int j = 2;
+  return add(i, j);
+}

diff  --git a/lldb/test/API/functionalities/gdb_remote_client/simple.yaml 
b/lldb/test/API/functionalities/gdb_remote_client/simple.yaml
new file mode 100644
index 0000000000000..cf1b7d82d1f80
--- /dev/null
+++ b/lldb/test/API/functionalities/gdb_remote_client/simple.yaml
@@ -0,0 +1,228 @@
+--- !WASM
+FileHeader:
+  Version:         0x1
+Sections:
+  - Type:            TYPE
+    Signatures:
+      - Index:           0
+        ParamTypes:      []
+        ReturnTypes:     []
+      - Index:           1
+        ParamTypes:
+          - I32
+          - I32
+        ReturnTypes:
+          - I32
+      - Index:           2
+        ParamTypes:      []
+        ReturnTypes:
+          - I32
+  - Type:            FUNCTION
+    FunctionTypes:   [ 0, 1, 2, 1 ]
+  - Type:            TABLE
+    Tables:
+      - Index:           0
+        ElemType:        FUNCREF
+        Limits:
+          Flags:           [ HAS_MAX ]
+          Minimum:         0x1
+          Maximum:         0x1
+  - Type:            MEMORY
+    Memories:
+      - Minimum:         0x2
+  - Type:            GLOBAL
+    Globals:
+      - Index:           0
+        Type:            I32
+        Mutable:         true
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           66560
+      - Index:           1
+        Type:            I32
+        Mutable:         false
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           1024
+      - Index:           2
+        Type:            I32
+        Mutable:         false
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           1024
+      - Index:           3
+        Type:            I32
+        Mutable:         false
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           1024
+      - Index:           4
+        Type:            I32
+        Mutable:         false
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           66560
+      - Index:           5
+        Type:            I32
+        Mutable:         false
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           1024
+      - Index:           6
+        Type:            I32
+        Mutable:         false
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           66560
+      - Index:           7
+        Type:            I32
+        Mutable:         false
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           131072
+      - Index:           8
+        Type:            I32
+        Mutable:         false
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           0
+      - Index:           9
+        Type:            I32
+        Mutable:         false
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           1
+      - Index:           10
+        Type:            I32
+        Mutable:         false
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           65536
+  - Type:            EXPORT
+    Exports:
+      - Name:            memory
+        Kind:            MEMORY
+        Index:           0
+      - Name:            __wasm_call_ctors
+        Kind:            FUNCTION
+        Index:           0
+      - Name:            add
+        Kind:            FUNCTION
+        Index:           1
+      - Name:            __original_main
+        Kind:            FUNCTION
+        Index:           2
+      - Name:            main
+        Kind:            FUNCTION
+        Index:           3
+      - Name:            __main_void
+        Kind:            FUNCTION
+        Index:           2
+      - Name:            __indirect_function_table
+        Kind:            TABLE
+        Index:           0
+      - Name:            __dso_handle
+        Kind:            GLOBAL
+        Index:           1
+      - Name:            __data_end
+        Kind:            GLOBAL
+        Index:           2
+      - Name:            __stack_low
+        Kind:            GLOBAL
+        Index:           3
+      - Name:            __stack_high
+        Kind:            GLOBAL
+        Index:           4
+      - Name:            __global_base
+        Kind:            GLOBAL
+        Index:           5
+      - Name:            __heap_base
+        Kind:            GLOBAL
+        Index:           6
+      - Name:            __heap_end
+        Kind:            GLOBAL
+        Index:           7
+      - Name:            __memory_base
+        Kind:            GLOBAL
+        Index:           8
+      - Name:            __table_base
+        Kind:            GLOBAL
+        Index:           9
+      - Name:            __wasm_first_page_end
+        Kind:            GLOBAL
+        Index:           10
+  - Type:            CODE
+    Functions:
+      - Index:           0
+        Locals:          []
+        Body:            0B
+      - Index:           1
+        Locals:
+          - Type:            I32
+            Count:           1
+        Body:            
23808080800041106B21022002200036020C20022001360208200228020C20022802086A0F0B
+      - Index:           2
+        Locals:
+          - Type:            I32
+            Count:           2
+        Body:            
23808080800041106B210020002480808080002000410036020C2000410136020820004102360204200028020820002802041081808080002101200041106A24808080800020010F0B
+      - Index:           3
+        Locals:          []
+        Body:            1082808080000F0B
+  - Type:            CUSTOM
+    Name:            .debug_abbrev
+    Payload:         
011101250E1305030E10171B0E110155170000022E01110112064018030E3A0B3B0B271949133F1900000305000218030E3A0B3B0B49130000042E01110112064018030E3A0B3B0B49133F1900000534000218030E3A0B3B0B49130000062400030E3E0B0B0B000000
+  - Type:            CUSTOM
+    Name:            .debug_info
+    Payload:         
940000000400000000000401620000001D0055000000000000000D000000000000000000000002050000002900000004ED00029F510000000101900000000302910C60000000010190000000030291085E00000001019000000000042F0000004C00000004ED00009F04000000010690000000050291080B0000000107900000000502910409000000010890000000000600000000050400
+  - Type:            CUSTOM
+    Name:            .debug_ranges
+    Payload:         050000002E0000002F0000007B0000000000000000000000
+  - Type:            CUSTOM
+    Name:            .debug_str
+    Payload:         
696E74006D61696E006A0069002F55736572732F6A6F6E61732F7761736D2D6D6963726F2D72756E74696D652F70726F647563742D6D696E692F706C6174666F726D732F64617277696E2F6275696C64006164640073696D706C652E630062006100636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A4A4465766C696567686572652F6C6C766D2D70726F6A6563742E67697420343161363839613132323834633834623632383933393461356338306264636534383733656466302900
+  - Type:            CUSTOM
+    Name:            .debug_line
+    Payload:         
62000000040020000000010101FB0E0D0001010101000000010000010073696D706C652E6300000000000005020500000001050A0A08AE050E0658050C5805032002020001010005022F0000001705070A08BB75050E7505110658050A58050382020F000101
+  - Type:            CUSTOM
+    Name:            name
+    FunctionNames:
+      - Index:           0
+        Name:            __wasm_call_ctors
+      - Index:           1
+        Name:            add
+      - Index:           2
+        Name:            __original_main
+      - Index:           3
+        Name:            main
+    GlobalNames:
+      - Index:           0
+        Name:            __stack_pointer
+  - Type:            CUSTOM
+    Name:            producers
+    Languages:
+      - Name:            C11
+        Version:         ''
+    Tools:
+      - Name:            clang
+        Version:         '22.0.0git'
+  - Type:            CUSTOM
+    Name:            target_features
+    Features:
+      - Prefix:          USED
+        Name:            bulk-memory
+      - Prefix:          USED
+        Name:            bulk-memory-opt
+      - Prefix:          USED
+        Name:            call-indirect-overlong
+      - Prefix:          USED
+        Name:            multivalue
+      - Prefix:          USED
+        Name:            mutable-globals
+      - Prefix:          USED
+        Name:            nontrapping-fptoint
+      - Prefix:          USED
+        Name:            reference-types
+      - Prefix:          USED
+        Name:            sign-ext
+...


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to