paolosev updated this revision to Diff 260216.
paolosev added a comment.

I am adding all the pieces to this patch to make the whole picture clearer; I 
thought to add a piece at the time to simplify reviews, but probably it ended 
up making things more obscure. I can always split this patch later and I need 
to refactor everything anyway.

So, the idea is to use DWARF as debug info for Wasm, as it is already supported 
by LLVM and Emscripten. For this we introduced some time ago the plugin classes 
ObjectFileWasm, SymbolVendorWasm and DynamicLoaderWasmDYLD. However, 
WebAssembly is peculiarly different from the native targets. When source code 
is compiled to Wasm, Clang produces a module that contains Wasm bytecode (a bit 
like it happens with Java and C#) and the DWARF info refers to this bytecode.
The Wasm module then runs in a Wasm runtime. (It is also possible to 
AoT-compile Wasm to native, but this is outside the scope of this patch).

Therefore, LLDB cannot debug Wasm by just controlling the inferior process, but 
it needs to talk with the Wasm engine to query the Wasm engine state. For 
example, for backtrace, only the runtime knows what is the current call stack. 
Hence the idea of using the gdb-remote protocol: if a Wasm engine has a GDB 
stub LLDB can connect to it to start a debugging session and access its state.

Wasm execution is defined in terms of a stack machine. There are no registers 
(besides the implicit IP) and most Wasm instructions push/pop values into/from 
a virtual stack. Besides the stack the other possible stores are a set of 
parameters and locals defined in the function, a set of global variables 
defined in the module and the module memory, which is separated from the code 
address space.

The DWARF debug info to evaluate the value of variables is defined in terms of 
these constructs. For example, we can have something like this in DWARF:

  0x00005a88:      DW_TAG_variable
                            DW_AT_location      (0x000006f3: 
                               [0x00000840, 0x00000850): DW_OP_WASM_location 
0x0 +8, DW_OP_stack_value)
                            DW_AT_name  ("xx")
                            DW_AT_type  (0x00002b17 "float")
                            […]

Which says that on that address range the value of ‘xx’ can be evaluated as the 
content of the 8th local. Here DW_OP_WASM_location is a Wasm-specific opcode, 
with two args, the first defines the store (0: Local, 1: Global, 2: the operand 
stack) and the index in that store. In most cases the value of the variable 
could be retrieved from the Wasm memory instead.

So, when LLDB wants to evaluate this variable, in 
`DWARFExpression::Evaluate()`, it needs to know what is the current the value 
of the Wasm locals, or to access the memory, and for this it needs to query the 
Wasm engine.

This is why there are changes to DWARFExpression::Evaluate(), to support the 
DW_OP_WASM_location case, and this is also why I created a class that derives 
from ProcessGDBRemote and overrides ReadMemory() in order to query the wasm 
engine. Also Value::GetValueAsData() needs to be modified when the value is 
retrieved from Wasm memory.

`GDBRemoteCommunicationClient` needs to be extended with a few Wasm-specific 
query packets:

- qWasmGlobal: query the value of a Wasm global variable
- qWasmLocal: query the value of a Wasm function argument or local
- qWasmStackValue: query the value in the Wasm operand stack
- qWasmMem: read from a Wasm memory
- qWasmCallStack: retrieve the Wasm call stack.

These are all the changes we need to fully support Wasm debugging.

Why the `IWasmProcess` interface? I was not sure whether gdb-remote should be 
the only way to access the engine state. In the future LLDB could also use some 
other (and less chatty) mechanisms to communicate with a Wasm engine. I did not 
want to put a dependency on GDBRemote in a class like DWARFExpression or Value, 
which should not care about these details. Therefore, I thought that the new 
class WasmProcessGDBRemote could implement the IWasmProcess interface, 
forwarding requests through the base class ProcessGDBRemote which then send the 
new gdb-remote query packets. But I agree that this makes the code certainly 
more convoluted and quite ugly.

My initial idea was to keep all the Wasm-related code as much as possible 
isolated in plugin classes. Now, I guess that the next steps instead would be 
to refactor the code to eliminate the new classes WasmProcessGDBRemote and 
UnwindWasm and modify existing ProcessGDBRemote and ThreadGDBRemote instead. 
However, I am not sure if this is possible without touching also the base 
classes Process and Thread. For example, let’s consider function 
DWARFExpression::Evaluate(). There, when the DWARF opcode is 
DW_OP_WASM_location, we need to access the Wasm state.  We can get to the 
Process object with frame->CalculateProcess() and then can we assume the 
process must always be a ProcessGDBRemote if the target machine is a 
llvm::Triple::wasm32 and cast Process* to  ProcessGDBRemote* and then use 
Wasm-specific query functions added to that class? Would this pattern be 
acceptable, in your opinion?

PS, I am sorry for the late reply… this lockdown is making me a little 
unproductive… :-(


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D78801/new/

https://reviews.llvm.org/D78801

Files:
  lldb/source/Core/Value.cpp
  lldb/source/Expression/DWARFExpression.cpp
  lldb/source/Plugins/CMakeLists.txt
  lldb/source/Plugins/Plugins.def.in
  lldb/source/Plugins/Process/CMakeLists.txt
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h
  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/UnwindWasm.cpp
  lldb/source/Plugins/Process/wasm/UnwindWasm.h
  lldb/source/Target/Thread.cpp

Index: lldb/source/Target/Thread.cpp
===================================================================
--- lldb/source/Target/Thread.cpp
+++ lldb/source/Target/Thread.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/Target/Thread.h"
+#include "Plugins/Process/wasm/UnwindWasm.h"
 #include "lldb/Breakpoint/BreakpointLocation.h"
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/FormatEntity.h"
@@ -1853,8 +1854,13 @@
 }
 
 Unwind &Thread::GetUnwinder() {
-  if (!m_unwinder_up)
-    m_unwinder_up.reset(new UnwindLLDB(*this));
+  if (!m_unwinder_up) {
+    if (CalculateTarget()->GetArchitecture().GetMachine() ==
+        llvm::Triple::wasm32)
+      m_unwinder_up.reset(new wasm::UnwindWasm(*this));
+    else
+      m_unwinder_up.reset(new UnwindLLDB(*this));
+  }
   return *m_unwinder_up;
 }
 
Index: lldb/source/Plugins/Process/wasm/UnwindWasm.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Process/wasm/UnwindWasm.h
@@ -0,0 +1,49 @@
+//===-- 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 {
+
+class UnwindWasm : public lldb_private::Unwind {
+public:
+  UnwindWasm(lldb_private::Thread &thread)
+      : Unwind(thread), m_frames(), m_unwind_complete(false) {}
+  ~UnwindWasm() override = default;
+
+protected:
+  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;
+
+  DISALLOW_COPY_AND_ASSIGN(UnwindWasm);
+};
+
+} // namespace wasm
+} // namespace lldb_private
+
+#endif // lldb_UnwindWasm_h_
Index: lldb/source/Plugins/Process/wasm/UnwindWasm.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Process/wasm/UnwindWasm.cpp
@@ -0,0 +1,57 @@
+//===-- 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/wasm/ProcessWasm.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace process_gdb_remote;
+using namespace wasm;
+
+lldb::RegisterContextSP
+UnwindWasm::DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) {
+  if (m_frames.size() <= frame->GetFrameIndex()) {
+    return lldb::RegisterContextSP();
+  }
+
+  IWasmProcess *wasm_process =
+      static_cast<WasmProcessGDBRemote *>(frame->CalculateProcess().get());
+  return wasm_process->CreateRegisterContextForFrame(
+      frame, m_frames[frame->GetFrameIndex()]);
+}
+
+uint32_t UnwindWasm::DoGetFrameCount() {
+  if (!m_unwind_complete) {
+    m_unwind_complete = true;
+    m_frames.clear();
+
+    IWasmProcess *wasm_process =
+        static_cast<WasmProcessGDBRemote *>(GetThread().GetProcess().get());
+    if (wasm_process)
+      if (!wasm_process->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
Index: lldb/source/Plugins/Process/wasm/ProcessWasm.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Process/wasm/ProcessWasm.h
@@ -0,0 +1,97 @@
+//===-- 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 {
+
+// Interface IWasmProcess provides the access to the Wasm program state
+//  retrieved from the Wasm engine.
+struct IWasmProcess {
+  ~IWasmProcess() = default;
+
+  virtual lldb::RegisterContextSP
+  CreateRegisterContextForFrame(StackFrame *frame, lldb::addr_t pc) = 0;
+
+  virtual bool GetWasmLocal(int frame_index, int index, void *buf,
+                            size_t buffer_size, size_t &size) = 0;
+
+  virtual bool GetWasmGlobal(int frame_index, int index, void *buf,
+                             size_t buffer_size, size_t &size) = 0;
+
+  virtual bool GetWasmStackValue(int frame_index, int index, void *buf,
+                                 size_t buffer_size, size_t &size) = 0;
+
+  virtual bool WasmReadMemory(int frame_index, lldb::addr_t addr, void *buf,
+                              size_t buffer_size) = 0;
+
+  virtual bool GetWasmCallStack(std::vector<lldb::addr_t> &call_stack_pcs) = 0;
+};
+
+class WasmProcessGDBRemote : public process_gdb_remote::ProcessGDBRemote,
+                             public IWasmProcess {
+public:
+  WasmProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp);
+
+  ~WasmProcessGDBRemote() override;
+
+  static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp,
+                                        lldb::ListenerSP listener_sp,
+                                        const FileSpec *crash_file_path);
+
+  static void Initialize();
+
+  static void DebuggerInitialize(Debugger &debugger);
+
+  static void Terminate();
+
+  static ConstString GetPluginNameStatic();
+
+  static const char *GetPluginDescriptionStatic();
+
+  // PluginInterface protocol
+  ConstString GetPluginName() override;
+
+  uint32_t GetPluginVersion() override;
+
+  // Process
+  size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+                    Status &error) override;
+
+  // IWasmProcess
+  lldb::RegisterContextSP
+  CreateRegisterContextForFrame(StackFrame *frame,
+                                lldb::addr_t address) override;
+
+  bool GetWasmLocal(int frame_index, int index, void *buf, size_t buffer_size,
+                    size_t &size) override;
+
+  bool GetWasmGlobal(int frame_index, int index, void *buf, size_t buffer_size,
+                     size_t &size) override;
+
+  bool GetWasmStackValue(int frame_index, int index, void *buf,
+                         size_t buffer_size, size_t &size) override;
+
+  bool WasmReadMemory(int frame_index, lldb::addr_t addr, void *buf,
+                      size_t buffer_size) override;
+
+  bool GetWasmCallStack(std::vector<lldb::addr_t> &call_stack_pcs) override;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(WasmProcessGDBRemote);
+};
+
+} // namespace wasm
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H
Index: lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
@@ -0,0 +1,122 @@
+//===-- 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 "Plugins/Process/gdb-remote/ThreadGDBRemote.h"
+#include "lldb/Core/PluginManager.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+using namespace lldb_private::wasm;
+
+LLDB_PLUGIN_DEFINE(WasmProcessGDBRemote)
+
+// ProcessGDBRemote constructor
+WasmProcessGDBRemote::WasmProcessGDBRemote(lldb::TargetSP target_sp,
+                                           ListenerSP listener_sp)
+    : ProcessGDBRemote(target_sp, listener_sp) {}
+
+// Destructor
+WasmProcessGDBRemote::~WasmProcessGDBRemote() {}
+
+void WasmProcessGDBRemote::Initialize() {
+  static llvm::once_flag g_once_flag;
+
+  llvm::call_once(g_once_flag, []() {
+    PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                  GetPluginDescriptionStatic(), CreateInstance,
+                                  DebuggerInitialize);
+  });
+}
+
+void WasmProcessGDBRemote::DebuggerInitialize(Debugger &debugger) {
+  ProcessGDBRemote::DebuggerInitialize(debugger);
+}
+
+// PluginInterface
+ConstString WasmProcessGDBRemote::GetPluginName() {
+  return GetPluginNameStatic();
+}
+
+uint32_t WasmProcessGDBRemote::GetPluginVersion() { return 1; }
+
+ConstString WasmProcessGDBRemote::GetPluginNameStatic() {
+  static ConstString g_name("wasm");
+  return g_name;
+}
+
+const char *WasmProcessGDBRemote::GetPluginDescriptionStatic() {
+  return "GDB Remote protocol based WebAssembly debugging plug-in.";
+}
+
+void WasmProcessGDBRemote::Terminate() {
+  PluginManager::UnregisterPlugin(WasmProcessGDBRemote::CreateInstance);
+}
+
+lldb::ProcessSP
+WasmProcessGDBRemote::CreateInstance(lldb::TargetSP target_sp,
+                                     ListenerSP listener_sp,
+                                     const FileSpec *crash_file_path) {
+  lldb::ProcessSP process_sp;
+  if (crash_file_path == nullptr)
+    process_sp = std::make_shared<WasmProcessGDBRemote>(target_sp, listener_sp);
+  return process_sp;
+}
+
+size_t WasmProcessGDBRemote::ReadMemory(lldb::addr_t vm_addr, void *buf,
+                                        size_t size, Status &error) {
+  if (vm_addr < 0x100000000) {
+    if (GetGDBRemote().WasmReadMemory(0 /*frame_index*/, vm_addr, buf, size)) {
+      return size;
+    }
+    return 0;
+  } else
+    return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error);
+}
+
+lldb::RegisterContextSP
+WasmProcessGDBRemote::CreateRegisterContextForFrame(StackFrame *frame,
+                                                    lldb::addr_t pc) {
+  ThreadGDBRemote *gdb_thread =
+      static_cast<ThreadGDBRemote *>(frame->CalculateThread().get());
+  std::shared_ptr<GDBRemoteRegisterContext> reg_ctx_sp =
+      std::make_shared<GDBRemoteRegisterContext>(
+          *gdb_thread, frame->GetFrameIndex(), m_register_info, false, false);
+  reg_ctx_sp->PrivateSetRegisterValue(0, pc);
+  return reg_ctx_sp;
+}
+
+bool WasmProcessGDBRemote::GetWasmLocal(int frame_index, int index, void *buf,
+                                        size_t buffer_size, size_t &size) {
+  return GetGDBRemote().GetWasmLocal(frame_index, index, buf, buffer_size,
+                                     size);
+}
+
+bool WasmProcessGDBRemote::GetWasmGlobal(int frame_index, int index, void *buf,
+                                         size_t buffer_size, size_t &size) {
+  return GetGDBRemote().GetWasmGlobal(frame_index, index, buf, buffer_size,
+                                      size);
+}
+
+bool WasmProcessGDBRemote::GetWasmStackValue(int frame_index, int index,
+                                             void *buf, size_t buffer_size,
+                                             size_t &size) {
+  return GetGDBRemote().GetWasmStackValue(frame_index, index, buf, buffer_size,
+                                          size);
+}
+
+bool WasmProcessGDBRemote::WasmReadMemory(int frame_index, lldb::addr_t addr,
+                                          void *buf, size_t buffer_size) {
+  return GetGDBRemote().WasmReadMemory(frame_index, addr, buf, buffer_size);
+}
+
+bool WasmProcessGDBRemote::GetWasmCallStack(
+    std::vector<lldb::addr_t> &call_stack_pcs) {
+  return GetGDBRemote().GetWasmCallStack(call_stack_pcs);
+}
Index: lldb/source/Plugins/Process/wasm/CMakeLists.txt
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Process/wasm/CMakeLists.txt
@@ -0,0 +1,11 @@
+
+add_lldb_library(lldbPluginProcessWasm PLUGIN
+  ProcessWasm.cpp
+  UnwindWasm.cpp
+
+  LINK_LIBS
+    lldbCore
+    ${LLDB_PLUGINS}
+  LINK_COMPONENTS
+    Support
+  )
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h
@@ -23,6 +23,11 @@
 class StringExtractor;
 
 namespace lldb_private {
+
+namespace wasm {
+class WasmProcessGDBRemote;
+}
+
 namespace process_gdb_remote {
 
 class ThreadGDBRemote;
@@ -75,6 +80,7 @@
 
 protected:
   friend class ThreadGDBRemote;
+  friend class wasm::WasmProcessGDBRemote;
 
   bool ReadRegisterBytes(const RegisterInfo *reg_info, DataExtractor &data);
 
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -297,6 +297,17 @@
 
   bool GetThreadStopInfo(lldb::tid_t tid, StringExtractorGDBRemote &response);
 
+  // WebAssembly-specific commands
+  bool GetWasmGlobal(int frame_index, int index, void *buf, size_t buffer_size,
+                     size_t &size);
+  bool GetWasmLocal(int frame_index, int index, void *buf, size_t buffer_size,
+                    size_t &size);
+  bool GetWasmStackValue(int frame_index, int index, void *buf,
+                         size_t buffer_size, size_t &size);
+  bool WasmReadMemory(int frame_index, lldb::addr_t vm_addr, void *buf,
+                      size_t buffer_size);
+  bool GetWasmCallStack(std::vector<lldb::addr_t> &call_stack_pcs);
+
   bool SupportsGDBStoppointPacket(GDBStoppointType type) {
     switch (type) {
     case eBreakpointSoftware:
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -2694,6 +2694,141 @@
   return false;
 }
 
+bool GDBRemoteCommunicationClient::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 (SendPacketAndWaitForResponse(packet.GetString(), response, false) !=
+      PacketResult::Success) {
+    return false;
+  }
+
+  if (!response.IsNormalResponse()) {
+    return false;
+  }
+
+  DataBufferSP 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 GDBRemoteCommunicationClient::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 (SendPacketAndWaitForResponse(packet.GetString(), response, false) !=
+      PacketResult::Success) {
+    return false;
+  }
+
+  if (!response.IsNormalResponse()) {
+    return false;
+  }
+
+  DataBufferSP 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 GDBRemoteCommunicationClient::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 (SendPacketAndWaitForResponse(packet.GetString(), response, false) !=
+      PacketResult::Success) {
+    return false;
+  }
+
+  if (!response.IsNormalResponse()) {
+    return false;
+  }
+
+  DataBufferSP 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 GDBRemoteCommunicationClient::WasmReadMemory(int frame_index,
+                                                  lldb::addr_t addr, void *buf,
+                                                  size_t buffer_size) {
+  char packet[64];
+  int packet_len = ::snprintf(
+      packet, sizeof(packet), "qWasmMem:%d;%" PRIx64 ";%" PRIx64, frame_index,
+      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 (SendPacketAndWaitForResponse(packet, response, true) ==
+      PacketResult::Success) {
+    if (response.IsNormalResponse()) {
+      return buffer_size ==
+             response.GetHexBytes(llvm::MutableArrayRef<uint8_t>(
+                                      static_cast<uint8_t *>(buf), buffer_size),
+                                  '\xdd');
+    }
+  }
+  return false;
+}
+
+bool GDBRemoteCommunicationClient::GetWasmCallStack(
+    std::vector<lldb::addr_t> &call_stack_pcs) {
+  call_stack_pcs.clear();
+  StreamString packet;
+  packet.Printf("qWasmCallStack");
+  StringExtractorGDBRemote response;
+  if (SendPacketAndWaitForResponse(packet.GetString(), response, false) !=
+      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;
+}
+
 uint8_t GDBRemoteCommunicationClient::SendGDBStoppointTypePacket(
     GDBStoppointType type, bool insert, addr_t addr, uint32_t length) {
   Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
Index: lldb/source/Plugins/Process/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/Process/CMakeLists.txt
+++ lldb/source/Plugins/Process/CMakeLists.txt
@@ -17,3 +17,4 @@
 add_subdirectory(elf-core)
 add_subdirectory(mach-core)
 add_subdirectory(minidump)
+add_subdirectory(wasm)
Index: lldb/source/Plugins/Plugins.def.in
===================================================================
--- lldb/source/Plugins/Plugins.def.in
+++ lldb/source/Plugins/Plugins.def.in
@@ -31,6 +31,7 @@
 
 @LLDB_ENUM_PLUGINS@
 @LLDB_PROCESS_WINDOWS_PLUGIN@
+@LLDB_PROCESS_WASM_PLUGIN@
 @LLDB_PROCESS_GDB_PLUGIN@
 
 #undef LLDB_PLUGIN
Index: lldb/source/Plugins/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/CMakeLists.txt
+++ lldb/source/Plugins/CMakeLists.txt
@@ -30,6 +30,7 @@
 # FIXME: ProcessWindowsCommon needs to be initialized after all other process
 # plugins but before ProcessGDBRemote.
 set(LLDB_PROCESS_WINDOWS_PLUGIN "")
+set(LLDB_PROCESS_WASM_PLUGIN "")
 set(LLDB_PROCESS_GDB_PLUGIN "")
 
 foreach(p ${LLDB_ALL_PLUGINS})
@@ -41,6 +42,8 @@
     set(LLDB_PROCESS_WINDOWS_PLUGIN "LLDB_PLUGIN(${pStripped})\n")
   elseif(${pStripped} STREQUAL "ProcessGDBRemote")
     set(LLDB_PROCESS_GDB_PLUGIN "LLDB_PLUGIN(${pStripped})\n")
+  elseif(${pStripped} STREQUAL "ProcessWasm")
+    set(LLDB_PROCESS_WASM_PLUGIN "LLDB_PLUGIN(WasmProcessGDBRemote)\n")
   else()
     set(LLDB_ENUM_PLUGINS "${LLDB_ENUM_PLUGINS}LLDB_PLUGIN(${pStripped})\n")
   endif()
Index: lldb/source/Expression/DWARFExpression.cpp
===================================================================
--- lldb/source/Expression/DWARFExpression.cpp
+++ lldb/source/Expression/DWARFExpression.cpp
@@ -36,6 +36,7 @@
 #include "lldb/Target/Target.h"
 #include "lldb/Target/Thread.h"
 
+#include "Plugins/Process/wasm/ProcessWasm.h"
 #include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
 
 using namespace lldb;
@@ -2483,6 +2484,66 @@
       stack.back().SetValueType(Value::eValueTypeLoadAddress);
     } break;
 
+    case DW_OP_WASM_location: {
+      if (frame) {
+        const llvm::Triple::ArchType machine =
+            frame->CalculateTarget()->GetArchitecture().GetMachine();
+        if (machine == llvm::Triple::wasm32) {
+          wasm::IWasmProcess *wasm_process =
+              static_cast<wasm::WasmProcessGDBRemote *>(
+                  frame->CalculateProcess().get());
+          int frame_index = frame->GetConcreteFrameIndex();
+          uint64_t wasm_op = opcodes.GetULEB128(&offset);
+          uint64_t index = opcodes.GetULEB128(&offset);
+          uint8_t buf[16];
+          size_t size = 0;
+          switch (wasm_op) {
+          case 0: // Local
+            if (!wasm_process->GetWasmLocal(frame_index, index, buf, 16,
+                                            size)) {
+              return false;
+            }
+            break;
+          case 1: // Global
+            if (!wasm_process->GetWasmGlobal(frame_index, index, buf, 16,
+                                             size)) {
+              return false;
+            }
+            break;
+          case 2: // Operand Stack
+            if (!wasm_process->GetWasmStackValue(frame_index, index, buf, 16,
+                                                 size)) {
+              return false;
+            }
+            break;
+          default:
+            return false;
+          }
+
+          if (size == sizeof(uint32_t)) {
+            uint32_t value;
+            memcpy(&value, buf, size);
+            stack.push_back(Scalar(value));
+          } else if (size == sizeof(uint64_t)) {
+            uint64_t value;
+            memcpy(&value, buf, size);
+            stack.push_back(Scalar(value));
+          } else
+            return false;
+        } else {
+          if (error_ptr)
+            error_ptr->SetErrorString("Invalid target architecture for "
+                                      "DW_OP_WASM_location opcode.");
+          return false;
+        }
+      } else {
+        if (error_ptr)
+          error_ptr->SetErrorString("Invalid stack frame in context for "
+                                    "DW_OP_WASM_location opcode.");
+        return false;
+      }
+    } break;
+
     // OPCODE: DW_OP_addrx (DW_OP_GNU_addr_index is the legacy name.)
     // OPERANDS: 1
     //      ULEB128: index to the .debug_addr section
Index: lldb/source/Core/Value.cpp
===================================================================
--- lldb/source/Core/Value.cpp
+++ lldb/source/Core/Value.cpp
@@ -30,6 +30,8 @@
 #include "lldb/lldb-forward.h"
 #include "lldb/lldb-types.h"
 
+#include "Plugins/Process/wasm/ProcessWasm.h"
+
 #include <memory>
 #include <string>
 
@@ -563,8 +565,27 @@
         Process *process = exe_ctx->GetProcessPtr();
 
         if (process) {
-          const size_t bytes_read =
-              process->ReadMemory(address, dst, byte_size, error);
+          StackFrame *frame = exe_ctx->GetFramePtr();
+          size_t bytes_read = 0;
+
+          bool isWasm =
+              frame
+                  ? (frame->CalculateTarget()->GetArchitecture().GetMachine() ==
+                     llvm::Triple::wasm32)
+                  : false;
+          if (isWasm) {
+            int frame_index = frame->GetConcreteFrameIndex();
+            wasm::IWasmProcess *wasm_process =
+                static_cast<wasm::WasmProcessGDBRemote *>(
+                    frame->CalculateProcess().get());
+            if (wasm_process->WasmReadMemory(frame_index, address, dst,
+                                             byte_size)) {
+              bytes_read = byte_size;
+            }
+          } else {
+            bytes_read = process->ReadMemory(address, dst, byte_size, error);
+          }
+
           if (bytes_read != byte_size)
             error.SetErrorStringWithFormat(
                 "read memory from 0x%" PRIx64 " failed (%u of %u bytes read)",
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to