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