[Lldb-commits] [lldb] [lldb] enable wasm source debugging (PR #72634)

Xu Jun via lldb-commits lldb-commits at lists.llvm.org
Fri Nov 17 02:28:05 PST 2023


https://github.com/xujuntwt95329 created https://github.com/llvm/llvm-project/pull/72634

Rebase https://reviews.llvm.org/D78978 on latest code base, with less modification to LLDB core part:

I treat wasm locals, globals and operand stack values as virtual registers, and implement a wasmRegisterContext to handle this. 

2 high bits in the reg_num are used as a tag to distinguish wasm values

>From b9f6f4c03b6f72f44f9270a7de377deab3e0d8f6 Mon Sep 17 00:00:00 2001
From: Xu Jun <693788454 at qq.com>
Date: Fri, 17 Nov 2023 16:07:09 +0800
Subject: [PATCH] [lldb] enable wasm source debugging

---
 lldb/source/Expression/DWARFExpression.cpp    |  42 +++
 lldb/source/Plugins/Process/CMakeLists.txt    |   1 +
 .../Process/gdb-remote/ProcessGDBRemote.cpp   |   7 +-
 .../Process/gdb-remote/ProcessGDBRemote.h     |   2 +
 .../Plugins/Process/wasm/CMakeLists.txt       |  15 +
 .../Plugins/Process/wasm/ProcessWasm.cpp      | 261 ++++++++++++++++++
 .../source/Plugins/Process/wasm/ProcessWasm.h | 132 +++++++++
 .../Plugins/Process/wasm/ThreadWasm.cpp       |  57 ++++
 lldb/source/Plugins/Process/wasm/ThreadWasm.h |  47 ++++
 .../Plugins/Process/wasm/UnwindWasm.cpp       |  79 ++++++
 lldb/source/Plugins/Process/wasm/UnwindWasm.h |  58 ++++
 .../Process/wasm/wasmRegisterContext.cpp      | 103 +++++++
 .../Process/wasm/wasmRegisterContext.h        |  70 +++++
 lldb/source/Target/Platform.cpp               |   8 +
 14 files changed, 881 insertions(+), 1 deletion(-)
 create mode 100644 lldb/source/Plugins/Process/wasm/CMakeLists.txt
 create mode 100644 lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
 create mode 100644 lldb/source/Plugins/Process/wasm/ProcessWasm.h
 create mode 100644 lldb/source/Plugins/Process/wasm/ThreadWasm.cpp
 create mode 100644 lldb/source/Plugins/Process/wasm/ThreadWasm.h
 create mode 100644 lldb/source/Plugins/Process/wasm/UnwindWasm.cpp
 create mode 100644 lldb/source/Plugins/Process/wasm/UnwindWasm.h
 create mode 100644 lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp
 create mode 100644 lldb/source/Plugins/Process/wasm/wasmRegisterContext.h

diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index fe4928d4f43a434..1693e390c2e9203 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -346,6 +346,17 @@ static offset_t GetOpcodeDataSize(const DataExtractor &data,
     return (offset - data_offset) + subexpr_len;
   }
 
+  case DW_OP_WASM_location: {
+    uint8_t wasm_op = data.GetU8(&offset);
+    if (wasm_op == 3) {
+      data.GetU32(&offset);
+    }
+    else {
+      data.GetULEB128(&offset);
+    }
+    return offset - data_offset;
+  }
+
   default:
     if (!dwarf_cu) {
       return LLDB_INVALID_OFFSET;
@@ -2595,6 +2606,37 @@ bool DWARFExpression::Evaluate(
       break;
     }
 
+    case DW_OP_WASM_location: {
+      uint8_t wasm_op = opcodes.GetU8(&offset);
+      uint32_t index;
+
+      /* LLDB doesn't have an address space to represents WebAssembly Locals,
+       * GLobals and operand stacks.
+       * We encode these elements into virtual registers: 
+       *   | tag: 2 bits | index: 30 bits |
+       *   where tag is:
+       *    0: Not a WebAssembly location
+       *    1: Local
+       *    2: Global
+       *    3: Operand stack value 
+       */
+      if (wasm_op == 3) {
+        index = opcodes.GetU32(&offset);
+        wasm_op = 1;
+      } else {
+        index = opcodes.GetULEB128(&offset);
+      }
+
+      reg_num = (((wasm_op + 1) & 0x03) << 30) | (index & 0x3fffffff);
+
+      if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp))
+        stack.push_back(tmp);
+      else
+        return false;
+
+      break;
+    }
+
     default:
       if (dwarf_cu) {
         if (dwarf_cu->GetSymbolFileDWARF().ParseVendorDWARFOpcode(
diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt
index a51d0f7afd17591..be109a303e86691 100644
--- a/lldb/source/Plugins/Process/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/CMakeLists.txt
@@ -19,3 +19,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 e653ef5d8ac54e4..f82c85ffbe20ffb 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -1627,6 +1627,11 @@ void ProcessGDBRemote::ParseExpeditedRegisters(
   }
 }
 
+std::shared_ptr<ThreadGDBRemote>
+ProcessGDBRemote::CreateThread(lldb::tid_t tid) {
+  return std::make_shared<ThreadGDBRemote>(*this, tid);
+}
+
 ThreadSP ProcessGDBRemote::SetThreadStopInfo(
     lldb::tid_t tid, ExpeditedRegisterMap &expedited_register_map,
     uint8_t signo, const std::string &thread_name, const std::string &reason,
@@ -1651,7 +1656,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 f3787e7169047e2..7c2300c979eec5e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -353,6 +353,8 @@ class ProcessGDBRemote : public Process,
   MonitorDebugserverProcess(std::weak_ptr<ProcessGDBRemote> process_wp,
                             lldb::pid_t pid, int signo, int exit_status);
 
+  virtual std::shared_ptr<ThreadGDBRemote> CreateThread(lldb::tid_t tid);
+
   lldb::StateType SetThreadStopInfo(StringExtractor &stop_packet);
 
   bool
diff --git a/lldb/source/Plugins/Process/wasm/CMakeLists.txt b/lldb/source/Plugins/Process/wasm/CMakeLists.txt
new file mode 100644
index 000000000000000..c47eec7464ed8a9
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/CMakeLists.txt
@@ -0,0 +1,15 @@
+# This file comes from https://reviews.llvm.org/D78978.
+# Author: [@paolosev](https://reviews.llvm.org/p/paolosev/).
+
+add_lldb_library(lldbPluginProcessWasm PLUGIN
+  ProcessWasm.cpp
+  ThreadWasm.cpp
+  UnwindWasm.cpp
+  wasmRegisterContext.cpp
+
+  LINK_LIBS
+    lldbCore
+    ${LLDB_PLUGINS}
+  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 000000000000000..a2c6a33f830eb5e
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
@@ -0,0 +1,261 @@
+// This file comes from https://reviews.llvm.org/D78978.
+// Author: [@paolosev](https://reviews.llvm.org/p/paolosev/).
+
+//===-- ProcessWasm.cpp ---------------------------------------------------===//
+//
+// 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/Utility/DataBufferHeap.h"
+
+#include "lldb/Target/UnixSignals.h"
+#include "llvm/ADT/ArrayRef.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+using namespace lldb_private::wasm;
+
+LLDB_PLUGIN_DEFINE(ProcessWasm)
+
+// ProcessGDBRemote constructor
+ProcessWasm::ProcessWasm(lldb::TargetSP target_sp, ListenerSP listener_sp)
+    : ProcessGDBRemote(target_sp, listener_sp) {
+  /* always use linux signals for wasm process */
+  m_unix_signals_sp = UnixSignals::Create(ArchSpec{"wasm32-Ant-wasi-wasm"});
+}
+
+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);
+}
+
+// PluginInterface
+llvm::StringRef ProcessWasm::GetPluginName() { return GetPluginNameStatic(); }
+
+ConstString ProcessWasm::GetPluginNameStatic() {
+  static ConstString g_name("wasm");
+  return g_name;
+}
+
+const char *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) {
+  lldb::ProcessSP process_sp;
+  if (crash_file_path == nullptr)
+    process_sp = std::make_shared<ProcessWasm>(target_sp, listener_sp);
+  return process_sp;
+}
+
+bool ProcessWasm::CanDebug(lldb::TargetSP target_sp,
+                                bool plugin_specified_by_name) {
+  if (plugin_specified_by_name)
+    return true;
+
+  Module *exe_module = target_sp->GetExecutableModulePointer();
+  if (exe_module) {
+    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);
+  size_t nread = 0;
+
+  switch (wasm_addr.GetType()) {
+  case WasmAddressType::Memory:
+  case WasmAddressType::Object:
+    return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error);
+  case WasmAddressType::Invalid:
+  default:
+    error.SetErrorStringWithFormat(
+        "Wasm read failed for invalid address 0x%" PRIx64, vm_addr);
+    return 0;
+  }
+}
+
+size_t ProcessWasm::WasmReadMemory(uint32_t wasm_module_id, lldb::addr_t addr,
+                                 void *buf, size_t buffer_size) {
+  char packet[64];
+  int packet_len =
+      ::snprintf(packet, sizeof(packet), "qWasmMem:%d;%" PRIx64 ";%" PRIx64,
+                 wasm_module_id, static_cast<uint64_t>(addr),
+                 static_cast<uint64_t>(buffer_size));
+  assert(packet_len + 1 < (int)sizeof(packet));
+  UNUSED_IF_ASSERT_DISABLED(packet_len);
+  StringExtractorGDBRemote response;
+  if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, GetInterruptTimeout()) ==
+      GDBRemoteCommunication::PacketResult::Success) {
+    if (response.IsNormalResponse()) {
+      return response.GetHexBytes(llvm::MutableArrayRef<uint8_t>(
+                                      static_cast<uint8_t *>(buf), buffer_size),
+                                  '\xdd');
+    }
+  }
+  return 0;
+}
+
+size_t ProcessWasm::WasmReadData(uint32_t wasm_module_id, lldb::addr_t addr,
+                               void *buf, size_t buffer_size) {
+  char packet[64];
+  int packet_len =
+      ::snprintf(packet, sizeof(packet), "qWasmData:%d;%" PRIx64 ";%" PRIx64,
+                 wasm_module_id, static_cast<uint64_t>(addr),
+                 static_cast<uint64_t>(buffer_size));
+  assert(packet_len + 1 < (int)sizeof(packet));
+  UNUSED_IF_ASSERT_DISABLED(packet_len);
+  StringExtractorGDBRemote response;
+  if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, GetInterruptTimeout()) ==
+      GDBRemoteCommunication::PacketResult::Success) {
+    if (response.IsNormalResponse()) {
+      return response.GetHexBytes(llvm::MutableArrayRef<uint8_t>(
+                                      static_cast<uint8_t *>(buf), buffer_size),
+                                  '\xdd');
+    }
+  }
+  return 0;
+}
+
+bool ProcessWasm::GetWasmLocal(int frame_index, int index, void *buf,
+                               size_t buffer_size, size_t &size) {
+  StreamString packet;
+  packet.Printf("qWasmLocal:");
+  packet.Printf("%d;%d", frame_index, index);
+  StringExtractorGDBRemote response;
+  if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=
+      GDBRemoteCommunication::PacketResult::Success) {
+    return false;
+  }
+
+  if (!response.IsNormalResponse()) {
+    return false;
+  }
+
+  WritableDataBufferSP buffer_sp(
+      new DataBufferHeap(response.GetStringRef().size() / 2, 0));
+  response.GetHexBytes(buffer_sp->GetData(), '\xcc');
+  size = buffer_sp->GetByteSize();
+  if (size <= buffer_size) {
+    memcpy(buf, buffer_sp->GetBytes(), size);
+    return true;
+  }
+
+  return false;
+}
+
+bool ProcessWasm::GetWasmGlobal(int frame_index, int index, void *buf,
+                                size_t buffer_size, size_t &size) {
+  StreamString packet;
+  packet.PutCString("qWasmGlobal:");
+  packet.Printf("%d;%d", frame_index, index);
+  StringExtractorGDBRemote response;
+  if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=
+      GDBRemoteCommunication::PacketResult::Success) {
+    return false;
+  }
+
+  if (!response.IsNormalResponse()) {
+    return false;
+  }
+
+  WritableDataBufferSP buffer_sp(
+      new DataBufferHeap(response.GetStringRef().size() / 2, 0));
+  response.GetHexBytes(buffer_sp->GetData(), '\xcc');
+  size = buffer_sp->GetByteSize();
+  if (size <= buffer_size) {
+    memcpy(buf, buffer_sp->GetBytes(), size);
+    return true;
+  }
+
+  return false;
+}
+
+bool ProcessWasm::GetWasmStackValue(int frame_index, int index, void *buf,
+                                    size_t buffer_size, size_t &size) {
+  StreamString packet;
+  packet.PutCString("qWasmStackValue:");
+  packet.Printf("%d;%d", frame_index, index);
+  StringExtractorGDBRemote response;
+  if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=
+      GDBRemoteCommunication::PacketResult::Success) {
+    return false;
+  }
+
+  if (!response.IsNormalResponse()) {
+    return false;
+  }
+
+  WritableDataBufferSP buffer_sp(
+      new DataBufferHeap(response.GetStringRef().size() / 2, 0));
+  response.GetHexBytes(buffer_sp->GetData(), '\xcc');
+  size = buffer_sp->GetByteSize();
+  if (size <= buffer_size) {
+    memcpy(buf, buffer_sp->GetBytes(), size);
+    return true;
+  }
+
+  return false;
+}
+
+bool ProcessWasm::GetWasmCallStack(lldb::tid_t tid,
+                                   std::vector<lldb::addr_t> &call_stack_pcs) {
+  call_stack_pcs.clear();
+  StreamString packet;
+  packet.Printf("qWasmCallStack:");
+  packet.Printf("%llx", tid);
+  StringExtractorGDBRemote response;
+  if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=
+      GDBRemoteCommunication::PacketResult::Success) {
+    return false;
+  }
+
+  if (!response.IsNormalResponse()) {
+    return false;
+  }
+
+  addr_t buf[1024 / sizeof(addr_t)];
+  size_t bytes = response.GetHexBytes(
+      llvm::MutableArrayRef<uint8_t>((uint8_t *)buf, sizeof(buf)), '\xdd');
+  if (bytes == 0) {
+    return false;
+  }
+
+  for (size_t i = 0; i < bytes / sizeof(addr_t); i++) {
+    call_stack_pcs.push_back(buf[i]);
+  }
+  return true;
+}
diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.h b/lldb/source/Plugins/Process/wasm/ProcessWasm.h
new file mode 100644
index 000000000000000..4ef784ae9aa270a
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h
@@ -0,0 +1,132 @@
+// This file comes from https://reviews.llvm.org/D78978.
+// Author: [@paolosev](https://reviews.llvm.org/p/paolosev/).
+
+//===-- ProcessWasm.h -------------------------------------------*- C++ -*-===//
+//
+// 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 "lldb/Target/RegisterContext.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.
+// For the purpose of debugging, we can represent all these separated 32-bit
+// address spaces with a single virtual 64-bit address space.
+//
+// Struct wasm_addr_t provides this encoding using bitfields
+//
+enum WasmAddressType {
+  Memory = 0x00,
+  Object = 0x01,
+  Invalid = 0x03
+};
+struct wasm_addr_t {
+  uint64_t offset : 32;
+  uint64_t module_id : 30;
+  uint64_t type : 2;
+
+  wasm_addr_t(lldb::addr_t addr)
+      : type(addr >> 62), module_id((addr & 0x00ffffff00000000) >> 32),
+        offset(addr & 0x00000000ffffffff) {}
+
+  wasm_addr_t(WasmAddressType type_, uint32_t module_id_, uint32_t offset_)
+      : type(type_), module_id(module_id_), offset(offset_) {}
+
+  WasmAddressType GetType() { return static_cast<WasmAddressType>(type); }
+  operator lldb::addr_t() { return *(uint64_t *)this; }
+};
+
+/// 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 ConstString GetPluginNameStatic();
+  static const char *GetPluginDescriptionStatic();
+
+  /// PluginInterface protocol.
+  /// \{
+  llvm::StringRef GetPluginName() override;
+  /// \}
+
+  /// Process protocol.
+  /// \{
+  size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+                    Status &error) override;
+  /// \}
+
+  /// Query the value of a WebAssembly local variable from the WebAssembly
+  /// remote process.
+  bool GetWasmLocal(int frame_index, int index, void *buf, size_t buffer_size,
+                    size_t &size);
+
+  /// Query the value of a WebAssembly global variable from the WebAssembly
+  /// remote process.
+  bool GetWasmGlobal(int frame_index, int index, void *buf, size_t buffer_size,
+                     size_t &size);
+
+  /// Query the value of an item in the WebAssembly operand stack from the
+  /// WebAssembly remote process.
+  bool GetWasmStackValue(int frame_index, int index, void *buf,
+                         size_t buffer_size, size_t &size);
+
+  /// Read from the WebAssembly Memory space.
+  size_t WasmReadMemory(uint32_t wasm_module_id, lldb::addr_t addr, void *buf,
+                      size_t buffer_size);
+
+  /// Read from the WebAssembly Data space.
+  size_t WasmReadData(uint32_t wasm_module_id, lldb::addr_t addr, void *buf,
+                    size_t buffer_size);
+
+  /// Retrieve the current call stack from the WebAssembly remote process.
+  bool GetWasmCallStack(lldb::tid_t tid,
+                        std::vector<lldb::addr_t> &call_stack_pcs);
+
+  // Check if a given Process
+  bool CanDebug(lldb::TargetSP target_sp,
+                bool plugin_specified_by_name) override;
+
+protected:
+  /// ProcessGDBRemote protocol.
+  /// \{
+  std::shared_ptr<process_gdb_remote::ThreadGDBRemote>
+  CreateThread(lldb::tid_t tid);
+  /// \}
+
+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 // LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H
diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp
new file mode 100644
index 000000000000000..d9715dd23b637ad
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp
@@ -0,0 +1,57 @@
+// This file comes from https://reviews.llvm.org/D78978.
+// Author: [@paolosev](https://reviews.llvm.org/p/paolosev/).
+
+//===-- ThreadWasm.cpp ----------------------------------------------------===//
+//
+// 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 "ThreadWasm.h"
+
+#include "ProcessWasm.h"
+#include "UnwindWasm.h"
+#include "lldb/Target/Target.h"
+#include "wasmRegisterContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::wasm;
+
+Unwind &ThreadWasm::GetUnwinder() {
+  if (!m_unwinder_up) {
+    assert(CalculateTarget()->GetArchitecture().GetMachine() ==
+           llvm::Triple::wasm32);
+    m_unwinder_up.reset(new wasm::UnwindWasm(*this));
+  }
+  return *m_unwinder_up;
+}
+
+bool ThreadWasm::GetWasmCallStack(std::vector<lldb::addr_t> &call_stack_pcs) {
+  ProcessSP process_sp(GetProcess());
+  if (process_sp) {
+    ProcessWasm *wasm_process = static_cast<ProcessWasm *>(process_sp.get());
+    return wasm_process->GetWasmCallStack(GetID(), call_stack_pcs);
+  }
+  return false;
+}
+
+lldb::RegisterContextSP
+ThreadWasm::CreateRegisterContextForFrame(StackFrame *frame) {
+  lldb::RegisterContextSP reg_ctx_sp;
+  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) {
+    reg_ctx_sp = std::make_shared<WasmRegisterContext>(*this, concrete_frame_idx, wasm_process->GetRegisterInfo());
+  } else {
+    reg_ctx_sp = GetUnwinder().CreateRegisterContextForFrame(frame);
+  }
+  return reg_ctx_sp;
+}
diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.h b/lldb/source/Plugins/Process/wasm/ThreadWasm.h
new file mode 100644
index 000000000000000..eb731f8d335063d
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.h
@@ -0,0 +1,47 @@
+// This file comes from https://reviews.llvm.org/D78978.
+// Author: [@paolosev](https://reviews.llvm.org/p/paolosev/).
+
+//===-- ThreadWasm.h --------------------------------------------*- C++ -*-===//
+//
+// 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_THREADWASM_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H
+
+#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h"
+
+namespace lldb_private {
+namespace wasm {
+
+/// ProcessWasm provides the access to the Wasm program state
+///  retrieved from the Wasm engine.
+class ThreadWasm : public process_gdb_remote::ThreadGDBRemote {
+public:
+  ThreadWasm(Process &process, lldb::tid_t tid)
+      : process_gdb_remote::ThreadGDBRemote(process, tid) {}
+  ~ThreadWasm() override = default;
+
+  /// Retrieve the current call stack from the WebAssembly remote process.
+  bool GetWasmCallStack(std::vector<lldb::addr_t> &call_stack_pcs);
+
+  lldb::RegisterContextSP
+  CreateRegisterContextForFrame(StackFrame *frame) override;
+
+protected:
+  /// Thread protocol.
+  /// \{
+  Unwind &GetUnwinder() override;
+  /// \}
+
+  ThreadWasm(const ThreadWasm &);
+  const ThreadWasm &operator=(const ThreadWasm &) = delete;
+};
+
+} // namespace wasm
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H
diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp
new file mode 100644
index 000000000000000..081d0209c091f3b
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp
@@ -0,0 +1,79 @@
+// This file comes from https://reviews.llvm.org/D78978.
+// Author: [@paolosev](https://reviews.llvm.org/p/paolosev/).
+
+//===-- UnwindWasm.cpp ----------------------------------------------------===//
+//
+// 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 "UnwindWasm.h"
+#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h"
+#include "Plugins/Process/wasm/ProcessWasm.h"
+#include "Plugins/Process/wasm/ThreadWasm.h"
+#include "lldb/lldb-forward.h"
+#include "wasmRegisterContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace process_gdb_remote;
+using namespace wasm;
+
+class WasmGDBRemoteRegisterContext : public GDBRemoteRegisterContext {
+public:
+  WasmGDBRemoteRegisterContext(ThreadGDBRemote &thread,
+                               uint32_t concrete_frame_idx,
+                               GDBRemoteDynamicRegisterInfoSP &reg_info_sp,
+                               uint64_t pc)
+      : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info_sp, false,
+                                 false) {
+    PrivateSetRegisterValue(0, pc);
+  }
+};
+
+lldb::RegisterContextSP
+UnwindWasm::DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) {
+  if (m_frames.size() <= frame->GetFrameIndex()) {
+    return lldb::RegisterContextSP();
+  }
+
+  ThreadSP thread = frame->GetThread();
+  ProcessSP process_sp = thread->GetProcess();
+  ThreadWasm *wasm_thread = static_cast<ThreadWasm *>(thread.get());
+  ProcessWasm *wasm_process = static_cast<ProcessWasm *>(process_sp.get());
+  std::shared_ptr<WasmRegisterContext> reg_ctx_sp =
+      std::make_shared<WasmRegisterContext>(*wasm_thread,
+                                            frame->GetConcreteFrameIndex(),
+                                            wasm_process->GetRegisterInfo());
+  return reg_ctx_sp;
+}
+
+uint32_t UnwindWasm::DoGetFrameCount() {
+  if (!m_unwind_complete) {
+    m_unwind_complete = true;
+    m_frames.clear();
+
+    ThreadWasm &wasm_thread = static_cast<ThreadWasm &>(GetThread());
+    if (!wasm_thread.GetWasmCallStack(m_frames))
+      m_frames.clear();
+  }
+  return m_frames.size();
+}
+
+bool UnwindWasm::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
+                                       lldb::addr_t &pc,
+                                       bool &behaves_like_zeroth_frame) {
+  if (m_frames.size() == 0) {
+    DoGetFrameCount();
+  }
+
+  if (frame_idx < m_frames.size()) {
+    behaves_like_zeroth_frame = (frame_idx == 0);
+    cfa = 0;
+    pc = m_frames[frame_idx];
+    return true;
+  }
+  return false;
+}
\ No newline at end of file
diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.h b/lldb/source/Plugins/Process/wasm/UnwindWasm.h
new file mode 100644
index 000000000000000..01c36ce442b9b67
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.h
@@ -0,0 +1,58 @@
+// This file comes from https://reviews.llvm.org/D78978.
+// Author: [@paolosev](https://reviews.llvm.org/p/paolosev/).
+
+//===-- UnwindWasm.h --------------------------------------------*- C++ -*-===//
+//
+// 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_UnwindWasm_h_
+#define lldb_UnwindWasm_h_
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Unwind.h"
+#include <vector>
+
+namespace lldb_private {
+namespace wasm {
+
+/// UnwindWasm manages stack unwinding for a WebAssembly process.
+class UnwindWasm : public lldb_private::Unwind {
+public:
+  UnwindWasm(lldb_private::Thread &thread)
+      : Unwind(thread), m_frames(), m_unwind_complete(false) {}
+  ~UnwindWasm() override = default;
+
+protected:
+  /// Unwind protocol.
+  /// \{
+  void DoClear() override {
+    m_frames.clear();
+    m_unwind_complete = false;
+  }
+
+  uint32_t DoGetFrameCount() override;
+
+  bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
+                             lldb::addr_t &pc,
+                             bool &behaves_like_zeroth_frame) override;
+
+  lldb::RegisterContextSP
+  DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
+  /// \}
+
+private:
+  std::vector<lldb::addr_t> m_frames;
+  bool m_unwind_complete;
+
+  UnwindWasm(const UnwindWasm &);
+  const UnwindWasm &operator=(const UnwindWasm &) = delete;
+};
+
+} // namespace wasm
+} // namespace lldb_private
+
+#endif // lldb_UnwindWasm_h_
diff --git a/lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp
new file mode 100644
index 000000000000000..cb19f07ec23c397
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp
@@ -0,0 +1,103 @@
+//===---- wasmRegisterContext.cpp -----------------------------------------===//
+//
+// 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 "wasmRegisterContext.h"
+#include "Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "ProcessWasm.h"
+#include "ThreadWasm.h"
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+using namespace lldb_private::wasm;
+
+WasmRegisterContext::WasmRegisterContext(
+    wasm::ThreadWasm &thread, uint32_t concrete_frame_idx,
+    GDBRemoteDynamicRegisterInfoSP reg_info_sp)
+    : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info_sp, false,
+                               false) {}
+
+WasmRegisterContext::~WasmRegisterContext() = default;
+
+uint32_t WasmRegisterContext::ConvertRegisterKindToRegisterNumber(
+    lldb::RegisterKind kind, uint32_t num) 
+{
+    return num;
+}
+
+size_t WasmRegisterContext::GetRegisterCount() { return 0; }
+
+const RegisterInfo *WasmRegisterContext::GetRegisterInfoAtIndex(size_t reg) {
+  uint32_t tag = (reg >> 30) & 0x03;
+  if (tag == 0) {
+    return m_reg_info_sp->GetRegisterInfoAtIndex(reg);
+  }
+
+  WasmVirtualRegisterKinds kind = static_cast<WasmVirtualRegisterKinds>(tag - 1);
+  return new WasmVirtualRegisterInfo(kind, reg & 0x3fffffff);
+}
+
+size_t WasmRegisterContext::GetRegisterSetCount() { return 0; }
+
+const RegisterSet *WasmRegisterContext::GetRegisterSet(size_t reg_set) {
+  return nullptr;
+}
+
+bool WasmRegisterContext::ReadRegister(const RegisterInfo *reg_info,
+                                       RegisterValue &value) {
+  if (reg_info->name) {
+    return GDBRemoteRegisterContext::ReadRegister(reg_info, value);
+  }
+
+  ThreadWasm *thread = static_cast<ThreadWasm *>(&GetThread());
+  ProcessWasm *process = static_cast<ProcessWasm *>(thread->GetProcess().get());
+  if (!thread)
+    return false;
+
+//   uint32_t frame_index = thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
+  uint32_t frame_index = m_concrete_frame_idx;
+  WasmVirtualRegisterInfo *wasm_reg_info =
+      static_cast<WasmVirtualRegisterInfo *>(
+          const_cast<RegisterInfo *>(reg_info));
+  uint8_t buf[16];
+  size_t size = 0;
+  switch (wasm_reg_info->kind) {
+  case eLocal:
+    process->GetWasmLocal(frame_index, wasm_reg_info->index, buf, sizeof(buf),
+                          size);
+    break;
+  case eGlobal:
+    process->GetWasmGlobal(frame_index, wasm_reg_info->index, buf, sizeof(buf),
+                           size);
+    break;
+  case eOperandStack:
+    process->GetWasmStackValue(frame_index, wasm_reg_info->index, buf,
+                               sizeof(buf), size);
+    break;
+  default:
+    return false;
+  }
+
+  DataExtractor reg_data(buf, size, process->GetByteOrder(),
+                         process->GetAddressByteSize());
+  const bool partial_data_ok = false;
+  wasm_reg_info->byte_size = size;
+  wasm_reg_info->encoding = lldb::eEncodingUint;
+  Status error(value.SetValueFromData(*reg_info, reg_data,
+                                      reg_info->byte_offset, partial_data_ok));
+  return error.Success();
+}
+
+void WasmRegisterContext::InvalidateAllRegisters() {}
+
+bool WasmRegisterContext::WriteRegister(const RegisterInfo *reg_info,
+                                        const RegisterValue &value) {
+  return false;
+}
diff --git a/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h
new file mode 100644
index 000000000000000..61fb95cef632c9f
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h
@@ -0,0 +1,70 @@
+//===----- wasmRegisterContext.h --------------------------------*- C++ -*-===//
+//
+// 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_WASMREGISTERCONTEXT_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_WASMREGISTERCONTEXT_H
+
+#include "Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h"
+#include "ThreadWasm.h"
+#include "lldb/lldb-private-types.h"
+
+namespace lldb_private {
+namespace wasm {
+
+class WasmRegisterContext;
+
+typedef std::shared_ptr<WasmRegisterContext> WasmRegisterContextSP;
+
+enum WasmVirtualRegisterKinds {
+  eLocal = 0, ///< wasm local
+  eGlobal,       ///< wasm global
+  eOperandStack, ///< wasm operand stack
+  kNumWasmVirtualRegisterKinds
+};
+
+struct WasmVirtualRegisterInfo : public RegisterInfo {
+  WasmVirtualRegisterKinds kind;
+  uint32_t index;
+
+  WasmVirtualRegisterInfo(WasmVirtualRegisterKinds kind, uint32_t index)
+      : RegisterInfo(), kind(kind), index(index) {}
+};
+
+class WasmRegisterContext
+    : public process_gdb_remote::GDBRemoteRegisterContext {
+public:
+  WasmRegisterContext(
+      wasm::ThreadWasm &thread, uint32_t concrete_frame_idx,
+      process_gdb_remote::GDBRemoteDynamicRegisterInfoSP reg_info_sp);
+
+  ~WasmRegisterContext() 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;
+};
+
+} // namespace wasm
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_WASMREGISTERCONTEXT_H
diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp
index c345e33136070f2..9aa047b8997aec6 100644
--- a/lldb/source/Target/Platform.cpp
+++ b/lldb/source/Target/Platform.cpp
@@ -2112,6 +2112,14 @@ size_t Platform::GetSoftwareBreakpointTrapOpcode(Target &target,
     trap_opcode_size = sizeof(g_loongarch_opcode);
   } break;
 
+  case llvm::Triple::wasm32: {
+    static const uint8_t g_wasm_opcode[] = {
+        0x00}; // wasm only support remote debugging, we don't need to know trap
+               // opcode
+    trap_opcode = g_wasm_opcode;
+    trap_opcode_size = sizeof(g_wasm_opcode);
+  } break;
+
   default:
     return 0;
   }



More information about the lldb-commits mailing list