168 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "ProcessWasm.h"10#include "ThreadWasm.h"11#include "lldb/Core/Module.h"12#include "lldb/Core/PluginManager.h"13#include "lldb/Core/Value.h"14#include "lldb/Utility/DataBufferHeap.h"15 16#include "lldb/Target/UnixSignals.h"17 18using namespace lldb;19using namespace lldb_private;20using namespace lldb_private::process_gdb_remote;21using namespace lldb_private::wasm;22 23LLDB_PLUGIN_DEFINE(ProcessWasm)24 25ProcessWasm::ProcessWasm(lldb::TargetSP target_sp, ListenerSP listener_sp)26 : ProcessGDBRemote(target_sp, listener_sp) {27 assert(target_sp);28 // Wasm doesn't have any Unix-like signals as a platform concept, but pretend29 // like it does to appease LLDB.30 m_unix_signals_sp = UnixSignals::Create(target_sp->GetArchitecture());31}32 33void ProcessWasm::Initialize() {34 static llvm::once_flag g_once_flag;35 36 llvm::call_once(g_once_flag, []() {37 PluginManager::RegisterPlugin(GetPluginNameStatic(),38 GetPluginDescriptionStatic(), CreateInstance,39 DebuggerInitialize);40 });41}42 43void ProcessWasm::DebuggerInitialize(Debugger &debugger) {44 ProcessGDBRemote::DebuggerInitialize(debugger);45}46 47llvm::StringRef ProcessWasm::GetPluginName() { return GetPluginNameStatic(); }48 49llvm::StringRef ProcessWasm::GetPluginNameStatic() { return "wasm"; }50 51llvm::StringRef ProcessWasm::GetPluginDescriptionStatic() {52 return "GDB Remote protocol based WebAssembly debugging plug-in.";53}54 55void ProcessWasm::Terminate() {56 PluginManager::UnregisterPlugin(ProcessWasm::CreateInstance);57}58 59lldb::ProcessSP ProcessWasm::CreateInstance(lldb::TargetSP target_sp,60 ListenerSP listener_sp,61 const FileSpec *crash_file_path,62 bool can_connect) {63 if (crash_file_path == nullptr)64 return std::make_shared<ProcessWasm>(target_sp, listener_sp);65 return {};66}67 68bool ProcessWasm::CanDebug(lldb::TargetSP target_sp,69 bool plugin_specified_by_name) {70 if (plugin_specified_by_name)71 return true;72 73 if (Module *exe_module = target_sp->GetExecutableModulePointer()) {74 if (ObjectFile *exe_objfile = exe_module->GetObjectFile())75 return exe_objfile->GetArchitecture().GetMachine() ==76 llvm::Triple::wasm32;77 }78 79 // However, if there is no wasm module, we return false, otherwise,80 // we might use ProcessWasm to attach gdb remote.81 return false;82}83 84std::shared_ptr<ThreadGDBRemote> ProcessWasm::CreateThread(lldb::tid_t tid) {85 return std::make_shared<ThreadWasm>(*this, tid);86}87 88size_t ProcessWasm::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,89 Status &error) {90 wasm_addr_t wasm_addr(vm_addr);91 92 switch (wasm_addr.GetType()) {93 case WasmAddressType::Memory:94 case WasmAddressType::Object:95 return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error);96 case WasmAddressType::Invalid:97 error.FromErrorStringWithFormat(98 "Wasm read failed for invalid address 0x%" PRIx64, vm_addr);99 return 0;100 }101 llvm_unreachable("Fully covered switch above");102}103 104llvm::Expected<std::vector<lldb::addr_t>>105ProcessWasm::GetWasmCallStack(lldb::tid_t tid) {106 StreamString packet;107 packet.Printf("qWasmCallStack:");108 packet.Printf("%" PRIx64, tid);109 110 StringExtractorGDBRemote response;111 if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=112 GDBRemoteCommunication::PacketResult::Success)113 return llvm::createStringError("failed to send qWasmCallStack");114 115 if (!response.IsNormalResponse())116 return llvm::createStringError("failed to get response for qWasmCallStack");117 118 WritableDataBufferSP data_buffer_sp =119 std::make_shared<DataBufferHeap>(response.GetStringRef().size() / 2, 0);120 const size_t bytes = response.GetHexBytes(data_buffer_sp->GetData(), '\xcc');121 if (bytes == 0 || bytes % sizeof(uint64_t) != 0)122 return llvm::createStringError("invalid response for qWasmCallStack");123 124 // To match the Wasm specification, the addresses are encoded in little endian125 // byte order.126 DataExtractor data(data_buffer_sp, lldb::eByteOrderLittle,127 GetAddressByteSize());128 lldb::offset_t offset = 0;129 std::vector<lldb::addr_t> call_stack_pcs;130 while (offset < bytes)131 call_stack_pcs.push_back(data.GetU64(&offset));132 133 return call_stack_pcs;134}135 136llvm::Expected<lldb::DataBufferSP>137ProcessWasm::GetWasmVariable(WasmVirtualRegisterKinds kind, int frame_index,138 int index) {139 StreamString packet;140 switch (kind) {141 case eWasmTagLocal:142 packet.Printf("qWasmLocal:");143 break;144 case eWasmTagGlobal:145 packet.Printf("qWasmGlobal:");146 break;147 case eWasmTagOperandStack:148 packet.PutCString("qWasmStackValue:");149 break;150 case eWasmTagNotAWasmLocation:151 return llvm::createStringError("not a Wasm location");152 }153 packet.Printf("%d;%d", frame_index, index);154 155 StringExtractorGDBRemote response;156 if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=157 GDBRemoteCommunication::PacketResult::Success)158 return llvm::createStringError("failed to send Wasm variable");159 160 if (!response.IsNormalResponse())161 return llvm::createStringError("failed to get response for Wasm variable");162 163 WritableDataBufferSP buffer_sp(164 new DataBufferHeap(response.GetStringRef().size() / 2, 0));165 response.GetHexBytes(buffer_sp->GetData(), '\xcc');166 return buffer_sp;167}168