[Lldb-commits] [lldb] [lldb] Implement WebAssembly debugging (PR #76683)

via lldb-commits lldb-commits at lists.llvm.org
Fri Jan 12 07:27:35 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Paolo Severini (paolosevMSFT)

<details>
<summary>Changes</summary>

Add support for source-level debugging of WebAssembly code that runs in a WebAssembly engine. 

The idea is to use the GDB-remote protocol to connect to a Wasm engine that implements a GDB-remote stub that offers the ability to access the engine runtime internal state. This protocol is already supported by the "**[WebAssembly Micro Runtime](https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/source_debugging.md)**" and the WAMR team greatly contributed to this work.

A WebAssembly debugging session can be started using the new command:
`wasm [<hostname>:]<portnum>`

The implementation is almost completely contained in Plugin classes and the only Wasm-specific changes to Core classes is in `class DWARFExpression` that handles a new Wasm-specific DWARF opcode (`DW_OP_WASM_location`) added to the DWARF standard.

---

Patch is 40.00 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/76683.diff


17 Files Affected:

- (modified) lldb/include/lldb/Target/Process.h (+40) 
- (modified) lldb/source/Core/Value.cpp (+1-1) 
- (modified) lldb/source/Expression/DWARFExpression.cpp (+42) 
- (modified) lldb/source/Interpreter/CommandInterpreter.cpp (+18) 
- (modified) lldb/source/Plugins/Process/CMakeLists.txt (+1) 
- (modified) lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp (+1-2) 
- (modified) lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (+6-1) 
- (modified) lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h (+2) 
- (added) lldb/source/Plugins/Process/wasm/CMakeLists.txt (+15) 
- (added) lldb/source/Plugins/Process/wasm/ProcessWasm.cpp (+295) 
- (added) lldb/source/Plugins/Process/wasm/ProcessWasm.h (+135) 
- (added) lldb/source/Plugins/Process/wasm/ThreadWasm.cpp (+57) 
- (added) lldb/source/Plugins/Process/wasm/ThreadWasm.h (+47) 
- (added) lldb/source/Plugins/Process/wasm/UnwindWasm.cpp (+79) 
- (added) lldb/source/Plugins/Process/wasm/UnwindWasm.h (+58) 
- (added) lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp (+108) 
- (added) lldb/source/Plugins/Process/wasm/wasmRegisterContext.h (+75) 


``````````diff
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index 24c599e044c78f..587ae085b479b7 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -1548,6 +1548,46 @@ class Process : public std::enable_shared_from_this<Process>,
   virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
                             Status &error);
 
+  /// Read of memory from a process.
+  ///
+  /// This function will read memory from the current process's address space
+  /// and remove any traps that may have been inserted into the memory.
+  ///
+  /// This overloads accepts an ExecutionContext as additional argument. By
+  /// default, it calls the previous overload without the ExecutionContext
+  /// argument, but it can be overridden by Process subclasses.
+  ///
+  /// \param[in] vm_addr
+  ///     A virtual load address that indicates where to start reading
+  ///     memory from.
+  ///
+  /// \param[out] buf
+  ///     A byte buffer that is at least \a size bytes long that
+  ///     will receive the memory bytes.
+  ///
+  /// \param[in] size
+  ///     The number of bytes to read.
+  ///
+  /// \param[in] exe_ctx
+  ///    The current execution context, if available.
+  ///
+  /// \param[out] error
+  ///     An error that indicates the success or failure of this
+  ///     operation. If error indicates success (error.Success()),
+  ///     then the value returned can be trusted, otherwise zero
+  ///     will be returned.
+  ///
+  /// \return
+  ///     The number of bytes that were actually read into \a buf. If
+  ///     the returned number is greater than zero, yet less than \a
+  ///     size, then this function will get called again with \a
+  ///     vm_addr, \a buf, and \a size updated appropriately. Zero is
+  ///     returned in the case of an error.
+  virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+                            ExecutionContext *exe_ctx, Status &error) {
+    return ReadMemory(vm_addr, buf, size, error);
+  }
+
   /// Read of memory from a process.
   ///
   /// This function has the same semantics of ReadMemory except that it
