diff --git a/lldb/source/Plugins/CMakeLists.txt b/lldb/source/Plugins/CMakeLists.txt --- a/lldb/source/Plugins/CMakeLists.txt +++ b/lldb/source/Plugins/CMakeLists.txt @@ -30,6 +30,7 @@ # FIXME: ProcessWindowsCommon needs to be initialized after all other process # plugins but before ProcessGDBRemote. set(LLDB_PROCESS_WINDOWS_PLUGIN "") +set(LLDB_PROCESS_WASM_PLUGIN "") set(LLDB_PROCESS_GDB_PLUGIN "") foreach(p ${LLDB_ALL_PLUGINS}) @@ -41,6 +42,8 @@ set(LLDB_PROCESS_WINDOWS_PLUGIN "LLDB_PLUGIN(${pStripped})\n") elseif(${pStripped} STREQUAL "ProcessGDBRemote") set(LLDB_PROCESS_GDB_PLUGIN "LLDB_PLUGIN(${pStripped})\n") + elseif(${pStripped} STREQUAL "ProcessWasm") + set(LLDB_PROCESS_WASM_PLUGIN "LLDB_PLUGIN(WasmProcessGDBRemote)\n") else() set(LLDB_ENUM_PLUGINS "${LLDB_ENUM_PLUGINS}LLDB_PLUGIN(${pStripped})\n") endif() diff --git a/lldb/source/Plugins/Plugins.def.in b/lldb/source/Plugins/Plugins.def.in --- a/lldb/source/Plugins/Plugins.def.in +++ b/lldb/source/Plugins/Plugins.def.in @@ -31,6 +31,7 @@ @LLDB_ENUM_PLUGINS@ @LLDB_PROCESS_WINDOWS_PLUGIN@ +@LLDB_PROCESS_WASM_PLUGIN@ @LLDB_PROCESS_GDB_PLUGIN@ #undef LLDB_PLUGIN diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt --- a/lldb/source/Plugins/Process/CMakeLists.txt +++ b/lldb/source/Plugins/Process/CMakeLists.txt @@ -17,3 +17,4 @@ add_subdirectory(elf-core) add_subdirectory(mach-core) add_subdirectory(minidump) +add_subdirectory(wasm) diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h @@ -23,6 +23,11 @@ class StringExtractor; namespace lldb_private { + +namespace wasm { +class WasmProcessGDBRemote; +} + namespace process_gdb_remote { class ThreadGDBRemote; @@ -75,6 +80,7 @@ protected: friend class ThreadGDBRemote; + friend class wasm::WasmProcessGDBRemote; bool ReadRegisterBytes(const RegisterInfo *reg_info, DataExtractor &data); diff --git a/lldb/source/Plugins/Process/wasm/CMakeLists.txt b/lldb/source/Plugins/Process/wasm/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/CMakeLists.txt @@ -0,0 +1,11 @@ + +add_lldb_library(lldbPluginProcessWasm PLUGIN + ProcessWasm.cpp + UnwindWasm.cpp + + LINK_LIBS + lldbCore + ${LLDB_PLUGINS} + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.h b/lldb/source/Plugins/Process/wasm/ProcessWasm.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h @@ -0,0 +1,97 @@ +//===-- ProcessWasm.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H + +#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" +#include "lldb/Target/RegisterContext.h" + +namespace lldb_private { +namespace wasm { + +// Interface IWasmProcess provides the access to the Wasm program state +// retrieved from the Wasm engine. +struct IWasmProcess { + ~IWasmProcess() = default; + + virtual lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame, lldb::addr_t pc) = 0; + + virtual bool GetWasmLocal(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size) = 0; + + virtual bool GetWasmGlobal(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size) = 0; + + virtual bool GetWasmStackValue(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size) = 0; + + virtual bool WasmReadMemory(int frame_index, lldb::addr_t addr, void *buf, + size_t buffer_size) = 0; + + virtual bool GetWasmCallStack(std::vector &call_stack_pcs) = 0; +}; + +class WasmProcessGDBRemote : public process_gdb_remote::ProcessGDBRemote, + public IWasmProcess { +public: + WasmProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); + + ~WasmProcessGDBRemote() override; + + static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file_path); + + static void Initialize(); + + static void DebuggerInitialize(Debugger &debugger); + + static void Terminate(); + + static ConstString GetPluginNameStatic(); + + static const char *GetPluginDescriptionStatic(); + + // PluginInterface protocol + ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + + // Process + size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, + Status &error) override; + + // IWasmProcess + lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame, + lldb::addr_t address) override; + + bool GetWasmLocal(int frame_index, int index, void *buf, size_t buffer_size, + size_t &size) override; + + bool GetWasmGlobal(int frame_index, int index, void *buf, size_t buffer_size, + size_t &size) override; + + bool GetWasmStackValue(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size) override; + + bool WasmReadMemory(int frame_index, lldb::addr_t addr, void *buf, + size_t buffer_size) override; + + bool GetWasmCallStack(std::vector &call_stack_pcs) override; + +private: + DISALLOW_COPY_AND_ASSIGN(WasmProcessGDBRemote); +}; + +} // namespace wasm +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp @@ -0,0 +1,119 @@ +//===-- ProcessWasm.cpp ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ProcessWasm.h" +#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h" +#include "lldb/Core/PluginManager.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private::wasm; + +LLDB_PLUGIN_DEFINE(WasmProcessGDBRemote) + +// ProcessGDBRemote constructor +WasmProcessGDBRemote::WasmProcessGDBRemote(lldb::TargetSP target_sp, + ListenerSP listener_sp) + : ProcessGDBRemote(target_sp, listener_sp) {} + +// Destructor +WasmProcessGDBRemote::~WasmProcessGDBRemote() {} + +void WasmProcessGDBRemote::Initialize() { + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); + }); +} + +void WasmProcessGDBRemote::DebuggerInitialize(Debugger &debugger) { + ProcessGDBRemote::DebuggerInitialize(debugger); +} + +// PluginInterface +ConstString WasmProcessGDBRemote::GetPluginName() { + return GetPluginNameStatic(); +} + +uint32_t WasmProcessGDBRemote::GetPluginVersion() { return 1; } + +ConstString WasmProcessGDBRemote::GetPluginNameStatic() { + static ConstString g_name("wasm"); + return g_name; +} + +const char *WasmProcessGDBRemote::GetPluginDescriptionStatic() { + return "GDB Remote protocol based WebAssembly debugging plug-in."; +} + +void WasmProcessGDBRemote::Terminate() { + PluginManager::UnregisterPlugin(WasmProcessGDBRemote::CreateInstance); +} + +lldb::ProcessSP +WasmProcessGDBRemote::CreateInstance(lldb::TargetSP target_sp, + ListenerSP listener_sp, + const FileSpec *crash_file_path) { + lldb::ProcessSP process_sp; + if (crash_file_path == nullptr) + process_sp = std::make_shared(target_sp, listener_sp); + return process_sp; +} + +size_t WasmProcessGDBRemote::ReadMemory(lldb::addr_t vm_addr, void *buf, + size_t size, Status &error) { + // TODO(paolosev): Implement in GDBRemoteComunicationClient + return false; +} + +lldb::RegisterContextSP +WasmProcessGDBRemote::CreateRegisterContextForFrame(StackFrame *frame, + lldb::addr_t pc) { + ThreadGDBRemote *gdb_thread = + static_cast(frame->CalculateThread().get()); + std::shared_ptr reg_ctx_sp = + std::make_shared( + *gdb_thread, frame->GetFrameIndex(), m_register_info, false, false); + reg_ctx_sp->PrivateSetRegisterValue(0, pc); + return reg_ctx_sp; +} + +bool WasmProcessGDBRemote::GetWasmLocal(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size) { + // TODO(paolosev): Implement in GDBRemoteComunicationClient + return false; +} + +bool WasmProcessGDBRemote::GetWasmGlobal(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size) { + // TODO(paolosev): Implement in GDBRemoteComunicationClient + return false; +} + +bool WasmProcessGDBRemote::GetWasmStackValue(int frame_index, int index, + void *buf, size_t buffer_size, + size_t &size) { + // TODO(paolosev): Implement in GDBRemoteComunicationClient + return false; +} + +bool WasmProcessGDBRemote::WasmReadMemory(int frame_index, lldb::addr_t addr, + void *buf, size_t buffer_size) { + // TODO(paolosev): Implement in GDBRemoteComunicationClient + return false; +} + +bool WasmProcessGDBRemote::GetWasmCallStack( + std::vector &call_stack_pcs) { + // TODO(paolosev): Implement in GDBRemoteComunicationClient + return false; +} diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.h b/lldb/source/Plugins/Process/wasm/UnwindWasm.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.h @@ -0,0 +1,49 @@ +//===-- UnwindWasm.h --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_UnwindWasm_h_ +#define lldb_UnwindWasm_h_ + +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Unwind.h" +#include + +namespace lldb_private { +namespace wasm { + +class UnwindWasm : public lldb_private::Unwind { +public: + UnwindWasm(lldb_private::Thread &thread) + : Unwind(thread), m_frames(), m_unwind_complete(false) {} + ~UnwindWasm() override = default; + +protected: + void DoClear() override { + m_frames.clear(); + m_unwind_complete = false; + } + + uint32_t DoGetFrameCount() override; + + bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, + lldb::addr_t &pc, + bool &behaves_like_zeroth_frame) override; + lldb::RegisterContextSP + DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + +private: + std::vector m_frames; + bool m_unwind_complete; + + DISALLOW_COPY_AND_ASSIGN(UnwindWasm); +}; + +} // namespace wasm +} // namespace lldb_private + +#endif // lldb_UnwindWasm_h_ diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp @@ -0,0 +1,57 @@ +//===-- UnwindWasm.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "UnwindWasm.h" +#include "Plugins/Process/wasm/ProcessWasm.h" + +using namespace lldb; +using namespace lldb_private; +using namespace process_gdb_remote; +using namespace wasm; + +lldb::RegisterContextSP +UnwindWasm::DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) { + if (m_frames.size() <= frame->GetFrameIndex()) { + return lldb::RegisterContextSP(); + } + + IWasmProcess *wasm_process = + static_cast(frame->CalculateProcess().get()); + return wasm_process->CreateRegisterContextForFrame( + frame, m_frames[frame->GetFrameIndex()]); +} + +uint32_t UnwindWasm::DoGetFrameCount() { + if (!m_unwind_complete) { + m_unwind_complete = true; + m_frames.clear(); + + IWasmProcess *wasm_process = + static_cast(GetThread().GetProcess().get()); + if (wasm_process) + if (!wasm_process->GetWasmCallStack(m_frames)) + m_frames.clear(); + } + return m_frames.size(); +} + +bool UnwindWasm::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, + lldb::addr_t &pc, + bool &behaves_like_zeroth_frame) { + if (m_frames.size() == 0) { + DoGetFrameCount(); + } + + if (frame_idx < m_frames.size()) { + behaves_like_zeroth_frame = (frame_idx == 0); + cfa = 0; + pc = m_frames[frame_idx]; + return true; + } + return false; +} \ No newline at end of file diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "lldb/Target/Thread.h" +#include "Plugins/Process/wasm/UnwindWasm.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/FormatEntity.h" @@ -1853,8 +1854,13 @@ } Unwind &Thread::GetUnwinder() { - if (!m_unwinder_up) - m_unwinder_up.reset(new UnwindLLDB(*this)); + if (!m_unwinder_up) { + if (CalculateTarget()->GetArchitecture().GetMachine() == + llvm::Triple::wasm32) + m_unwinder_up.reset(new wasm::UnwindWasm(*this)); + else + m_unwinder_up.reset(new UnwindLLDB(*this)); + } return *m_unwinder_up; }