Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[lldb] Implement WebAssembly debugging #76683

Closed
wants to merge 1,109 commits into from

Conversation

paolosevMSFT
Copy link
Contributor

@paolosevMSFT paolosevMSFT commented Jan 1, 2024

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" 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.

Copy link

github-actions bot commented Jan 1, 2024

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff 25cd249355b0f3192ca5b0c69514ad68a1cb8897 61b46091e9f04ad628fe9605464080be8c9cf29a -- lldb/source/Plugins/Process/wasm/ProcessWasm.cpp lldb/source/Plugins/Process/wasm/ProcessWasm.h lldb/source/Plugins/Process/wasm/ThreadWasm.cpp lldb/source/Plugins/Process/wasm/ThreadWasm.h lldb/source/Plugins/Process/wasm/UnwindWasm.cpp lldb/source/Plugins/Process/wasm/UnwindWasm.h lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp lldb/source/Plugins/Process/wasm/wasmRegisterContext.h lldb/include/lldb/Target/Process.h lldb/source/Core/Value.cpp lldb/source/Expression/DWARFExpression.cpp lldb/source/Interpreter/CommandInterpreter.cpp lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
View the diff from clang-format here.
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index 1693e390c2..8e8ea610b8 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -350,8 +350,7 @@ static offset_t GetOpcodeDataSize(const DataExtractor &data,
     uint8_t wasm_op = data.GetU8(&offset);
     if (wasm_op == 3) {
       data.GetU32(&offset);
-    }
-    else {
+    } else {
       data.GetULEB128(&offset);
     }
     return offset - data_offset;
@@ -2612,13 +2611,13 @@ bool DWARFExpression::Evaluate(
 
       /* LLDB doesn't have an address space to represents WebAssembly Locals,
        * GLobals and operand stacks.
-       * We encode these elements into virtual registers: 
+       * 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 
+       *    3: Operand stack value
        */
       if (wasm_op == 3) {
         index = opcodes.GetU32(&offset);
diff --git a/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp
index 50d0bd37bb..63cf64f20b 100644
--- a/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp
@@ -29,7 +29,8 @@
 using namespace lldb;
 using namespace lldb_private;
 
-using GetThreadDescriptionFunctionPtr = HRESULT  (*)(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/wasm/ProcessWasm.cpp b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
index d8ab6d6458..df6236a712 100644
--- a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
@@ -29,7 +29,8 @@ LLDB_PLUGIN_DEFINE(ProcessWasm)
 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"});
+  m_unix_signals_sp =
+      UnixSignals::Create(ArchSpec{"wasm32-unknown-unknown-wasm"});
 }
 
 void ProcessWasm::Initialize() {
@@ -73,7 +74,7 @@ lldb::ProcessSP ProcessWasm::CreateInstance(lldb::TargetSP target_sp,
 }
 
 bool ProcessWasm::CanDebug(lldb::TargetSP target_sp,
-                                bool plugin_specified_by_name) {
+                           bool plugin_specified_by_name) {
   if (plugin_specified_by_name)
     return true;
 
@@ -143,7 +144,7 @@ size_t ProcessWasm::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
 }
 
 size_t ProcessWasm::WasmReadMemory(uint32_t wasm_module_id, lldb::addr_t addr,
-                                 void *buf, size_t buffer_size) {
+                                   void *buf, size_t buffer_size) {
   char packet[64];
   int packet_len =
       ::snprintf(packet, sizeof(packet), "qWasmMem:%d;%" PRIx64 ";%" PRIx64,
@@ -152,7 +153,8 @@ size_t ProcessWasm::WasmReadMemory(uint32_t wasm_module_id, lldb::addr_t addr,
   assert(packet_len + 1 < (int)sizeof(packet));
   UNUSED_IF_ASSERT_DISABLED(packet_len);
   StringExtractorGDBRemote response;
-  if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, GetInterruptTimeout()) ==
+  if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response,
+                                              GetInterruptTimeout()) ==
       GDBRemoteCommunication::PacketResult::Success) {
     if (response.IsNormalResponse()) {
       return response.GetHexBytes(llvm::MutableArrayRef<uint8_t>(
@@ -164,7 +166,7 @@ size_t ProcessWasm::WasmReadMemory(uint32_t wasm_module_id, lldb::addr_t addr,
 }
 
 size_t ProcessWasm::WasmReadData(uint32_t wasm_module_id, lldb::addr_t addr,
-                               void *buf, size_t buffer_size) {
+                                 void *buf, size_t buffer_size) {
   char packet[64];
   int packet_len =
       ::snprintf(packet, sizeof(packet), "qWasmData:%d;%" PRIx64 ";%" PRIx64,
@@ -173,7 +175,8 @@ size_t ProcessWasm::WasmReadData(uint32_t wasm_module_id, lldb::addr_t addr,
   assert(packet_len + 1 < (int)sizeof(packet));
   UNUSED_IF_ASSERT_DISABLED(packet_len);
   StringExtractorGDBRemote response;
-  if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, GetInterruptTimeout()) ==
+  if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response,
+                                              GetInterruptTimeout()) ==
       GDBRemoteCommunication::PacketResult::Success) {
     if (response.IsNormalResponse()) {
       return response.GetHexBytes(llvm::MutableArrayRef<uint8_t>(
diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.h b/lldb/source/Plugins/Process/wasm/ProcessWasm.h
index c4579390da..0d57934b9e 100644
--- a/lldb/source/Plugins/Process/wasm/ProcessWasm.h
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h
@@ -26,11 +26,7 @@ namespace wasm {
 //
 // Struct wasm_addr_t provides this encoding using bitfields
 //
-enum WasmAddressType {
-  Memory = 0x00,
-  Object = 0x01,
-  Invalid = 0x03
-};
+enum WasmAddressType { Memory = 0x00, Object = 0x01, Invalid = 0x03 };
 struct wasm_addr_t {
   uint64_t offset : 32;
   uint64_t module_id : 30;
@@ -96,11 +92,11 @@ public:
 
   /// Read from the WebAssembly Memory space.
   size_t WasmReadMemory(uint32_t wasm_module_id, lldb::addr_t addr, void *buf,
-                      size_t buffer_size);
+                        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);
+                      size_t buffer_size);
 
   /// Retrieve the current call stack from the WebAssembly remote process.
   bool GetWasmCallStack(lldb::tid_t tid,
diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp
index d9715dd23b..adcfba7400 100644
--- a/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp
+++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp
@@ -49,7 +49,8 @@ ThreadWasm::CreateRegisterContextForFrame(StackFrame *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());
+    reg_ctx_sp = std::make_shared<WasmRegisterContext>(
+        *this, concrete_frame_idx, wasm_process->GetRegisterInfo());
   } else {
     reg_ctx_sp = GetUnwinder().CreateRegisterContextForFrame(frame);
   }
diff --git a/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h
index 62bdf66c05..f60cc8e6a0 100644
--- a/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h
+++ b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h
@@ -22,7 +22,7 @@ class WasmRegisterContext;
 typedef std::shared_ptr<WasmRegisterContext> WasmRegisterContextSP;
 
 enum WasmVirtualRegisterKinds {
-  eLocal = 0, ///< wasm local
+  eLocal = 0,    ///< wasm local
   eGlobal,       ///< wasm global
   eOperandStack, ///< wasm operand stack
   kNumWasmVirtualRegisterKinds
@@ -46,7 +46,7 @@ public:
   ~WasmRegisterContext() override;
 
   uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
-                                                uint32_t num) override;
+                                               uint32_t num) override;
 
   void InvalidateAllRegisters() override;
 

jpienaar and others added 29 commits January 10, 2024 10:41
Store the last token parsed in the parser state so that the range parsed
can utilize its end rather than the start of the token after parsed.
This results in a tighter range (especially true in the case of
comments, see

```mlir
|%c4 = arith.constant 4 : index

  // Foo

  |
```

vs

```mlir
|%c4 = arith.constant 4 : index|
```

).

Discovered while working on a little textual post processing tool.
Part of https://reviews.llvm.org/D158218

GCC_INSTALL_PREFIX is a rarely-used legacy option inherited from
pre-CMake build system and has configuration file replacement nowadays.
Many `clang/test/Driver` tests specify `--gcc-toolchain=` to prevent
failures when `GCC_INSTALL_PREFIX` is specified: some contributors add
them to fix tests and some just do cargo culting. This is not healthy
for contributors adding cross compilation support for this rarely used
option.

`DEFAULT_SYSROOT` should in spirit be deprecated as well, but a relative
path doesn't have good replacement, so don't deprecate it for now.

Link:
https://discourse.llvm.org/t/add-gcc-install-dir-deprecate-gcc-toolchain-and-remove-gcc-install-prefix/65091
Link:
https://discourse.llvm.org/t/correct-cmake-parameters-for-building-clang-and-lld-for-riscv/72833

---

With `GCC_INSTALL_PREFIX=/usr`, `clang a.c` behaves like
`clang --gcc-toolchain=/usr a.c`.

Here is a simplified version of GCC installation detection code.
```
if (OPT_gcc_install_dir_EQ)
  return OPT_gcc_install_dir_EQ;

if (OPT_gcc_triple)
  candidate_gcc_triples = {OPT_gcc_triple};
else
  candidate_gcc_triples = collectCandidateTriples();
if (OPT_gcc_toolchain)
  prefixes = {OPT_gcc_toolchain};
else
  prefixes = {OPT_sysroot/usr, OPT_sysroot};
for (prefix : prefixes)
  if "$prefix/lib/gcc" exists // also tries $prefix/lib/gcc-cross
    for (triple : candidate_gcc_triples)
      if "$prefix/lib/gcc/$triple" exists
        return "$prefix/lib/gcc/$triple/$version"; // pick the largest version
```

`--gcc-toolchain=` specifies a directory where
`lib/gcc{,-cross}/$triple/$version` can be found. If you actually want
to use a specific version of GCC, specify something like
`--gcc-install-dir=/usr/lib/gcc/x86_64-linux-gnu/11` in a configuration
file. You can also specify `--gcc-triple=`.

On Debian and its derivatives where the target triple omits the vendor
part, the following ways are roughly equivalent, except that
`--gcc-install-dir=` specifies a version as well:
```
clang --gcc-toolchain=/usr a.c
clang --gcc-install-dir=/usr/lib/gcc/x86_64-linux-gnu/11 a.c
clang --gcc-triple=x86_64-linux-gnu a.c
```
…llvm#77532)

To enable the condition variable, you have to define both
UseConditionVariable and the ConditionVariableT. Otherwise, it'll be
disabled. However, you may want to disable the condition variable by
setting UseConditionVariable=false, for example, while measuring the
performance and you want to turn it off temporarily. Instead of
requiring the removal of the variable, examining its value makes more
sense.
…instructions in computeMinimumValueSizes. (llvm#72679)

After changes, that does not require support from InstCombine, we can
drop some extra requirements for values-to-be-demoted. No need to check
for external uses for roots/other instructions, just check that the
  no non-vectorized insertelement instruction, which may require
  widening.
StackSafetyAnalysis determines whether stack-allocated variables are
guaranteed to be safe from memory access bugs and enables the removal of
certain unneeded instrumentations.
(hwasan enables StackSafetyAnalysis in https://reviews.llvm.org/D108381)

Test updates:

* asan-stack-safety.ll: test the -asan-use-stack-safety=1 default
* lifetime-uar-uas.ll: switch to an indexed store to prevent
  StackSafetyAnalysis from optimizing out instrumentation for %c
* alloca_vla_interact.cpp: add a load to prevent StackSafetyAnalysis
  from optimizing out `__asan_alloca_poison` for the VLA `array`
* scariness_score_test.cpp: add -asan-use-stack-safety=0 to make a load
  of a `__asan_poison_memory_region`-poisoned local variable fail as
  intended.
* other .ll tests: add -asan-use-stack-safety=0

Reviewers: kstoimenov, eugenis, vitalybuka

Reviewed By: kstoimenov

Pull Request: llvm#77210
We need to handle this at the CastExpr level.
…nimodular cones (llvm#77235)

We implement a function that computes the generating function
corresponding to a unimodular cone.
The generating function for a polytope is obtained by summing these
generating functions over all tangent cones.
This adds new isel patterns for Zacas that take priority over the
pseudoinstructions we use for the A extension.

Support for 2x XLen types will come in a separate patch since they need
to be done differently.
This adds a new document that covers the HLSL approach to function calls
and parameter semantics. At time of writing this document is a proposal
for the implementation.
…acas. (llvm#77669)

With Zacas we will use amocas.w which doesn't require the input to be
sign extended.
…#77041)

After llvm#75679, it is no longer necessary to add the `All` pseudo
subcommand to the list of registered subcommands. The change causes the
list to contain only real subcommands, i.e. an unnamed top-level
subcommand and named ones. This simplifies the code a bit by removing
some checks for this special case.
This was missed when mass-adding support for other LTO options in
0b51e64.

Group the existing thinlto_cache_dir with these other options in a new
group, next to the other LTO options.

This skips adding the options --thinlto-emit-index-files and
--thinlto-single-module=, which don't seem to have corresponding options
on the lld-link level currently.

This should fix mstorsjo/llvm-mingw#386.
…peline (llvm#77688)

Rationale:
Since this mini-pipeline may be used in alternative pipelines (viz.
different from the default "sparsifier" pipeline) where unknown ops are
handled by alternative bufferization methods that are downstream of this
mini-pipeline, we allow unknown ops by default (failure to bufferize is
eventually apparent by failing to convert to LLVM IR).

This is part of enabling e2e testing for TORCH-MLIR tests using a
sparsifier backend
Previously, we've been mentioning tests that were placed in their own files in corresponding `drNNxx.cpp` file. This patch makes sure we do this consistently, and improves upon existing practice by specifying the name of the file test is placed in.
… cmake configuration works

These tests were slightly broken, in one case a failing test that now works. In the other case
some accidentally left over code during a name change that broke compilation due to missing
symbols.
Due to the inclusion of a header, a global type is was being shadowed,
which upset GCC.
)

This patch adds support in the llvm-exegesis tablegen emitter for
validation counters. Full support for validation counters in
llvm-exegesis will be added in a future patch.
This is the follopw implementation to
ARM-software/abi-aa#223 that supports this
relocation in llvm and lld.
This is the followup implementation to
riscv-non-isa/riscv-elf-psabi-doc#402 that
supports this relocation in llvm and lld.
While landing llvm#76652, I realized I messed up a rebase/merge at some
point and some of the changes I intended to land with llvm#76652 ended up in
a different PR (llvm#76653) instead. This patch fixes the validation
counters to how they were intended to land in llvm#76652.
The 'counted_by' attribute is used on flexible array members. The
argument for the attribute is the name of the field member holding the
count of elements in the flexible array. This information is used to
improve the results of the array bound sanitizer and the
'__builtin_dynamic_object_size' builtin. The 'count' field member must
be within the same non-anonymous, enclosing struct as the flexible array
member. For example:

```
  struct bar;
  struct foo {
    int count;
    struct inner {
      struct {
        int count; /* The 'count' referenced by 'counted_by' */
      };
      struct {
        /* ... */
        struct bar *array[] __attribute__((counted_by(count)));
      };
    } baz;
  };
```

This example specifies that the flexible array member 'array' has the
number of elements allocated for it in 'count':

```
  struct bar;
  struct foo {
    size_t count;
     /* ... */
    struct bar *array[] __attribute__((counted_by(count)));
  };
```

This establishes a relationship between 'array' and 'count';
specifically that 'p->array' must have *at least* 'p->count' number of
elements available. It's the user's responsibility to ensure that this
relationship is maintained throughout changes to the structure.

In the following, the allocated array erroneously has fewer elements
than what's specified by 'p->count'. This would result in an
out-of-bounds access not not being detected:

```
  struct foo *p;

  void foo_alloc(size_t count) {
    p = malloc(MAX(sizeof(struct foo),
                   offsetof(struct foo, array[0]) + count *
                       sizeof(struct bar *)));
    p->count = count + 42;
  }
```

The next example updates 'p->count', breaking the relationship
requirement that 'p->array' must have at least 'p->count' number of
elements available:

```
  void use_foo(int index, int val) {
    p->count += 42;
    p->array[index] = val; /* The sanitizer can't properly check this access */
  }
```

In this example, an update to 'p->count' maintains the relationship
requirement:

```
  void use_foo(int index, int val) {
    if (p->count == 0)
      return;
    --p->count;
    p->array[index] = val;
  }
```
Describe a limitation of the 'counted_by' attribute when used in unions.
Also fix a errant typo.
Add a rewriter for DIExpressions & use it to run legalization patterns
before exporting to llvm (because LLVM dialect allows DI Expressions
that may not be valid in LLVM IR).

The rewriter driver works similarly to the existing mlir rewriter
drivers, except it operates on lists of DIExpressionElemAttr (i.e.
DIExpressionAttr). Each rewrite pattern transforms a range of
DIExpressionElemAttr into a new list of DIExpressionElemAttr.

In addition, this PR sets up a place to add legalization patterns that
are broadly applicable internally to the LLVM dialect, and they will
always be applied prior to export. This PR adds one pattern for merging
fragment operators.

---------

Co-authored-by: Tobias Gysi <[email protected]>
…or RISCV and AMDGPU (llvm#77631)

Both RISC-V and AMDGPU(GCN) deploy two VirtRegRewriter in their codegen
pipeline. This test prematurely stops at the first one, which doesn't
cleanup the virtual register map and cause an assertion failure. Ideally
we can solve this by teaching `-stop-after` how to stop at the last
instance of a Pass, but we're just marking XFAIL for these two targets
for now.
sudonatalie and others added 7 commits January 12, 2024 10:03
Add Float16 to Vulkan's available capabilities, and guard Float16Buffer
(Kernel-only capability) against being added outside OpenCL
environments.

Add tests to verify half and half vector types, and validate with
spirv-val.

Fixes llvm#66398
This reverts commit 18734f6.

This caused a test suite failure on our bots:
https://lab.llvm.org/buildbot/#/builders/184/builds/9407

```
$ ~/stage1.install/bin/flang-new flush_1.f90
/usr/bin/ld: /home/david.spickett/stage1.install/lib/libFortranRuntime.a(extensions.cpp.o): in function `getlog_':
extensions.cpp:(.text.getlog_+0x1c): undefined reference to `operator new(unsigned long)'
/usr/bin/ld: extensions.cpp:(.text.getlog_+0x7c): undefined reference to `operator delete(void*)'
/usr/bin/ld: extensions.cpp:(.text.getlog_+0xc4): undefined reference to `operator delete(void*)'
/usr/bin/ld: extensions.cpp:(.text.getlog_+0xe8): undefined reference to `operator delete(void*)'
flang-new: error: linker command failed with exit code 1 (use -v to see invocation)
```
This patch sorts the tests which check if SLEEF and ArmPL mappings are
used, in the order of the math functions base names.
the second vector.

Need to transform all elements in the long mask, if we decided to
produce shorter version, some elements may still have incorrect inifices
after transformation for the first vector in the permutation.
… not cast compatiable (llvm#71310)

The simplify of bufferization.clone generates a memref.cast op, but the
checks in simplify do not verify whether the operand types and return
types of clone op is compatiable, leading to errors. This patch
addresses this issue.
Fix a crash of `replace-with-veclib` pass, when the arguments of the TLI
mapping do not match the original call.
Now, it simply ignores such cases.

Test require assertions as it accesses programmatically the debug log.
@paolosevMSFT paolosevMSFT marked this pull request as ready for review January 12, 2024 15:26
@llvmbot llvmbot added the lldb label Jan 12, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Jan 12, 2024

@llvm/pr-subscribers-lldb

Author: Paolo Severini (paolosevMSFT)

Changes

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" and the WAMR team greatly contributed to this work.

A WebAssembly debugging session can be started using the new command:
wasm [&lt;hostname&gt;:]&lt;portnum&gt;

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 --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]

Copy link
Member

@JDevlieghere JDevlieghere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking this up again. I was going to ask about testing and I see that Adrian asked a similar question on Phabricator in the original review. My recommendation would be to use GdbRemoteTestCaseBase style tests for this, as they'll run on all the existing bots. If you want to integration test the stub this is actually going to talk to it would be great to have that running in CI too, but that's not a requirement to get this merged.

The support for DW_OP_WASM_location seems like it can be hoisted out in a separate patch with its own dedicated test.

uint32_t index;

/* LLDB doesn't have an address space to represents WebAssembly Locals,
* GLobals and operand stacks.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/GLobals/globals/

uint8_t wasm_op = opcodes.GetU8(&offset);
uint32_t index;

/* LLDB doesn't have an address space to represents WebAssembly Locals,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/Locals/locals/

Comment on lines +1 to +3
# This file comes from https://reviews.llvm.org/D78978.
# Author: [@paolosev](https://reviews.llvm.org/p/paolosev/).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this

Comment on lines +1 to +3
// This file comes from https://reviews.llvm.org/D78978.
// Author: [@paolosev](https://reviews.llvm.org/p/paolosev/).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this here and everywhere else.

Comment on lines +52 to +55
ConstString ProcessWasm::GetPluginNameStatic() {
static ConstString g_name("wasm");
return g_name;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetPluginNameStatic should return a llvm::StringRef. Same for GetPluginDescriptionStatic below.

case WasmAddressType::Memory:
return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error);
case WasmAddressType::Object:
// TODO ?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

? :-)

assert(packet_len + 1 < (int)sizeof(packet));
UNUSED_IF_ASSERT_DISABLED(packet_len);
StringExtractorGDBRemote response;
if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, GetInterruptTimeout()) ==
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge conflict marker?

Comment on lines +21 to +28
// 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
//
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be Doxygen style comments (///).

Memory = 0x00,
Object = 0x01,
Invalid = 0x03
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newline

};

/// ProcessWasm provides the access to the Wasm program state
/// retrieved from the Wasm engine.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spurious space

@paolosevMSFT paolosevMSFT requested review from Endilll, nikic and a team as code owners January 12, 2024 16:14
@Endilll Endilll removed their request for review January 12, 2024 16:40
@ldionne ldionne removed request for a team January 12, 2024 22:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.