paolosev created this revision. paolosev added reviewers: labath, clayborg. paolosev added a project: LLDB. Herald added subscribers: lldb-commits, sunfish, aheejin, jgravelle-google, sbc100, aprantl, mgorny, dschuff. paolosev edited the summary of this revision.
This patch is a refactoring of https://reviews.llvm.org/D78801. As suggested, I have created a separate patch to more easily compare the implementations. Even here, the goal 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. Here I am removing the interface IWasmProcess and the new class WasmProcessGDBRemote and moving most of the Wasm logic in `ProcessGDBRemote`. I am still adding the new Unwind class `UnwindWasm`, which now however is in core and not in plugins code. Having a Unwind-derived class for Wasm unwinding seems to be the cleanest solution. The advantage with this new design is that the required changes are smaller and isolated in a few places. The drawback is that three 'core' classes (UnwindWasm, Value and DWARFExpression) have now a dependency on ProcessGDBRemote and/or ThreadGDBRemote. This could be avoided re-introducing a core interface like IWasmProcess. We could add a couple of virtual method `bool IsWasmProcess()` and `IWasmProcess* AsWasmProcess()` to Process, which would be overridden by ProcessGDBRemote. Then these classes could just query this interface, removing direct dependencies on GDBRemote. I am also adding a new `qSupported` flag named "wasm" that should tell LLDB whether the remote supports Wasm debugging. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D78978 Files: lldb/include/lldb/Target/UnwindWasm.h lldb/source/Core/Value.cpp lldb/source/Expression/DWARFExpression.cpp lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h lldb/source/Target/CMakeLists.txt lldb/source/Target/Thread.cpp lldb/source/Target/UnwindWasm.cpp
Index: lldb/source/Target/UnwindWasm.cpp =================================================================== --- /dev/null +++ lldb/source/Target/UnwindWasm.cpp @@ -0,0 +1,75 @@ +//===-- 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 "lldb/Target/UnwindWasm.h" +#include "lldb/Target/Thread.h" + +#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" +#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h" + +using namespace lldb; +using namespace lldb_private; +using namespace process_gdb_remote; + +class WasmGDBRemoteRegisterContext : public GDBRemoteRegisterContext { +public: + WasmGDBRemoteRegisterContext(ThreadGDBRemote &thread, + uint32_t concrete_frame_idx, + GDBRemoteDynamicRegisterInfo ®_info, + uint64_t pc) + : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info, false, + false) { + PrivateSetRegisterValue(0, pc); + } +}; + +lldb::RegisterContextSP +UnwindWasm::DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) { + if (m_frames.size() <= frame->GetFrameIndex()) { + return lldb::RegisterContextSP(); + } + + ThreadSP thread = frame->GetThread(); + ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *>(thread.get()); + ProcessGDBRemote *gdb_process = + static_cast<ProcessGDBRemote *>(thread->GetProcess().get()); + std::shared_ptr<GDBRemoteRegisterContext> reg_ctx_sp = + std::make_shared<WasmGDBRemoteRegisterContext>( + *gdb_thread, frame->GetConcreteFrameIndex(), + gdb_process->m_register_info, m_frames[frame->GetFrameIndex()]); + return reg_ctx_sp; +} + +uint32_t UnwindWasm::DoGetFrameCount() { + if (!m_unwind_complete) { + m_unwind_complete = true; + m_frames.clear(); + + ProcessGDBRemote *gdb_process = + static_cast<ProcessGDBRemote *>(GetThread().GetProcess().get()); + if (!gdb_process->GetWasmCallStack(m_frames)) + return 0; + } + 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/Target/Thread.cpp =================================================================== --- lldb/source/Target/Thread.cpp +++ lldb/source/Target/Thread.cpp @@ -43,6 +43,7 @@ #include "lldb/Target/ThreadPlanStepUntil.h" #include "lldb/Target/ThreadSpec.h" #include "lldb/Target/UnwindLLDB.h" +#include "lldb/Target/UnwindWasm.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/State.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 UnwindWasm(*this)); + else + m_unwinder_up.reset(new UnwindLLDB(*this)); + } return *m_unwinder_up; } Index: lldb/source/Target/CMakeLists.txt =================================================================== --- lldb/source/Target/CMakeLists.txt +++ lldb/source/Target/CMakeLists.txt @@ -68,6 +68,7 @@ UnixSignals.cpp UnwindAssembly.cpp UnwindLLDB.cpp + UnwindWasm.cpp LINK_LIBS lldbBreakpoint Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -231,7 +231,26 @@ std::string HarmonizeThreadIdsForProfileData( StringExtractorGDBRemote &inputStringExtractor); + // Query remote GDBServer for Wasm information + + bool IsWasmProcess() const; + + bool GetWasmLocal(int frame_index, int index, void *buf, size_t buffer_size, + size_t &size); + + bool GetWasmGlobal(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 addr, void *buf, + size_t buffer_size); + + bool GetWasmCallStack(std::vector<lldb::addr_t> &call_stack_pcs); + protected: + friend class UnwindWasm; friend class ThreadGDBRemote; friend class GDBRemoteCommunicationClient; friend class GDBRemoteRegisterContext; Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -2689,9 +2689,61 @@ } } +// WebAssembly features + +bool ProcessGDBRemote::IsWasmProcess() const { + return GetTarget().GetArchitecture().GetMachine() == llvm::Triple::wasm32; +} + +bool ProcessGDBRemote::GetWasmLocal(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size) { + if (!m_gdb_comm.GetWasmFeaturesSupported()) + return false; + return GetGDBRemote().GetWasmLocal(frame_index, index, buf, buffer_size, + size); +} + +bool ProcessGDBRemote::GetWasmGlobal(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size) { + if (!m_gdb_comm.GetWasmFeaturesSupported()) + return false; + return GetGDBRemote().GetWasmGlobal(frame_index, index, buf, buffer_size, + size); +} + +bool ProcessGDBRemote::GetWasmStackValue(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size) { + if (!m_gdb_comm.GetWasmFeaturesSupported()) + return false; + return GetGDBRemote().GetWasmStackValue(frame_index, index, buf, buffer_size, + size); +} + +bool ProcessGDBRemote::WasmReadMemory(int frame_index, lldb::addr_t addr, + void *buf, size_t buffer_size) { + if (!m_gdb_comm.GetWasmFeaturesSupported()) + return false; + return GetGDBRemote().WasmReadMemory(frame_index, addr, buf, buffer_size); +} + +bool ProcessGDBRemote::GetWasmCallStack( + std::vector<lldb::addr_t> &call_stack_pcs) { + if (!m_gdb_comm.GetWasmFeaturesSupported()) + return false; + return GetGDBRemote().GetWasmCallStack(call_stack_pcs); +} + // Process Memory size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size, Status &error) { + if (addr < 0x100000000 && IsWasmProcess() && + m_gdb_comm.GetWasmFeaturesSupported()) { + if (WasmReadMemory(0 /*frame_index*/, addr, buf, size)) { + return size; + } + return 0; + } + GetMaxMemorySize(); bool binary_memory_read = m_gdb_comm.GetxPacketSupported(); // M and m packets take 2 bytes for 1 byte of memory 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: @@ -357,6 +368,8 @@ bool GetQXferMemoryMapReadSupported(); + bool GetWasmFeaturesSupported(); + LazyBool SupportsAllocDeallocMemory() // const { // Uncomment this to have lldb pretend the debug server doesn't respond to @@ -552,6 +565,7 @@ LazyBool m_supports_jGetSharedCacheInfo; LazyBool m_supports_QPassSignals; LazyBool m_supports_error_string_reply; + LazyBool m_supports_wasm; bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1, m_supports_qUserName : 1, m_supports_qGroupName : 1, 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 @@ -199,6 +199,13 @@ return m_supports_qXfer_memory_map_read == eLazyBoolYes; } +bool GDBRemoteCommunicationClient::GetWasmFeaturesSupported() { + if (m_supports_wasm == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_wasm == eLazyBoolYes; +} + uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() { if (m_max_packet_size == 0) { GetRemoteQSupported(); @@ -342,6 +349,7 @@ m_supports_augmented_libraries_svr4_read = eLazyBoolNo; m_supports_qXfer_features_read = eLazyBoolNo; m_supports_qXfer_memory_map_read = eLazyBoolNo; + m_supports_wasm = eLazyBoolNo; m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if // not, we assume no limit @@ -433,6 +441,11 @@ else m_supports_QPassSignals = eLazyBoolNo; + if (::strstr(response_cstr, "wasm+")) + m_supports_wasm = eLazyBoolYes; + else + m_supports_wasm = eLazyBoolNo; + const char *packet_size_str = ::strstr(response_cstr, "PacketSize="); if (packet_size_str) { StringExtractorGDBRemote packet_response(packet_size_str + @@ -2694,6 +2707,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/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/gdb-remote/ProcessGDBRemote.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) { + process_gdb_remote::ProcessGDBRemote *wasm_process = + static_cast<process_gdb_remote::ProcessGDBRemote *>( + 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/gdb-remote/ProcessGDBRemote.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(); + process_gdb_remote::ProcessGDBRemote *wasm_process = + static_cast<process_gdb_remote::ProcessGDBRemote *>( + 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)", Index: lldb/include/lldb/Target/UnwindWasm.h =================================================================== --- /dev/null +++ lldb/include/lldb/Target/UnwindWasm.h @@ -0,0 +1,47 @@ +//===-- 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_TARGET_UNWINDWASM_H +#define LLDB_TARGET_UNWINDWASM_H + +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Unwind.h" +#include <vector> + +namespace lldb_private { + +class UnwindWasm : public 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 lldb_private + +#endif // LLDB_TARGET_UNWINDWASM_H
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits