https://github.com/paolosevMSFT updated https://github.com/llvm/llvm-project/pull/77949
>From 30d932bb0988e1c78c3e023be2270259df5e6664 Mon Sep 17 00:00:00 2001 From: Paolo Severini <paolo...@microsoft.com> Date: Fri, 12 Jan 2024 09:10:59 -0800 Subject: [PATCH 1/6] Add support for source-level debugging of WebAssembly code --- lldb/include/lldb/Target/Process.h | 40 +++ lldb/source/Core/Value.cpp | 2 +- lldb/source/Expression/DWARFExpression.cpp | 41 +++ .../source/Interpreter/CommandInterpreter.cpp | 18 ++ lldb/source/Plugins/Process/CMakeLists.txt | 1 + .../Process/gdb-remote/ProcessGDBRemote.cpp | 7 +- .../Process/gdb-remote/ProcessGDBRemote.h | 2 + .../Plugins/Process/wasm/CMakeLists.txt | 12 + .../Plugins/Process/wasm/ProcessWasm.cpp | 291 ++++++++++++++++++ .../source/Plugins/Process/wasm/ProcessWasm.h | 129 ++++++++ .../Plugins/Process/wasm/ThreadWasm.cpp | 55 ++++ lldb/source/Plugins/Process/wasm/ThreadWasm.h | 44 +++ .../Plugins/Process/wasm/UnwindWasm.cpp | 76 +++++ lldb/source/Plugins/Process/wasm/UnwindWasm.h | 55 ++++ .../Process/wasm/wasmRegisterContext.cpp | 108 +++++++ .../Process/wasm/wasmRegisterContext.h | 75 +++++ 16 files changed, 954 insertions(+), 2 deletions(-) create mode 100644 lldb/source/Plugins/Process/wasm/CMakeLists.txt create mode 100644 lldb/source/Plugins/Process/wasm/ProcessWasm.cpp create mode 100644 lldb/source/Plugins/Process/wasm/ProcessWasm.h create mode 100644 lldb/source/Plugins/Process/wasm/ThreadWasm.cpp create mode 100644 lldb/source/Plugins/Process/wasm/ThreadWasm.h create mode 100644 lldb/source/Plugins/Process/wasm/UnwindWasm.cpp create mode 100644 lldb/source/Plugins/Process/wasm/UnwindWasm.h create mode 100644 lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp create mode 100644 lldb/source/Plugins/Process/wasm/wasmRegisterContext.h 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..ca24611724dc7c 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -346,6 +346,16 @@ 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 +2605,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/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..ef2bfc634962d5 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/CMakeLists.txt @@ -0,0 +1,12 @@ +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..6e633e69b9767a --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp @@ -0,0 +1,291 @@ +//===-- 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); +} + +llvm::StringRef ProcessWasm::GetPluginName() { return GetPluginNameStatic(); } + +llvm::StringRef ProcessWasm::GetPluginNameStatic() { + static ConstString g_name("wasm"); + return g_name; +} + +llvm::StringRef 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: + case WasmAddressType::Object: + 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: + 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..fc9736ec0684d2 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h @@ -0,0 +1,129 @@ +//===-- 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 { + +/// 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 +/// +enum WasmAddressType { Memory = 0x00, Object = 0x01, Invalid = 0x03 }; + +struct wasm_addr_t { + uint64_t offset : 32; + uint64_t module_id : 30; + uint64_t type : 2; + + wasm_addr_t(lldb::addr_t addr) + : type(addr >> 62), module_id((addr & 0x00ffffff00000000) >> 32), + offset(addr & 0x00000000ffffffff) {} + + wasm_addr_t(WasmAddressType type_, uint32_t module_id_, uint32_t offset_) + : type(type_), module_id(module_id_), offset(offset_) {} + + WasmAddressType GetType() { return static_cast<WasmAddressType>(type); } + operator lldb::addr_t() { return *(uint64_t *)this; } +}; + +/// ProcessWasm provides the access to the Wasm program state +/// retrieved from the Wasm engine. +class ProcessWasm : public process_gdb_remote::ProcessGDBRemote { +public: + ProcessWasm(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); + ~ProcessWasm() override = default; + + static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file_path, + bool can_connect); + + static void Initialize(); + static void DebuggerInitialize(Debugger &debugger); + static void Terminate(); + static llvm::StringRef GetPluginNameStatic(); + static llvm::StringRef GetPluginDescriptionStatic(); + + /// PluginInterface protocol. + /// \{ + llvm::StringRef GetPluginName() override; + /// \} + + /// Process protocol. + /// \{ + size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, + Status &error) override; + + size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, + ExecutionContext *exe_ctx, Status &error) override; + /// \} + + /// Query the value of a WebAssembly local variable from the WebAssembly + /// remote process. + bool GetWasmLocal(int frame_index, int index, void *buf, size_t buffer_size, + size_t &size); + + /// Query the value of a WebAssembly global variable from the WebAssembly + /// remote process. + bool GetWasmGlobal(int frame_index, int index, void *buf, size_t buffer_size, + size_t &size); + + /// Query the value of an item in the WebAssembly operand stack from the + /// WebAssembly remote process. + bool GetWasmStackValue(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size); + + /// Read from the WebAssembly Memory space. + size_t WasmReadMemory(uint32_t wasm_module_id, lldb::addr_t addr, void *buf, + 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); + + /// Retrieve the current call stack from the WebAssembly remote process. + bool GetWasmCallStack(lldb::tid_t tid, + std::vector<lldb::addr_t> &call_stack_pcs); + + // Check if a given Process + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + +protected: + /// ProcessGDBRemote protocol. + /// \{ + std::shared_ptr<process_gdb_remote::ThreadGDBRemote> + CreateThread(lldb::tid_t tid); + /// \} + +private: + friend class UnwindWasm; + friend class ThreadWasm; + + process_gdb_remote::GDBRemoteDynamicRegisterInfoSP &GetRegisterInfo() { + return m_register_info_sp; + } + + ProcessWasm(const ProcessWasm &); + const ProcessWasm &operator=(const ProcessWasm &) = delete; +}; + +} // namespace wasm +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp new file mode 100644 index 00000000000000..9d3ee48912e405 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp @@ -0,0 +1,55 @@ +//===-- ThreadWasm.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 "ThreadWasm.h" + +#include "ProcessWasm.h" +#include "UnwindWasm.h" +#include "lldb/Target/Target.h" +#include "wasmRegisterContext.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::wasm; + +Unwind &ThreadWasm::GetUnwinder() { + if (!m_unwinder_up) { + assert(CalculateTarget()->GetArchitecture().GetMachine() == + llvm::Triple::wasm32); + m_unwinder_up.reset(new wasm::UnwindWasm(*this)); + } + return *m_unwinder_up; +} + +bool ThreadWasm::GetWasmCallStack(std::vector<lldb::addr_t> &call_stack_pcs) { + ProcessSP process_sp(GetProcess()); + if (process_sp) { + ProcessWasm *wasm_process = static_cast<ProcessWasm *>(process_sp.get()); + return wasm_process->GetWasmCallStack(GetID(), call_stack_pcs); + } + return false; +} + +lldb::RegisterContextSP +ThreadWasm::CreateRegisterContextForFrame(StackFrame *frame) { + lldb::RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + ProcessSP process_sp(GetProcess()); + ProcessWasm *wasm_process = static_cast<ProcessWasm *>(process_sp.get()); + + if (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()); + } else { + reg_ctx_sp = GetUnwinder().CreateRegisterContextForFrame(frame); + } + return reg_ctx_sp; +} diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.h b/lldb/source/Plugins/Process/wasm/ThreadWasm.h new file mode 100644 index 00000000000000..c1aa2cd8b98825 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.h @@ -0,0 +1,44 @@ +//===-- ThreadWasm.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_THREADWASM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H + +#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h" + +namespace lldb_private { +namespace wasm { + +/// ProcessWasm provides the access to the Wasm program state +/// retrieved from the Wasm engine. +class ThreadWasm : public process_gdb_remote::ThreadGDBRemote { +public: + ThreadWasm(Process &process, lldb::tid_t tid) + : process_gdb_remote::ThreadGDBRemote(process, tid) {} + ~ThreadWasm() override = default; + + /// Retrieve the current call stack from the WebAssembly remote process. + bool GetWasmCallStack(std::vector<lldb::addr_t> &call_stack_pcs); + + lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame) override; + +protected: + /// Thread protocol. + /// \{ + Unwind &GetUnwinder() override; + /// \} + + ThreadWasm(const ThreadWasm &); + const ThreadWasm &operator=(const ThreadWasm &) = delete; +}; + +} // namespace wasm +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp new file mode 100644 index 00000000000000..9e8f6a9ce7d9ff --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp @@ -0,0 +1,76 @@ +//===-- 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/gdb-remote/ThreadGDBRemote.h" +#include "Plugins/Process/wasm/ProcessWasm.h" +#include "Plugins/Process/wasm/ThreadWasm.h" +#include "lldb/lldb-forward.h" +#include "wasmRegisterContext.h" + +using namespace lldb; +using namespace lldb_private; +using namespace process_gdb_remote; +using namespace wasm; + +class WasmGDBRemoteRegisterContext : public GDBRemoteRegisterContext { +public: + WasmGDBRemoteRegisterContext(ThreadGDBRemote &thread, + uint32_t concrete_frame_idx, + GDBRemoteDynamicRegisterInfoSP ®_info_sp, + uint64_t pc) + : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info_sp, 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(); + ProcessSP process_sp = thread->GetProcess(); + ThreadWasm *wasm_thread = static_cast<ThreadWasm *>(thread.get()); + ProcessWasm *wasm_process = static_cast<ProcessWasm *>(process_sp.get()); + std::shared_ptr<WasmRegisterContext> reg_ctx_sp = + std::make_shared<WasmRegisterContext>(*wasm_thread, + frame->GetConcreteFrameIndex(), + wasm_process->GetRegisterInfo()); + return reg_ctx_sp; +} + +uint32_t UnwindWasm::DoGetFrameCount() { + if (!m_unwind_complete) { + m_unwind_complete = true; + m_frames.clear(); + + ThreadWasm &wasm_thread = static_cast<ThreadWasm &>(GetThread()); + if (!wasm_thread.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 diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.h b/lldb/source/Plugins/Process/wasm/UnwindWasm.h new file mode 100644 index 00000000000000..9bd1dac9a98ae3 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.h @@ -0,0 +1,55 @@ +//===-- 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 { + +/// UnwindWasm manages stack unwinding for a WebAssembly process. +class UnwindWasm : public lldb_private::Unwind { +public: + UnwindWasm(lldb_private::Thread &thread) + : Unwind(thread), m_frames(), m_unwind_complete(false) {} + ~UnwindWasm() override = default; + +protected: + /// Unwind protocol. + /// \{ + 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; + + UnwindWasm(const UnwindWasm &); + const UnwindWasm &operator=(const UnwindWasm &) = delete; +}; + +} // namespace wasm +} // namespace lldb_private + +#endif // lldb_UnwindWasm_h_ diff --git a/lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp new file mode 100644 index 00000000000000..470a0fb6a43cb3 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp @@ -0,0 +1,108 @@ +//===---- wasmRegisterContext.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 "wasmRegisterContext.h" +#include "Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h" +#include "ProcessWasm.h" +#include "ThreadWasm.h" +#include "lldb/Utility/RegisterValue.h" +#include <memory> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private::wasm; + +WasmRegisterContext::WasmRegisterContext( + wasm::ThreadWasm &thread, uint32_t concrete_frame_idx, + GDBRemoteDynamicRegisterInfoSP reg_info_sp) + : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info_sp, false, + false) {} + +WasmRegisterContext::~WasmRegisterContext() = default; + +uint32_t WasmRegisterContext::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t num) { + return num; +} + +size_t WasmRegisterContext::GetRegisterCount() { return 0; } + +const RegisterInfo *WasmRegisterContext::GetRegisterInfoAtIndex(size_t reg) { + uint32_t tag = (reg >> 30) & 0x03; + if (tag == 0) { + return m_reg_info_sp->GetRegisterInfoAtIndex(reg); + } + + auto it = m_register_map.find(reg); + if (it == m_register_map.end()) { + WasmVirtualRegisterKinds kind = + static_cast<WasmVirtualRegisterKinds>(tag - 1); + std::tie(it, std::ignore) = m_register_map.insert( + {reg, + std::make_unique<WasmVirtualRegisterInfo>(kind, reg & 0x3fffffff)}); + } + return it->second.get(); +} + +size_t WasmRegisterContext::GetRegisterSetCount() { return 0; } + +const RegisterSet *WasmRegisterContext::GetRegisterSet(size_t reg_set) { + return nullptr; +} + +bool WasmRegisterContext::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) { + if (reg_info->name) { + return GDBRemoteRegisterContext::ReadRegister(reg_info, value); + } + + ThreadWasm *thread = static_cast<ThreadWasm *>(&GetThread()); + ProcessWasm *process = static_cast<ProcessWasm *>(thread->GetProcess().get()); + if (!thread) + return false; + + uint32_t frame_index = m_concrete_frame_idx; + WasmVirtualRegisterInfo *wasm_reg_info = + static_cast<WasmVirtualRegisterInfo *>( + const_cast<RegisterInfo *>(reg_info)); + uint8_t buf[16]; + size_t size = 0; + switch (wasm_reg_info->kind) { + case eLocal: + process->GetWasmLocal(frame_index, wasm_reg_info->index, buf, sizeof(buf), + size); + break; + case eGlobal: + process->GetWasmGlobal(frame_index, wasm_reg_info->index, buf, sizeof(buf), + size); + break; + case eOperandStack: + process->GetWasmStackValue(frame_index, wasm_reg_info->index, buf, + sizeof(buf), size); + break; + default: + return false; + } + + DataExtractor reg_data(buf, size, process->GetByteOrder(), + process->GetAddressByteSize()); + const bool partial_data_ok = false; + wasm_reg_info->byte_size = size; + wasm_reg_info->encoding = lldb::eEncodingUint; + Status error(value.SetValueFromData(*reg_info, reg_data, + reg_info->byte_offset, partial_data_ok)); + return error.Success(); +} + +void WasmRegisterContext::InvalidateAllRegisters() {} + +bool WasmRegisterContext::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) { + return false; +} diff --git a/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h new file mode 100644 index 00000000000000..f60cc8e6a099ab --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h @@ -0,0 +1,75 @@ +//===----- wasmRegisterContext.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_WASMREGISTERCONTEXT_H +#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_WASMREGISTERCONTEXT_H + +#include "Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h" +#include "ThreadWasm.h" +#include "lldb/lldb-private-types.h" +#include <unordered_map> + +namespace lldb_private { +namespace wasm { + +class WasmRegisterContext; + +typedef std::shared_ptr<WasmRegisterContext> WasmRegisterContextSP; + +enum WasmVirtualRegisterKinds { + eLocal = 0, ///< wasm local + eGlobal, ///< wasm global + eOperandStack, ///< wasm operand stack + kNumWasmVirtualRegisterKinds +}; + +struct WasmVirtualRegisterInfo : public RegisterInfo { + WasmVirtualRegisterKinds kind; + uint32_t index; + + WasmVirtualRegisterInfo(WasmVirtualRegisterKinds kind, uint32_t index) + : RegisterInfo(), kind(kind), index(index) {} +}; + +class WasmRegisterContext + : public process_gdb_remote::GDBRemoteRegisterContext { +public: + WasmRegisterContext( + wasm::ThreadWasm &thread, uint32_t concrete_frame_idx, + process_gdb_remote::GDBRemoteDynamicRegisterInfoSP reg_info_sp); + + ~WasmRegisterContext() override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const RegisterSet *GetRegisterSet(size_t reg_set) override; + + bool ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) override; + + bool WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) override; + +private: + std::unordered_map<size_t, std::unique_ptr<WasmVirtualRegisterInfo>> + m_register_map; +}; + +} // namespace wasm +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_WASMREGISTERCONTEXT_H >From cbd046618749f229fce7221e012bf51f4789301b Mon Sep 17 00:00:00 2001 From: Paolo Severini <paolo...@microsoft.com> Date: Mon, 22 Jan 2024 05:17:23 -0800 Subject: [PATCH 2/6] Add unit tests --- lldb/include/lldb/Target/Process.h | 54 +--- lldb/source/Core/Value.cpp | 5 +- lldb/source/Expression/DWARFExpression.cpp | 2 +- lldb/source/Expression/Materializer.cpp | 4 +- .../Plugins/Process/wasm/ProcessWasm.cpp | 62 ++--- .../source/Plugins/Process/wasm/ProcessWasm.h | 13 +- .../gdb_remote_client/TestWasm.py | 239 ++++++++++++++---- 7 files changed, 242 insertions(+), 137 deletions(-) diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 587ae085b479b7..b7779985db4fc3 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -1449,6 +1449,14 @@ class Process : public std::enable_shared_from_this<Process>, /// platforms where there is a difference (only Arm Thumb at this time). lldb::addr_t FixAnyAddress(lldb::addr_t pc); + /// Some targets might use bits in a code address to represent additional + /// information; for example WebAssembly targets have a different memory space + /// per module and have a different address space per memory and code. + virtual lldb::addr_t FixMemoryAddress(lldb::addr_t address, + StackFrame *stack_frame) const { + return address; + } + /// Get the Modification ID of the process. /// /// \return @@ -1548,46 +1556,6 @@ 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 @@ -1965,9 +1933,9 @@ class Process : public std::enable_shared_from_this<Process>, /// the instruction has completed executing. bool GetWatchpointReportedAfter(); - lldb::ModuleSP ReadModuleFromMemory(const FileSpec &file_spec, - lldb::addr_t header_addr, - size_t size_to_read = 512); + virtual lldb::ModuleSP ReadModuleFromMemory(const FileSpec &file_spec, + lldb::addr_t header_addr, + size_t size_to_read = 512); /// Attempt to get the attributes for a region of memory in the process. /// diff --git a/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp index 47a5fdee773886..7766d6543edb0c 100644 --- a/lldb/source/Core/Value.cpp +++ b/lldb/source/Core/Value.cpp @@ -551,8 +551,9 @@ Status Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data, Process *process = exe_ctx->GetProcessPtr(); if (process) { - const size_t bytes_read = - process->ReadMemory(address, dst, byte_size, exe_ctx, error); + const size_t bytes_read = process->ReadMemory( + process->FixMemoryAddress(address, exe_ctx->GetFramePtr()), dst, + byte_size, 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 ca24611724dc7c..95033db5ed8f5a 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -2621,7 +2621,7 @@ bool DWARFExpression::Evaluate( */ if (wasm_op == 3) { index = opcodes.GetU32(&offset); - wasm_op = 1; + wasm_op = 2; // Global } else { index = opcodes.GetULEB128(&offset); } diff --git a/lldb/source/Expression/Materializer.cpp b/lldb/source/Expression/Materializer.cpp index 6e344dfd57c4b5..8c4cd4dfc2db5f 100644 --- a/lldb/source/Expression/Materializer.cpp +++ b/lldb/source/Expression/Materializer.cpp @@ -1077,7 +1077,9 @@ class EntityResultVariable : public Materializer::Entity { const size_t pvar_byte_size = ret->GetByteSize().value_or(0); uint8_t *pvar_data = ret->GetValueBytes(); - map.ReadMemory(pvar_data, address, pvar_byte_size, read_error); + map.ReadMemory(pvar_data, + process_sp->FixMemoryAddress(address, frame_sp.get()), + pvar_byte_size, read_error); if (!read_error.Success()) { err.SetErrorString( diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp index 6e633e69b9767a..19b8babeb59351 100644 --- a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp +++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp @@ -94,8 +94,19 @@ size_t ProcessWasm::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, switch (wasm_addr.GetType()) { case WasmAddressType::Memory: - case WasmAddressType::Object: - return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error); + if (wasm_addr.module_id != 0) { + 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; + } else { + return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error); + } + case WasmAddressType::Code: + wasm_addr.type = 0; + return ProcessGDBRemote::ReadMemory(wasm_addr, buf, size, error); case WasmAddressType::Invalid: default: error.SetErrorStringWithFormat( @@ -104,36 +115,27 @@ size_t ProcessWasm::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, } } -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); +lldb::ModuleSP ProcessWasm::ReadModuleFromMemory(const FileSpec &file_spec, + lldb::addr_t header_addr, + size_t size_to_read) { + wasm_addr_t wasm_addr(header_addr); + wasm_addr.type = WasmAddressType::Code; + return Process::ReadModuleFromMemory(file_spec, wasm_addr, size_to_read); +} - 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: - 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; +lldb::addr_t ProcessWasm::FixMemoryAddress(lldb::addr_t address, + StackFrame *stack_frame) const { + if (stack_frame) { + assert(stack_frame->CalculateTarget()->GetArchitecture().GetMachine() == + llvm::Triple::wasm32); + // Extract Wasm module ID from the program counter. + wasm_addr_t wasm_addr(address); + wasm_addr.module_id = + wasm_addr_t(stack_frame->GetStackID().GetPC()).module_id; + wasm_addr.type = WasmAddressType::Memory; + return wasm_addr; } + return address; } size_t ProcessWasm::WasmReadMemory(uint32_t wasm_module_id, lldb::addr_t addr, diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.h b/lldb/source/Plugins/Process/wasm/ProcessWasm.h index fc9736ec0684d2..b1051b0840c2b2 100644 --- a/lldb/source/Plugins/Process/wasm/ProcessWasm.h +++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h @@ -23,7 +23,7 @@ namespace wasm { /// /// Struct wasm_addr_t provides this encoding using bitfields /// -enum WasmAddressType { Memory = 0x00, Object = 0x01, Invalid = 0x03 }; +enum WasmAddressType { Memory = 0x00, Code = 0x01, Invalid = 0x03 }; struct wasm_addr_t { uint64_t offset : 32; @@ -64,14 +64,15 @@ class ProcessWasm : public process_gdb_remote::ProcessGDBRemote { llvm::StringRef GetPluginName() override; /// \} - /// Process protocol. - /// \{ size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error) override; - size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, - ExecutionContext *exe_ctx, Status &error) override; - /// \} + lldb::ModuleSP ReadModuleFromMemory(const FileSpec &file_spec, + lldb::addr_t header_addr, + size_t size_to_read = 512) override; + + lldb::addr_t FixMemoryAddress(lldb::addr_t address, + StackFrame *stack_frame) const override; /// Query the value of a WebAssembly local variable from the WebAssembly /// remote process. diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py index 61f315bb65ca53..f2ac2abdeef0c0 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py @@ -6,8 +6,8 @@ from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase LLDB_INVALID_ADDRESS = lldb.LLDB_INVALID_ADDRESS -load_address = 0x400000000 - +MODULE_ID = 4 +LOAD_ADDRESS = MODULE_ID << 32 def format_register_value(val): """ @@ -23,18 +23,33 @@ def format_register_value(val): shift += 8 return result +def make_code_address(module_id, offset): + return 0x4000000000000000 | (module_id << 32) | offset class MyResponder(MockGDBServerResponder): - current_pc = load_address + 0x0A + current_pc = LOAD_ADDRESS + 0x0A - def __init__(self, obj_path, module_name=""): + def __init__(self, obj_path, module_name, wasm_call_stacks=[], memory_view=[]): self._obj_path = obj_path self._module_name = module_name or obj_path + self._bp_address = 0 + self._wasm_call_stacks = wasm_call_stacks + self._call_stack_request_count = 0 + self._memory_view = memory_view MockGDBServerResponder.__init__(self) + def SetCurrentPC(self, address): + self.current_pc = LOAD_ADDRESS + address + def respond(self, packet): if packet[0:13] == "qRegisterInfo": return self.qRegisterInfo(packet[13:]) + if packet.startswith("qWasmCallStack"): + return self.qWasmCallStack(); + if packet.startswith("qWasmLocal"): + return self.qWasmLocal(packet); + if packet.startswith("qWasmMem"): + return self.qWasmMem(packet); return MockGDBServerResponder.respond(self, packet) def qSupported(self, client_supported): @@ -50,7 +65,7 @@ def qfThreadInfo(self): return "OK" def qRegisterInfo(self, index): - if index == 0: + if index == "0": return "name:pc;alt-name:pc;bitsize:64;offset:0;encoding:uint;format:hex;set:General Purpose Registers;gcc:16;dwarf:16;generic:pc;" return "E45" @@ -61,36 +76,131 @@ def qProcessInfo(self): ) def haltReason(self): - return "T05thread:1;" + return "T05thread-pcs:300000083;thread:1;library:;" def readRegister(self, register): + assert(register == 0) return format_register_value(self.current_pc) def qXferRead(self, obj, annex, offset, length): if obj == "libraries": xml = ( '<library-list><library name="%s"><section address="%d"/></library></library-list>' - % (self._module_name, load_address) + % (self._module_name, make_code_address(MODULE_ID, 0)) ) return xml, False else: return None, False def readMemory(self, addr, length): - if addr < load_address: - return "E02" result = "" with open(self._obj_path, mode="rb") as file: + if addr < LOAD_ADDRESS: + return "E02" file_content = bytearray(file.read()) - addr_from = addr - load_address + if addr >= LOAD_ADDRESS + len(file_content): + return "E03" + addr_from = addr - LOAD_ADDRESS addr_to = addr_from + min(length, len(file_content) - addr_from) for i in range(addr_from, addr_to): result += format(file_content[i], "02x") file.close() return result + def setBreakpoint(self, packet): + bp_data = packet[1:].split(",") + self._bp_address = bp_data[1] + return "OK" + + def qfThreadInfo(self): + return "m1" + + def cont(self): + # Continue execution. Simulates running the Wasm engine until a breakpoint is hit. + return "T05thread-pcs:" + format(int(self._bp_address, 16) & 0x3fffffffffffffff, 'x') + ";thread:1" + + def qWasmCallStack(self): + if len(self._wasm_call_stacks) == 0: + return "" + result = self._wasm_call_stacks[self._call_stack_request_count].format(); + self._call_stack_request_count = self._call_stack_request_count + 1 + return result + + def qWasmLocal(self, packet): + # Format: qWasmLocal:frame_index;index + data = packet.split(":") + data = data[1].split(";") + frame_index = data[0] + local_index = data[1] + if frame_index == '0' and local_index == '4': + return "b0ff0000" + if frame_index == '1' and local_index == '5': + return "c0ff0000" + return "E03"; + + def qWasmMem(self, packet): + # Format: qWasmMem:module_id;addr;len + data = packet.split(":") + data = data[1].split(";") + module_id = data[0] + addr = int(data[1], 16) + length = int(data[2]) + if module_id != '4': + return "E03" + if addr >= 0xffb8 and addr < 0x10000: + chunk = self._memory_view[addr:addr+length] + return chunk.hex() + return "E03"; + +class WasmStackFrame: + pass + + def __init__(self, module_id, address): + self._module_id = module_id + self._address = address + + def format(self): + return format_register_value(make_code_address(self._module_id, self._address)) + +class WasmCallStack: + pass + + def __init__(self, wasm_stack_frames): + self._wasm_stack_frames = wasm_stack_frames + + def format(self): + result = "" + for frame in self._wasm_stack_frames: + result += frame.format() + return result + +class WasmMemorySpan: + pass + + def __init__(self, offset, bytes): + self._offset = offset + self._bytes = bytes class TestWasm(GDBRemoteTestBase): + def connect_to_wasm_engine(self, target): + """ + Create a process by connecting to the mock GDB server running in a mock WebAssembly engine. + Includes assertions that the process was successfully created. + """ + listener = self.dbg.GetListener() + error = lldb.SBError() + process = target.ConnectRemote( + listener, self.server.get_connect_url(), "wasm", error + ) + self.assertTrue(error.Success(), error.description) + self.assertTrue(process, PROCESS_IS_VALID) + return process + + def store_bytes(self, offset, bytes_obj): + chunk = self.memory_view[offset:offset+len(bytes_obj)] + for i in range(len(bytes_obj)): + chunk[i] = bytes_obj[i] + @skipIfAsan @skipIfXmlSupportMissing def test_load_module_with_embedded_symbols_from_remote(self): @@ -104,7 +214,7 @@ def test_load_module_with_embedded_symbols_from_remote(self): self.server.responder = MyResponder(obj_path, "test_wasm") target = self.dbg.CreateTarget("") - process = self.connect(target) + process = self.connect_to_wasm_engine(target) lldbutil.expect_state_changes( self, self.dbg.GetListener(), process, [lldb.eStateStopped] ) @@ -119,35 +229,35 @@ def test_load_module_with_embedded_symbols_from_remote(self): code_section = module.GetSectionAtIndex(0) self.assertEquals("code", code_section.GetName()) self.assertEquals( - load_address | code_section.GetFileOffset(), + make_code_address(MODULE_ID, code_section.GetFileOffset()), code_section.GetLoadAddress(target), ) debug_info_section = module.GetSectionAtIndex(1) self.assertEquals(".debug_info", debug_info_section.GetName()) self.assertEquals( - load_address | debug_info_section.GetFileOffset(), + make_code_address(MODULE_ID, debug_info_section.GetFileOffset()), debug_info_section.GetLoadAddress(target), ) debug_abbrev_section = module.GetSectionAtIndex(2) self.assertEquals(".debug_abbrev", debug_abbrev_section.GetName()) self.assertEquals( - load_address | debug_abbrev_section.GetFileOffset(), + make_code_address(MODULE_ID, debug_abbrev_section.GetFileOffset()), debug_abbrev_section.GetLoadAddress(target), ) debug_line_section = module.GetSectionAtIndex(3) self.assertEquals(".debug_line", debug_line_section.GetName()) self.assertEquals( - load_address | debug_line_section.GetFileOffset(), + make_code_address(MODULE_ID, debug_line_section.GetFileOffset()), debug_line_section.GetLoadAddress(target), ) debug_str_section = module.GetSectionAtIndex(4) self.assertEquals(".debug_str", debug_str_section.GetName()) self.assertEquals( - load_address | debug_line_section.GetFileOffset(), + make_code_address(MODULE_ID, debug_line_section.GetFileOffset()), debug_line_section.GetLoadAddress(target), ) @@ -174,7 +284,7 @@ def test_load_module_with_stripped_symbols_from_remote(self): ) target = self.dbg.CreateTarget("") - process = self.connect(target) + process = self.connect_to_wasm_engine(target) lldbutil.expect_state_changes( self, self.dbg.GetListener(), process, [lldb.eStateStopped] ) @@ -189,7 +299,7 @@ def test_load_module_with_stripped_symbols_from_remote(self): code_section = module.GetSectionAtIndex(0) self.assertEquals("code", code_section.GetName()) self.assertEquals( - load_address | code_section.GetFileOffset(), + make_code_address(MODULE_ID, code_section.GetFileOffset()), code_section.GetLoadAddress(target), ) @@ -216,59 +326,80 @@ def test_load_module_with_stripped_symbols_from_remote(self): self.assertEquals( LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target) ) - + @skipIfAsan @skipIfXmlSupportMissing - def test_load_module_from_file(self): - """Test connecting to a WebAssembly engine via GDB-remote and loading a Wasm module from a file""" + def test_simple_wasm_debugging_session(self): + """Test connecting to a WebAssembly engine via GDB-remote, loading a Wasm module with embedded DWARF symbols, setting a breakpoint and checking the debuggee state""" - yaml_path = "test_wasm_embedded_debug_sections.yaml" + yaml_path = "calc-cpp-o0.yaml" yaml_base, ext = os.path.splitext(yaml_path) - obj_path = self.getBuildArtifact(yaml_base) + obj_path = self.getBuildArtifact(yaml_base) + ".wasm" self.yaml2obj(yaml_path, obj_path) - self.server.responder = MyResponder(obj_path) + self.memory = bytearray(65536) + self.memory_view = memoryview(self.memory) + self.store_bytes(0xffb8, bytes.fromhex("d8ff0000")) + self.store_bytes(0xffbc, bytes.fromhex("e0ff0000")) + self.store_bytes(0xffe0, bytes.fromhex("0000000000000000")) + self.store_bytes(0xffe8, bytes.fromhex("0000000000004540")) + self.store_bytes(0xfff0, bytes.fromhex("0000000000003640")) + self.store_bytes(0xfff8, bytes.fromhex("0000000000003440")) + + call_stacks = [ + WasmCallStack([WasmStackFrame(3, 0x00000083), WasmStackFrame(3, 0x0000000f)]), + WasmCallStack([WasmStackFrame(4, 0x000002ad), WasmStackFrame(4, 0x0000014a), WasmStackFrame(3, 0x00000083), WasmStackFrame(3, 0x0000000f)]) + ] + self.server.responder = MyResponder(obj_path, "test_wasm", call_stacks, self.memory_view) target = self.dbg.CreateTarget("") - process = self.connect(target) + breakpoint = target.BreakpointCreateByLocation("calc.cpp", 9); + + process = self.connect_to_wasm_engine(target) lldbutil.expect_state_changes( self, self.dbg.GetListener(), process, [lldb.eStateStopped] ) - num_modules = target.GetNumModules() - self.assertEquals(1, num_modules) + # Verify all breakpoint locations are enabled. + location = breakpoint.GetLocationAtIndex(0) + self.assertTrue(location and location.IsEnabled(), VALID_BREAKPOINT_LOCATION) - module = target.GetModuleAtIndex(0) - num_sections = module.GetNumSections() - self.assertEquals(5, num_sections) + # Continue execution. + self.runCmd("c") - code_section = module.GetSectionAtIndex(0) - self.assertEquals("code", code_section.GetName()) - self.assertEquals( - load_address | code_section.GetFileOffset(), - code_section.GetLoadAddress(target), - ) + # Verify 1st breakpoint location is hit. + from lldbsuite.test.lldbutil import get_stopped_thread - debug_info_section = module.GetSectionAtIndex(1) - self.assertEquals(".debug_info", debug_info_section.GetName()) - self.assertEquals( - LLDB_INVALID_ADDRESS, debug_info_section.GetLoadAddress(target) + thread = get_stopped_thread(process, lldb.eStopReasonSignal) + self.assertTrue( + thread.IsValid(), "There should be a thread stopped due to breakpoint" ) + frame0 = thread.GetFrameAtIndex(0) + self.server.responder.SetCurrentPC(0x000002ad); - debug_abbrev_section = module.GetSectionAtIndex(2) - self.assertEquals(".debug_abbrev", debug_abbrev_section.GetName()) - self.assertEquals( - LLDB_INVALID_ADDRESS, debug_abbrev_section.GetLoadAddress(target) - ) + # Update Wasm server memory state + self.store_bytes(0xffe0, bytes.fromhex("0000000000003440")) - debug_line_section = module.GetSectionAtIndex(3) - self.assertEquals(".debug_line", debug_line_section.GetName()) - self.assertEquals( - LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target) + # We should be in function 'bar'. + self.assertTrue(frame0.IsValid()) + function_name = frame0.GetFunctionName() + self.assertIn( + "Calc::add(Number const&)", function_name, "Unexpected function name {}".format(function_name) ) - debug_str_section = module.GetSectionAtIndex(4) - self.assertEquals(".debug_str", debug_str_section.GetName()) - self.assertEquals( - LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target) - ) + # We should be able to evaluate the expression "*this". + value = frame0.EvaluateExpression("*this") + self.assertEqual(value.GetTypeName(), "Calc") + field = value.GetChildAtIndex(0) + self.assertEqual(field.GetName(), "result_") + self.assertEqual(field.GetTypeName(), "double") + self.assertEqual(field.GetValue(), "20") + + # Examine next stack frame. + self.runCmd("up") + frame1 = thread.GetSelectedFrame() + + # We should be able to evaluate the expression "v2". + value = frame1.EvaluateExpression("v2") + self.assertEqual(value.GetTypeName(), "double") + self.assertEqual(value.GetValue(), "22") >From db0bc4829b02aedf7a988824b38b56a1564a8125 Mon Sep 17 00:00:00 2001 From: Paolo Severini <paolo...@microsoft.com> Date: Mon, 22 Jan 2024 05:57:53 -0800 Subject: [PATCH 3/6] Add test files and fix formatting --- .../gdb_remote_client/TestWasm.py | 76 ++++-- .../gdb_remote_client/calc-cpp-o0.yaml | 251 ++++++++++++++++++ .../gdb_remote_client/calc.cpp | 27 ++ 3 files changed, 326 insertions(+), 28 deletions(-) create mode 100644 lldb/test/API/functionalities/gdb_remote_client/calc-cpp-o0.yaml create mode 100644 lldb/test/API/functionalities/gdb_remote_client/calc.cpp diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py index f2ac2abdeef0c0..5cde7f58eb920d 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py @@ -45,11 +45,11 @@ def respond(self, packet): if packet[0:13] == "qRegisterInfo": return self.qRegisterInfo(packet[13:]) if packet.startswith("qWasmCallStack"): - return self.qWasmCallStack(); + return self.qWasmCallStack() if packet.startswith("qWasmLocal"): - return self.qWasmLocal(packet); + return self.qWasmLocal(packet) if packet.startswith("qWasmMem"): - return self.qWasmMem(packet); + return self.qWasmMem(packet) return MockGDBServerResponder.respond(self, packet) def qSupported(self, client_supported): @@ -79,7 +79,7 @@ def haltReason(self): return "T05thread-pcs:300000083;thread:1;library:;" def readRegister(self, register): - assert(register == 0) + assert register == 0 return format_register_value(self.current_pc) def qXferRead(self, obj, annex, offset, length): @@ -117,12 +117,16 @@ def qfThreadInfo(self): def cont(self): # Continue execution. Simulates running the Wasm engine until a breakpoint is hit. - return "T05thread-pcs:" + format(int(self._bp_address, 16) & 0x3fffffffffffffff, 'x') + ";thread:1" + return ( + "T05thread-pcs:" + + format(int(self._bp_address, 16) & 0x3FFFFFFFFFFFFFFF, "x") + + ";thread:1" + ) def qWasmCallStack(self): if len(self._wasm_call_stacks) == 0: return "" - result = self._wasm_call_stacks[self._call_stack_request_count].format(); + result = self._wasm_call_stacks[self._call_stack_request_count].format() self._call_stack_request_count = self._call_stack_request_count + 1 return result @@ -132,11 +136,11 @@ def qWasmLocal(self, packet): data = data[1].split(";") frame_index = data[0] local_index = data[1] - if frame_index == '0' and local_index == '4': + if frame_index == "0" and local_index == "4": return "b0ff0000" - if frame_index == '1' and local_index == '5': + if frame_index == "1" and local_index == "5": return "c0ff0000" - return "E03"; + return "E03" def qWasmMem(self, packet): # Format: qWasmMem:module_id;addr;len @@ -145,12 +149,13 @@ def qWasmMem(self, packet): module_id = data[0] addr = int(data[1], 16) length = int(data[2]) - if module_id != '4': + if module_id != "4": return "E03" - if addr >= 0xffb8 and addr < 0x10000: - chunk = self._memory_view[addr:addr+length] + if addr >= 0xFFB8 and addr < 0x10000: + chunk = self._memory_view[addr : addr + length] return chunk.hex() - return "E03"; + return "E03" + class WasmStackFrame: pass @@ -162,6 +167,7 @@ def __init__(self, module_id, address): def format(self): return format_register_value(make_code_address(self._module_id, self._address)) + class WasmCallStack: pass @@ -174,6 +180,7 @@ def format(self): result += frame.format() return result + class WasmMemorySpan: pass @@ -197,7 +204,7 @@ def connect_to_wasm_engine(self, target): return process def store_bytes(self, offset, bytes_obj): - chunk = self.memory_view[offset:offset+len(bytes_obj)] + chunk = self.memory_view[offset : offset + len(bytes_obj)] for i in range(len(bytes_obj)): chunk[i] = bytes_obj[i] @@ -326,7 +333,7 @@ def test_load_module_with_stripped_symbols_from_remote(self): self.assertEquals( LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target) ) - + @skipIfAsan @skipIfXmlSupportMissing def test_simple_wasm_debugging_session(self): @@ -339,21 +346,32 @@ def test_simple_wasm_debugging_session(self): self.memory = bytearray(65536) self.memory_view = memoryview(self.memory) - self.store_bytes(0xffb8, bytes.fromhex("d8ff0000")) - self.store_bytes(0xffbc, bytes.fromhex("e0ff0000")) - self.store_bytes(0xffe0, bytes.fromhex("0000000000000000")) - self.store_bytes(0xffe8, bytes.fromhex("0000000000004540")) - self.store_bytes(0xfff0, bytes.fromhex("0000000000003640")) - self.store_bytes(0xfff8, bytes.fromhex("0000000000003440")) + self.store_bytes(0xFFB8, bytes.fromhex("d8ff0000")) + self.store_bytes(0xFFBC, bytes.fromhex("e0ff0000")) + self.store_bytes(0xFFE0, bytes.fromhex("0000000000000000")) + self.store_bytes(0xFFE8, bytes.fromhex("0000000000004540")) + self.store_bytes(0xFFF0, bytes.fromhex("0000000000003640")) + self.store_bytes(0xFFF8, bytes.fromhex("0000000000003440")) call_stacks = [ - WasmCallStack([WasmStackFrame(3, 0x00000083), WasmStackFrame(3, 0x0000000f)]), - WasmCallStack([WasmStackFrame(4, 0x000002ad), WasmStackFrame(4, 0x0000014a), WasmStackFrame(3, 0x00000083), WasmStackFrame(3, 0x0000000f)]) + WasmCallStack( + [WasmStackFrame(3, 0x00000083), WasmStackFrame(3, 0x0000000F)] + ), + WasmCallStack( + [ + WasmStackFrame(4, 0x000002AD), + WasmStackFrame(4, 0x0000014A), + WasmStackFrame(3, 0x00000083), + WasmStackFrame(3, 0x0000000F), + ] + ), ] - self.server.responder = MyResponder(obj_path, "test_wasm", call_stacks, self.memory_view) + self.server.responder = MyResponder( + obj_path, "test_wasm", call_stacks, self.memory_view + ) target = self.dbg.CreateTarget("") - breakpoint = target.BreakpointCreateByLocation("calc.cpp", 9); + breakpoint = target.BreakpointCreateByLocation("calc.cpp", 9) process = self.connect_to_wasm_engine(target) lldbutil.expect_state_changes( @@ -375,16 +393,18 @@ def test_simple_wasm_debugging_session(self): thread.IsValid(), "There should be a thread stopped due to breakpoint" ) frame0 = thread.GetFrameAtIndex(0) - self.server.responder.SetCurrentPC(0x000002ad); + self.server.responder.SetCurrentPC(0x000002AD) # Update Wasm server memory state - self.store_bytes(0xffe0, bytes.fromhex("0000000000003440")) + self.store_bytes(0xFFE0, bytes.fromhex("0000000000003440")) # We should be in function 'bar'. self.assertTrue(frame0.IsValid()) function_name = frame0.GetFunctionName() self.assertIn( - "Calc::add(Number const&)", function_name, "Unexpected function name {}".format(function_name) + "Calc::add(Number const&)", + function_name, + "Unexpected function name {}".format(function_name), ) # We should be able to evaluate the expression "*this". diff --git a/lldb/test/API/functionalities/gdb_remote_client/calc-cpp-o0.yaml b/lldb/test/API/functionalities/gdb_remote_client/calc-cpp-o0.yaml new file mode 100644 index 00000000000000..ec7b99f19106fe --- /dev/null +++ b/lldb/test/API/functionalities/gdb_remote_client/calc-cpp-o0.yaml @@ -0,0 +1,251 @@ +--- !WASM +FileHeader: + Version: 0x1 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ParamTypes: + - F64 + - F64 + - F64 + ReturnTypes: + - I32 + - Index: 1 + ParamTypes: + - I32 + ReturnTypes: + - I32 + - Index: 2 + ParamTypes: + - I32 + - F64 + ReturnTypes: + - I32 + - Index: 3 + ParamTypes: + - I32 + - I32 + ReturnTypes: [] + - Index: 4 + ParamTypes: + - I32 + ReturnTypes: + - F64 + - Type: FUNCTION + FunctionTypes: [ 0, 1, 2, 3, 4 ] + - Type: TABLE + Tables: + - Index: 0 + ElemType: FUNCREF + Limits: + Flags: [ HAS_MAX ] + Minimum: 0x1 + Maximum: 0x1 + - Type: MEMORY + Memories: + - Flags: [ HAS_MAX ] + Minimum: 0x100 + Maximum: 0x100 + - Type: GLOBAL + Globals: + - Index: 0 + Type: I32 + Mutable: true + InitExpr: + Opcode: I32_CONST + Value: 65536 + - Type: EXPORT + Exports: + - Name: memory + Kind: MEMORY + Index: 0 + - Name: test_sum + Kind: FUNCTION + Index: 0 + - Name: __indirect_function_table + Kind: TABLE + Index: 0 + - Type: CODE + Functions: + - Index: 0 + Locals: + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: F64 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: F64 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: F64 + Count: 1 + - Type: F64 + Count: 1 + - Type: F64 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + Body: 2300210341C0002104200320046B21052005240020052000390338200520013903302005200239032841202106200520066A210720072108200810011A20052B033821094118210A2005200A6A210B200B210C200C200910021A4120210D2005200D6A210E200E210F41182110200520106A211120112112200F2012100320052B0330211341102114200520146A2115201521162016201310021A41202117200520176A2118201821194110211A2005201A6A211B201B211C2019201C10034120211D2005201D6A211E201E211F201F100421202005202039030820052B0328212120052B0308212220212022612123410121242023202471212541C0002126200520266A21272027240020250F0B + - Index: 1 + Locals: + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: F64 + Count: 1 + Body: 2300210141102102200120026B21032003200036020C200328020C2104410021052005B721062004200639030020040F0B + - Index: 2 + Locals: + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: F64 + Count: 1 + Body: 2300210241102103200220036B21042004200036020C20042001390300200428020C210520042B030021062005200639030020050F0B + - Index: 3 + Locals: + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: F64 + Count: 1 + - Type: F64 + Count: 1 + - Type: F64 + Count: 1 + Body: 2300210241102103200220036B21042004200036020C20042001360208200428020C21052004280208210620062B0300210720052B0300210820082007A02109200520093903000F0B + - Index: 4 + Locals: + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: F64 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + - Type: F64 + Count: 1 + - Type: I32 + Count: 1 + - Type: I32 + Count: 1 + Body: 2300210141102102200120026B210320032400200320003602042003280204210420042B0300210541082106200320066A2107200721082008200510021A20032B030821094110210A2003200A6A210B200B240020090F0B + - Type: CUSTOM + Name: name + FunctionNames: + - Index: 0 + Name: test_sum + - Index: 1 + Name: 'Calc::Calc()' + - Index: 2 + Name: 'Number::Number(double)' + - Index: 3 + Name: 'Calc::add(Number const&)' + - Index: 4 + Name: 'Calc::result() const' + GlobalNames: + - Index: 0 + Name: __stack_pointer + - Type: CUSTOM + Name: .debug_abbrev + Payload: 011101250E1305030E10171B0E110155170000021301360B030E0B0B3A0B3B0B0000030D00030E49133A0B3B0B380B0000042E01030E3A0B3B0B3C193F19000005050049133419000006050049130000072400030E3E0B0B0B0000080F0049130000090D00030E49133A0B3B0B380B320B00000A2E016E0E030E3A0B3B0B3C193F1900000B2E016E0E030E3A0B3B0B49133C193F1900000C2E01030E3C1934193F1900000D1000491300000E2600491300000F2E01110112064018030E3A0B3B0B49133F1900001005000218030E3A0B3B0B491300001134000218030E3A0B3B0B49130000122E0111011206401864133A0B3B0B6E0E471300001305000218030E491334190000142E01110112064018641347130000152E0111011206401864136E0E4713000000 + - Type: CUSTOM + Name: .debug_info + Payload: E40100000400000000000401F60000002100970000000000000020000000000000000000000002059000000008010103E90000004E000000010300049000000001020555000000064E000000000007B4000000040808260000000205DC00000008010609E10000004E000000011000030A80000000C4000000010705A700000006AC000000000B000000006D000000010B2600000005B6000000000CDC00000005A70000000000085A0000000DB10000000E2600000008BB0000000E5A0000000F030000005A01000004ED00059FA00000000114D101000010029138F300000001144E00000010029130F000000001144E00000010029128BB00000001144E00000011029120D700000001155A000000110291086D00000001182600000000125E0100003E00000004ED00039F3B0100000106130000009B0000001302910C74000000D80100000014DF0100005A00000004ED00049F5E010000700000001302910C74000000D801000010029108790000000107AC00000000159D0100004100000004ED00049F93010000C80000003B0000001302910C74000000DD01000010029100AE00000001024E00000000143A0200006F00000004ED00039FC4010000860000001302910474000000E20100000007A90000000201085A000000082600000008BB00000000 + - Type: CUSTOM + Name: .debug_ranges + Payload: 030000005D0100005E0100009C010000DF010000390200009D010000DE0100003A020000A90200000000000000000000 + - Type: CUSTOM + Name: .debug_str + Payload: 5F5A4E4B3443616C6336726573756C744576005F5A4E3443616C634332457600513A5C70616F6C6F7365764D5346545C6C6C766D2D70726F6A6563745C6C6C64625C746573745C4150495C66756E6374696F6E616C69746965735C6764625F72656D6F74655F636C69656E7400726573756C740074686973006E756D626572005F5A4E3443616C633361646445524B364E756D6265720063616C632E63707000746573745F73756D00626F6F6C0076616C756500646F75626C6500657870656374656400616464005F5A4E364E756D626572433245640063616C630043616C6300726573756C745F0076616C75655F00763200763100636C616E672076657273696F6E2031382E302E30202868747470733A2F2F6769746875622E636F6D2F6C6C766D2F6C6C766D2D70726F6A65637420373535303166353336323464653932616166636532663164613639386232343961373239336463372900 + - Type: CUSTOM + Name: .debug_line + Payload: A701000004002B000000010101FB0E0D0001010101000000010000010063616C632E637070000000003C737464696E3E00000000000005020300000003130100050277000000030105080A010005028B000000030105130100050292000000050C0601000502A8000000050801000502CC000000030105130601000502D3000000050C0601000502E90000000508010005020D010000030105180601000502290100000301050A0100050230010000051D0601000502370100000513010005023E0100000503010005025D0100000001010005025E01000003050100050288010000030A050A0A010005029801000003760508010005029C0100000001010005029D010000030101000502CC01000005210A01000502D3010000051A0601000502DA010000052901000502DE010000000101000502DF01000003060100050214020000030105100A010005021B0200000517060100050222020000050D0100050237020000030105030601000502390200000001010005023A020000030A0100050272020000030105130A0100050279020000050C06010005028F020000050501000502A9020000000101 +... diff --git a/lldb/test/API/functionalities/gdb_remote_client/calc.cpp b/lldb/test/API/functionalities/gdb_remote_client/calc.cpp new file mode 100644 index 00000000000000..7e4c53c42cb500 --- /dev/null +++ b/lldb/test/API/functionalities/gdb_remote_client/calc.cpp @@ -0,0 +1,27 @@ +struct Number { + Number(double value) : value_(value) {} + double value_; +}; + +struct Calc { + void add(const Number& number) { + result_ += number.value_; + } + + Number result() const { + return Number(result_); + } + + private: + double result_ = 0; +}; + +extern "C" { +bool test_sum(double v1, double v2, double expected) { + Calc calc; + calc.add(Number(v1)); + calc.add(Number(v2)); + Number result = calc.result(); + return expected == result.value_; +} +} \ No newline at end of file >From 18f853c1b0263bbfc38bddbf743e15137cbd76f3 Mon Sep 17 00:00:00 2001 From: Paolo Severini <paolo...@microsoft.com> Date: Mon, 19 Feb 2024 06:07:24 -0800 Subject: [PATCH 4/6] Address review comments --- lldb/source/Expression/DWARFExpression.cpp | 5 ++--- lldb/source/Interpreter/CommandInterpreter.cpp | 18 ------------------ .../Plugins/Process/wasm/ProcessWasm.cpp | 1 - 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index 95033db5ed8f5a..42863a7f61d615 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -348,11 +348,10 @@ static offset_t GetOpcodeDataSize(const DataExtractor &data, case DW_OP_WASM_location: { uint8_t wasm_op = data.GetU8(&offset); - if (wasm_op == 3) { + if (wasm_op == 3) data.GetU32(&offset); - } else { + else data.GetULEB128(&offset); - } return offset - data_offset; } diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index bcacc7aabb66ca..00651df48b6224 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -794,24 +794,6 @@ 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/wasm/ProcessWasm.cpp b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp index 19b8babeb59351..a8318223e7675d 100644 --- a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp +++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp @@ -105,7 +105,6 @@ size_t ProcessWasm::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error); } case WasmAddressType::Code: - wasm_addr.type = 0; return ProcessGDBRemote::ReadMemory(wasm_addr, buf, size, error); case WasmAddressType::Invalid: default: >From 89c4ec1b1dec51df187e84c83ad3cb25eb7e847a Mon Sep 17 00:00:00 2001 From: Paolo Severini <paolo...@microsoft.com> Date: Tue, 20 Feb 2024 13:21:38 -0800 Subject: [PATCH 5/6] Fix formatting --- .../API/functionalities/gdb_remote_client/TestWasm.py | 1 + .../API/functionalities/gdb_remote_client/calc.cpp | 10 +++------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py index 5cde7f58eb920d..459f65d2696b7e 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py @@ -23,6 +23,7 @@ def format_register_value(val): shift += 8 return result + def make_code_address(module_id, offset): return 0x4000000000000000 | (module_id << 32) | offset diff --git a/lldb/test/API/functionalities/gdb_remote_client/calc.cpp b/lldb/test/API/functionalities/gdb_remote_client/calc.cpp index 7e4c53c42cb500..450a318ec8006d 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/calc.cpp +++ b/lldb/test/API/functionalities/gdb_remote_client/calc.cpp @@ -4,15 +4,11 @@ struct Number { }; struct Calc { - void add(const Number& number) { - result_ += number.value_; - } + void add(const Number &number) { result_ += number.value_; } - Number result() const { - return Number(result_); - } + Number result() const { return Number(result_); } - private: +private: double result_ = 0; }; >From 0ca8b8be138bda5bd1e455356388bdac28f62e22 Mon Sep 17 00:00:00 2001 From: Paolo Severini <paolo...@microsoft.com> Date: Thu, 22 Feb 2024 09:38:55 -0800 Subject: [PATCH 6/6] Address more review comments --- lldb/source/Expression/DWARFExpression.cpp | 32 ++++++++++------- .../Plugins/Process/wasm/UnwindWasm.cpp | 3 +- .../Process/wasm/wasmRegisterContext.cpp | 15 ++++---- .../Process/wasm/wasmRegisterContext.h | 35 +++++++++++++++++-- .../Plugins/SymbolFile/DWARF/DWARFWasm.h | 22 ++++++++++++ .../gdb_remote_client/TestWasm.py | 1 + 6 files changed, 82 insertions(+), 26 deletions(-) create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFWasm.h diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index 4f88eedeefc9fc..3e7a41d1f2a8d9 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -41,6 +41,8 @@ #include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "Plugins/SymbolFile/DWARF/DWARFUnit.h" +#include "Plugins/SymbolFile/DWARF/DWARFWasm.h" +#include "Plugins/Process/wasm/wasmRegisterContext.h" using namespace lldb; using namespace lldb_private; @@ -347,8 +349,9 @@ static offset_t GetOpcodeDataSize(const DataExtractor &data, } case DW_OP_WASM_location: { - uint8_t wasm_op = data.GetU8(&offset); - if (wasm_op == 3) + DWARFWasmLocation wasm_location = + static_cast<DWARFWasmLocation>(data.GetU8(&offset)); + if (wasm_location == DWARFWasmLocation::eGlobalU32) data.GetU32(&offset); else data.GetULEB128(&offset); @@ -2605,26 +2608,29 @@ bool DWARFExpression::Evaluate( case DW_OP_WASM_location: { uint8_t wasm_op = opcodes.GetU8(&offset); - uint32_t index; + if (wasm_op > DWARFWasmLocation::eGlobalU32) { + if (error_ptr) + error_ptr->SetErrorString("Invalid Wasm location index"); + return false; + } + DWARFWasmLocation wasm_location = static_cast<DWARFWasmLocation>(wasm_op); /* 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 + * | WasmVirtualRegisterKinds: 2 bits | index: 30 bits | */ - if (wasm_op == 3) { + uint32_t index; + if (wasm_location == DWARFWasmLocation::eGlobalU32) { index = opcodes.GetU32(&offset); - wasm_op = 2; // Global } else { index = opcodes.GetULEB128(&offset); } - - reg_num = (((wasm_op + 1) & 0x03) << 30) | (index & 0x3fffffff); + wasm::WasmVirtualRegisterKinds register_tag = + wasm::WasmVirtualRegisterInfo::VirtualRegisterKindFromDWARFLocation( + wasm_location); + reg_num = (register_tag << wasm::WasmRegisterContext::kTagShift) | + (index & wasm::WasmRegisterContext::kIndexMask); if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp)) stack.push_back(tmp); diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp index 9e8f6a9ce7d9ff..4d2f8f61ff7d4f 100644 --- a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp +++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp @@ -62,9 +62,8 @@ uint32_t UnwindWasm::DoGetFrameCount() { 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) { + if (m_frames.size() == 0) DoGetFrameCount(); - } if (frame_idx < m_frames.size()) { behaves_like_zeroth_frame = (frame_idx == 0); diff --git a/lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp index 470a0fb6a43cb3..c4d125b13d953b 100644 --- a/lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp +++ b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.cpp @@ -34,18 +34,16 @@ uint32_t WasmRegisterContext::ConvertRegisterKindToRegisterNumber( size_t WasmRegisterContext::GetRegisterCount() { return 0; } const RegisterInfo *WasmRegisterContext::GetRegisterInfoAtIndex(size_t reg) { - uint32_t tag = (reg >> 30) & 0x03; - if (tag == 0) { - return m_reg_info_sp->GetRegisterInfoAtIndex(reg); - } + uint32_t tag = (reg >> kTagShift) & kTagMask; + if (tag == WasmVirtualRegisterKinds::eNotAWasmLocation) + return m_reg_info_sp->GetRegisterInfoAtIndex(reg & kIndexMask); auto it = m_register_map.find(reg); if (it == m_register_map.end()) { - WasmVirtualRegisterKinds kind = - static_cast<WasmVirtualRegisterKinds>(tag - 1); + WasmVirtualRegisterKinds kind = static_cast<WasmVirtualRegisterKinds>(tag); std::tie(it, std::ignore) = m_register_map.insert( {reg, - std::make_unique<WasmVirtualRegisterInfo>(kind, reg & 0x3fffffff)}); + std::make_unique<WasmVirtualRegisterInfo>(kind, reg & kIndexMask)}); } return it->second.get(); } @@ -58,9 +56,8 @@ const RegisterSet *WasmRegisterContext::GetRegisterSet(size_t reg_set) { bool WasmRegisterContext::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) { - if (reg_info->name) { + if (reg_info->name) return GDBRemoteRegisterContext::ReadRegister(reg_info, value); - } ThreadWasm *thread = static_cast<ThreadWasm *>(&GetThread()); ProcessWasm *process = static_cast<ProcessWasm *>(thread->GetProcess().get()); diff --git a/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h index f60cc8e6a099ab..796f06fbd3de62 100644 --- a/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h +++ b/lldb/source/Plugins/Process/wasm/wasmRegisterContext.h @@ -10,6 +10,7 @@ #define LLDB_SOURCE_PLUGINS_PROCESS_WASM_WASMREGISTERCONTEXT_H #include "Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h" +#include "Plugins/SymbolFile/DWARF/DWARFWasm.h" #include "ThreadWasm.h" #include "lldb/lldb-private-types.h" #include <unordered_map> @@ -21,11 +22,22 @@ class WasmRegisterContext; typedef std::shared_ptr<WasmRegisterContext> WasmRegisterContextSP; +/* + * WebAssembly locals, globals and operand stacks are encoded as virtual + * registers with the format: + * | WasmVirtualRegisterKinds: 2 bits | index: 30 bits | + * where tag is: + * 0: Not a WebAssembly location + * 1: Local + * 2: Global + * 3: Operand stack value + */ + enum WasmVirtualRegisterKinds { - eLocal = 0, ///< wasm local + eNotAWasmLocation = 0, + eLocal, ///< wasm local eGlobal, ///< wasm global eOperandStack, ///< wasm operand stack - kNumWasmVirtualRegisterKinds }; struct WasmVirtualRegisterInfo : public RegisterInfo { @@ -34,11 +46,30 @@ struct WasmVirtualRegisterInfo : public RegisterInfo { WasmVirtualRegisterInfo(WasmVirtualRegisterKinds kind, uint32_t index) : RegisterInfo(), kind(kind), index(index) {} + + static WasmVirtualRegisterKinds VirtualRegisterKindFromDWARFLocation( + plugin::dwarf::DWARFWasmLocation dwarf_location) { + switch (dwarf_location) { + case plugin::dwarf::DWARFWasmLocation::eLocal: + return WasmVirtualRegisterKinds::eLocal; + case plugin::dwarf::DWARFWasmLocation::eGlobal: + case plugin::dwarf::DWARFWasmLocation::eGlobalU32: + return WasmVirtualRegisterKinds::eLocal; + case plugin::dwarf::DWARFWasmLocation::eOperandStack: + return WasmVirtualRegisterKinds::eOperandStack; + default: + llvm_unreachable("Invalid DWARF Wasm location"); + } + } }; class WasmRegisterContext : public process_gdb_remote::GDBRemoteRegisterContext { public: + static const uint32_t kTagMask = 0x03; + static const uint32_t kIndexMask = 0x3fffffff; + static const uint32_t kTagShift = 30; + WasmRegisterContext( wasm::ThreadWasm &thread, uint32_t concrete_frame_idx, process_gdb_remote::GDBRemoteDynamicRegisterInfoSP reg_info_sp); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFWasm.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFWasm.h new file mode 100644 index 00000000000000..e86fef897f3751 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFWasm.h @@ -0,0 +1,22 @@ +//===-- DWARFWasm.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_SYMBOLFILE_DWARF_DWARFWASM_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFWASM_H + +#include "Plugins/SymbolFile/DWARF/DWARFWasm.h" + +namespace lldb_private::plugin { +namespace dwarf { + +enum DWARFWasmLocation { eLocal = 0, eGlobal, eOperandStack, eGlobalU32 }; + +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFWASM_H diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py index 761ae828dd5181..bcdabeba2875af 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py @@ -27,6 +27,7 @@ def format_register_value(val): def make_code_address(module_id, offset): return 0x4000000000000000 | (module_id << 32) | offset + class MyResponder(MockGDBServerResponder): current_pc = LOAD_ADDRESS + 0x0A _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits