diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md index 80c68091ecd07..41628cffcc566 100644 --- a/lldb/docs/resources/lldbgdbremote.md +++ b/lldb/docs/resources/lldbgdbremote.md @@ -1998,6 +1998,22 @@ threads (live system debug) / cores (JTAG) in your program have stopped and allows LLDB to display and control your program correctly. +## qWasmCallStack + +Get the Wasm call stack for the given thread id. This returns a hex-encoded +list of PC values, one for each frame of the call stack. To match the Wasm +specification, the addresses are encoded in little endian byte order, even if +the endian of the Wasm runtime's host is not little endian. + +``` +send packet: $qWasmCallStack:202dbe040#08 +read packet: $9c01000000000040e501000000000040fe01000000000040# +``` + +**Priority to Implement:** Only required for Wasm support. This packed is +supported by the [WAMR](https://github.com/bytecodealliance/wasm-micro-runtime) +and [V8](https://v8.dev) Wasm runtimes. + ## qWatchpointSupportInfo Get the number of hardware watchpoints available on the remote target. diff --git a/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py b/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py index 459460b84fbae..599f7878e6edb 100644 --- a/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py +++ b/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py @@ -45,7 +45,7 @@ def createTarget(self, yaml_path): self.yaml2obj(yaml_path, obj_path) return self.dbg.CreateTarget(obj_path) - def connect(self, target): + def connect(self, target, plugin="gdb-remote"): """ Create a process by connecting to the mock GDB server. @@ -54,7 +54,7 @@ def connect(self, target): listener = self.dbg.GetListener() error = lldb.SBError() process = target.ConnectRemote( - listener, self.server.get_connect_url(), "gdb-remote", error + listener, self.server.get_connect_url(), plugin, error ) self.assertTrue(error.Success(), error.description) self.assertTrue(process, PROCESS_IS_VALID) diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt index bd9b1b86dbf13..3413360e975fb 100644 --- a/lldb/source/Plugins/Process/CMakeLists.txt +++ b/lldb/source/Plugins/Process/CMakeLists.txt @@ -29,3 +29,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 a2c34ddfc252e..14dfdec6a6f62 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -323,6 +323,11 @@ ProcessGDBRemote::~ProcessGDBRemote() { KillDebugserverProcess(); } +std::shared_ptr +ProcessGDBRemote::CreateThread(lldb::tid_t tid) { + return std::make_shared(*this, tid); +} + bool ProcessGDBRemote::ParsePythonTargetDefinition( const FileSpec &target_definition_fspec) { ScriptInterpreter *interpreter = @@ -1594,7 +1599,7 @@ bool ProcessGDBRemote::DoUpdateThreadList(ThreadList &old_thread_list, ThreadSP thread_sp( old_thread_list_copy.RemoveThreadByProtocolID(tid, false)); if (!thread_sp) { - thread_sp = std::make_shared(*this, tid); + thread_sp = CreateThread(tid); LLDB_LOGV(log, "Making new thread: {0} for thread ID: {1:x}.", thread_sp.get(), thread_sp->GetID()); } else { @@ -1726,7 +1731,7 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( if (!thread_sp) { // Create the thread if we need to - thread_sp = std::make_shared(*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 7ae33837fd067..7c3dfb179a4b3 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -246,6 +246,8 @@ class ProcessGDBRemote : public Process, ProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); + virtual std::shared_ptr CreateThread(lldb::tid_t tid); + bool SupportsMemoryTagging() override; /// Broadcaster event bits definitions. diff --git a/lldb/source/Plugins/Process/wasm/CMakeLists.txt b/lldb/source/Plugins/Process/wasm/CMakeLists.txt new file mode 100644 index 0000000000000..ff8a3c792ad53 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/CMakeLists.txt @@ -0,0 +1,10 @@ +add_lldb_library(lldbPluginProcessWasm PLUGIN + ProcessWasm.cpp + ThreadWasm.cpp + UnwindWasm.cpp + + LINK_LIBS + lldbCore + 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 0000000000000..3ea3ea2a46847 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp @@ -0,0 +1,133 @@ +//===----------------------------------------------------------------------===// +// +// 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/Core/Value.h" +#include "lldb/Utility/DataBufferHeap.h" + +#include "lldb/Target/UnixSignals.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private::wasm; + +LLDB_PLUGIN_DEFINE(ProcessWasm) + +ProcessWasm::ProcessWasm(lldb::TargetSP target_sp, ListenerSP listener_sp) + : ProcessGDBRemote(target_sp, listener_sp) { + assert(target_sp); + // Wasm doesn't have any Unix-like signals as a platform concept, but pretend + // like it does to appease LLDB. + m_unix_signals_sp = UnixSignals::Create(target_sp->GetArchitecture()); +} + +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() { return "wasm"; } + +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) { + if (crash_file_path == nullptr) + return std::make_shared(target_sp, listener_sp); + return {}; +} + +bool ProcessWasm::CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) { + if (plugin_specified_by_name) + return true; + + if (Module *exe_module = target_sp->GetExecutableModulePointer()) { + if (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 ProcessWasm::CreateThread(lldb::tid_t tid) { + return std::make_shared(*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: + error.FromErrorStringWithFormat( + "Wasm read failed for invalid address 0x%" PRIx64, vm_addr); + return 0; + } +} + +llvm::Expected> +ProcessWasm::GetWasmCallStack(lldb::tid_t tid) { + StreamString packet; + packet.Printf("qWasmCallStack:"); + packet.Printf("%llx", tid); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) != + GDBRemoteCommunication::PacketResult::Success) + return llvm::createStringError("failed to send qWasmCallStack"); + + if (!response.IsNormalResponse()) + return llvm::createStringError("failed to get response for qWasmCallStack"); + + WritableDataBufferSP data_buffer_sp = + std::make_shared(response.GetStringRef().size() / 2, 0); + const size_t bytes = response.GetHexBytes(data_buffer_sp->GetData(), '\xcc'); + if (bytes == 0 || bytes % sizeof(uint64_t) != 0) + return llvm::createStringError("invalid response for qWasmCallStack"); + + // To match the Wasm specification, the addresses are encoded in little endian + // byte order. + DataExtractor data(data_buffer_sp, lldb::eByteOrderLittle, + GetAddressByteSize()); + lldb::offset_t offset = 0; + std::vector call_stack_pcs; + while (offset < bytes) + call_stack_pcs.push_back(data.GetU64(&offset)); + + return call_stack_pcs; +} diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.h b/lldb/source/Plugins/Process/wasm/ProcessWasm.h new file mode 100644 index 0000000000000..bab14a8be7dbe --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// 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" + +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. +enum WasmAddressType : uint8_t { Memory = 0x00, Object = 0x01, Invalid = 0xff }; + +/// For the purpose of debugging, we can represent all these separated 32-bit +/// address spaces with a single virtual 64-bit address space. The +/// wasm_addr_t provides this encoding using bitfields. +struct wasm_addr_t { + uint64_t offset : 32; + uint64_t module_id : 30; + uint64_t type : 2; + + wasm_addr_t(lldb::addr_t addr) + : offset(addr & 0x00000000ffffffff), + module_id((addr & 0x00ffffff00000000) >> 32), type(addr >> 62) {} + + wasm_addr_t(WasmAddressType type, uint32_t module_id, uint32_t offset) + : offset(offset), module_id(module_id), type(type) {} + + WasmAddressType GetType() { return static_cast(type); } + + operator lldb::addr_t() { return *(uint64_t *)this; } +}; + +static_assert(sizeof(wasm_addr_t) == 8, ""); + +/// 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(); + + llvm::StringRef GetPluginName() override; + + size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, + Status &error) override; + + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + /// Retrieve the current call stack from the WebAssembly remote process. + llvm::Expected> GetWasmCallStack(lldb::tid_t tid); + +protected: + std::shared_ptr + CreateThread(lldb::tid_t tid) override; + +private: + friend class UnwindWasm; + 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 diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp new file mode 100644 index 0000000000000..a6553ffffedaa --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// 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" + +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; +} + +llvm::Expected> ThreadWasm::GetWasmCallStack() { + if (ProcessSP process_sp = GetProcess()) { + ProcessWasm *wasm_process = static_cast(process_sp.get()); + return wasm_process->GetWasmCallStack(GetID()); + } + return llvm::createStringError("no process"); +} diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.h b/lldb/source/Plugins/Process/wasm/ThreadWasm.h new file mode 100644 index 0000000000000..1c90f58767bc8 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.h @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// 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. + llvm::Expected> GetWasmCallStack(); + +protected: + 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 0000000000000..99845dd2918d0 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp @@ -0,0 +1,85 @@ +//===----------------------------------------------------------------------===// +// +// 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 "ProcessWasm.h" +#include "ThreadWasm.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.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) { + // Wasm does not have a fixed set of registers but relies on a mechanism + // named local and global variables to store information such as the stack + // pointer. The only actual register is the PC. + 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(thread.get()); + ProcessWasm *wasm_process = + static_cast(thread->GetProcess().get()); + + return std::make_shared( + *gdb_thread, frame->GetConcreteFrameIndex(), + wasm_process->GetRegisterInfo(), m_frames[frame->GetFrameIndex()]); +} + +uint32_t UnwindWasm::DoGetFrameCount() { + if (m_unwind_complete) + return m_frames.size(); + + m_unwind_complete = true; + m_frames.clear(); + + ThreadWasm &wasm_thread = static_cast(GetThread()); + llvm::Expected> call_stack_pcs = + wasm_thread.GetWasmCallStack(); + if (!call_stack_pcs) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Unwind), call_stack_pcs.takeError(), + "Failed to get Wasm callstack: {0}"); + m_frames.clear(); + return 0; + } + + m_frames = *call_stack_pcs; + 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()) + return false; + + behaves_like_zeroth_frame = (frame_idx == 0); + cfa = 0; + pc = m_frames[frame_idx]; + return true; +} diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.h b/lldb/source/Plugins/Process/wasm/UnwindWasm.h new file mode 100644 index 0000000000000..ff5e06d23d960 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.h @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// 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_UNWINDWASM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_UNWINDWASM_H + +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Unwind.h" +#include + +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) {} + ~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 m_frames; + bool m_unwind_complete = false; + + UnwindWasm(const UnwindWasm &); + const UnwindWasm &operator=(const UnwindWasm &) = delete; +}; + +} // namespace wasm +} // namespace lldb_private + +#endif diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp index 8000cd07565ae..e9d9c8f0976b6 100644 --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -2076,6 +2076,13 @@ size_t Platform::GetSoftwareBreakpointTrapOpcode(Target &target, trap_opcode_size = sizeof(g_loongarch_opcode); } break; + case llvm::Triple::wasm32: { + // Unreachable (0x00) triggers an unconditional trap. + static const uint8_t g_wasm_opcode[] = {0x00}; + trap_opcode = g_wasm_opcode; + trap_opcode_size = sizeof(g_wasm_opcode); + } break; + default: return 0; } diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py index 733f40b5e1429..445f4222e2179 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py @@ -8,7 +8,6 @@ LLDB_INVALID_ADDRESS = lldb.LLDB_INVALID_ADDRESS load_address = 0x400000000 - def format_register_value(val): """ Encode each byte by two hex digits in little-endian order. @@ -35,6 +34,8 @@ def __init__(self, obj_path, module_name=""): def respond(self, packet): if packet[0:13] == "qRegisterInfo": return self.qRegisterInfo(packet[13:]) + if packet.startswith("qWasmCallStack"): + return self.qWasmCallStack() return MockGDBServerResponder.respond(self, packet) def qSupported(self, client_supported): @@ -47,7 +48,7 @@ def QEnableErrorStrings(self): return "" def qfThreadInfo(self): - return "OK" + return "m1," def qRegisterInfo(self, index): if index == 0: @@ -61,7 +62,7 @@ def qProcessInfo(self): ) def haltReason(self): - return "T05thread:1;" + return "T02thread:1;" def readRegister(self, register): return format_register_value(self.current_pc) @@ -89,6 +90,10 @@ def readMemory(self, addr, length): file.close() return result + def qWasmCallStack(self): + # Return two 64-bit addresses: 0x40000000000001B3, 0x40000000000001FE + return "b301000000000040fe01000000000040" + class TestWasm(GDBRemoteTestBase): @skipIfAsan @@ -104,7 +109,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(target, "wasm") lldbutil.expect_state_changes( self, self.dbg.GetListener(), process, [lldb.eStateStopped] ) @@ -174,7 +179,7 @@ def test_load_module_with_stripped_symbols_from_remote(self): ) target = self.dbg.CreateTarget("") - process = self.connect(target) + process = self.connect(target, "wasm") lldbutil.expect_state_changes( self, self.dbg.GetListener(), process, [lldb.eStateStopped] ) @@ -230,7 +235,7 @@ def test_load_module_from_file(self): self.server.responder = MyResponder(obj_path) target = self.dbg.CreateTarget("") - process = self.connect(target) + process = self.connect(target, "wasm") lldbutil.expect_state_changes( self, self.dbg.GetListener(), process, [lldb.eStateStopped] ) @@ -272,3 +277,14 @@ def test_load_module_from_file(self): self.assertEqual( LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target) ) + + thread = process.GetThreadAtIndex(0) + self.assertTrue(thread.IsValid()) + + frame = thread.GetFrameAtIndex(0) + self.assertTrue(frame.IsValid()) + self.assertEqual(frame.GetPC(), 0x40000000000001B3) + + frame = thread.GetFrameAtIndex(1) + self.assertTrue(frame.IsValid()) + self.assertEqual(frame.GetPC(), 0x40000000000001FE)