diff --git a/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp
index 995cc934c82044..47a5fdee773886 100644
--- a/lldb/source/Core/Value.cpp
+++ b/lldb/source/Core/Value.cpp
@@ -552,7 +552,7 @@ Status Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data,
 
         if (process) {
           const size_t bytes_read =
-              process->ReadMemory(address, dst, byte_size, error);
+              process->ReadMemory(address, dst, byte_size, exe_ctx, error);
           if (bytes_read != byte_size)
             error.SetErrorStringWithFormat(
                 "read memory from 0x%" PRIx64 " failed (%u of %u bytes read)",
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index fe4928d4f43a43..1693e390c2e920 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/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 00651df48b6224..bcacc7aabb66ca 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -794,6 +794,24 @@ void CommandInterpreter::LoadCommandDictionary() {
     }
   }
 
+  std::unique_ptr<CommandObjectRegexCommand> connect_wasm_cmd_up(
+      new CommandObjectRegexCommand(
+          *this, "wasm",
+          "Connect to a WebAssembly process via remote GDB server.  "
+          "If no host is specifed, localhost is assumed.",
+          "wasm [<hostname>:]<portnum>", 0, false));
+  if (connect_wasm_cmd_up) {
+    if (connect_wasm_cmd_up->AddRegexCommand(
+            "^([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)$",
+            "process connect --plugin wasm connect://%1:%2") &&
+        connect_wasm_cmd_up->AddRegexCommand(
+            "^([[:digit:]]+)$",
+            "process connect --plugin wasm connect://localhost:%1")) {
+      CommandObjectSP command_sp(connect_wasm_cmd_up.release());
+      m_command_dict[std::string(command_sp->GetCommandName())] = command_sp;
+    }
+  }
+
   std::unique_ptr<CommandObjectRegexCommand> connect_kdp_remote_cmd_up(
       new CommandObjectRegexCommand(
           *this, "kdp-remote",
diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt
index a51d0f7afd1759..be109a303e8669 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/Windows/Common/TargetThreadWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp
index ad67e764fe10f2..50d0bd37bb0a49 100644
--- a/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp
@@ -29,8 +29,7 @@
 using namespace lldb;
 using namespace lldb_private;
 
-using GetThreadDescriptionFunctionPtr = HRESULT
-WINAPI (*)(HANDLE hThread, PWSTR *ppszThreadDescription);
+using GetThreadDescriptionFunctionPtr = HRESULT  (*)(HANDLE hThread, PWSTR *ppszThreadDescription);
 
 TargetThreadWindows::TargetThreadWindows(ProcessWindows &process,
                                          const HostThread &thread)
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 316be471df9295..674bbc9ff4fd0b 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -1628,6 +1628,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,
@@ -1652,7 +1657,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 c1ea1cc7905587..0463e39b7a63a4 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -355,6 +355,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 00000000000000..c47eec7464ed8a
--- /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 00000000000000..d8ab6d645884c3
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
@@ -0,0 +1,295 @@
+// 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-unknown-unknown-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);
+
+  switch (wasm_addr.GetType()) {
+  case WasmAddressType::Memory:
+    return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error);
+  case WasmAddressType::Object:
+    // TODO ?
+    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::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+                               ExecutionContext *exe_ctx, Status &error) {
+  wasm_addr_t wasm_addr(vm_addr);
+
+  switch (wasm_addr.GetType()) {
+  case WasmAddressType::Memory: {
+    // If we don't have a valid module_id, this is actually a read from the
+    // Wasm memory space. We can calculate the module_id from the execution
+    // context.
+    if (wasm_addr.module_id == 0 && exe_ctx != nullptr) {
+      StackFrame *frame = exe_ctx->GetFramePtr();
+      assert(frame->CalculateTarget()->GetArchitecture().GetMachine() ==
+             llvm::Triple::wasm32);
+      wasm_addr.module_id = wasm_addr_t(frame->GetStackID().GetPC()).module_id;
+      wasm_addr.type = WasmAddressType::Memory;
+    }
+    if (WasmReadMemory(wasm_addr.module_id, wasm_addr.offset, buf, size))
+      return size;
+    error.SetErrorStringWithFormat("Wasm memory read failed for 0x%" PRIx64,
+                                   vm_addr);
+    return 0;
+  }
+  case WasmAddressType::Object:
+    // TODO ?
+    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 00000000000000..c4579390daf082
--- /dev/null
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h
@@ -0,0 +1,135 @@
+// 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 L...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/76683


More information about the lldb-commits mailing list