| 
 | 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-exception  | 
 | 6 | +//  | 
 | 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 | + | 
 | 18 | +using namespace lldb;  | 
 | 19 | +using namespace lldb_private;  | 
 | 20 | +using namespace lldb_private::process_gdb_remote;  | 
 | 21 | +using namespace lldb_private::wasm;  | 
 | 22 | + | 
 | 23 | +LLDB_PLUGIN_DEFINE(ProcessWasm)  | 
 | 24 | + | 
 | 25 | +ProcessWasm::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 pretend  | 
 | 29 | +  // like it does to appease LLDB.  | 
 | 30 | +  m_unix_signals_sp = UnixSignals::Create(target_sp->GetArchitecture());  | 
 | 31 | +}  | 
 | 32 | + | 
 | 33 | +void 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 | + | 
 | 43 | +void ProcessWasm::DebuggerInitialize(Debugger &debugger) {  | 
 | 44 | +  ProcessGDBRemote::DebuggerInitialize(debugger);  | 
 | 45 | +}  | 
 | 46 | + | 
 | 47 | +llvm::StringRef ProcessWasm::GetPluginName() { return GetPluginNameStatic(); }  | 
 | 48 | + | 
 | 49 | +llvm::StringRef ProcessWasm::GetPluginNameStatic() { return "wasm"; }  | 
 | 50 | + | 
 | 51 | +llvm::StringRef ProcessWasm::GetPluginDescriptionStatic() {  | 
 | 52 | +  return "GDB Remote protocol based WebAssembly debugging plug-in.";  | 
 | 53 | +}  | 
 | 54 | + | 
 | 55 | +void ProcessWasm::Terminate() {  | 
 | 56 | +  PluginManager::UnregisterPlugin(ProcessWasm::CreateInstance);  | 
 | 57 | +}  | 
 | 58 | + | 
 | 59 | +lldb::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 | + | 
 | 68 | +bool 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 | + | 
 | 84 | +std::shared_ptr<ThreadGDBRemote> ProcessWasm::CreateThread(lldb::tid_t tid) {  | 
 | 85 | +  return std::make_shared<ThreadWasm>(*this, tid);  | 
 | 86 | +}  | 
 | 87 | + | 
 | 88 | +size_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 | +}  | 
 | 102 | + | 
 | 103 | +llvm::Expected<std::vector<lldb::addr_t>>  | 
 | 104 | +ProcessWasm::GetWasmCallStack(lldb::tid_t tid) {  | 
 | 105 | +  StreamString packet;  | 
 | 106 | +  packet.Printf("qWasmCallStack:");  | 
 | 107 | +  packet.Printf("%llx", tid);  | 
 | 108 | + | 
 | 109 | +  StringExtractorGDBRemote response;  | 
 | 110 | +  if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=  | 
 | 111 | +      GDBRemoteCommunication::PacketResult::Success)  | 
 | 112 | +    return llvm::createStringError("failed to send qWasmCallStack");  | 
 | 113 | + | 
 | 114 | +  if (!response.IsNormalResponse())  | 
 | 115 | +    return llvm::createStringError("failed to get response for qWasmCallStack");  | 
 | 116 | + | 
 | 117 | +  WritableDataBufferSP data_buffer_sp =  | 
 | 118 | +      std::make_shared<DataBufferHeap>(response.GetStringRef().size() / 2, 0);  | 
 | 119 | +  const size_t bytes = response.GetHexBytes(data_buffer_sp->GetData(), '\xcc');  | 
 | 120 | +  if (bytes == 0 || bytes % sizeof(uint64_t) != 0)  | 
 | 121 | +    return llvm::createStringError("invalid response for qWasmCallStack");  | 
 | 122 | + | 
 | 123 | +  // To match the Wasm specification, the addresses are encoded in little endian  | 
 | 124 | +  // byte order.  | 
 | 125 | +  DataExtractor data(data_buffer_sp, lldb::eByteOrderLittle,  | 
 | 126 | +                     GetAddressByteSize());  | 
 | 127 | +  lldb::offset_t offset = 0;  | 
 | 128 | +  std::vector<lldb::addr_t> call_stack_pcs;  | 
 | 129 | +  while (offset < bytes)  | 
 | 130 | +    call_stack_pcs.push_back(data.GetU64(&offset));  | 
 | 131 | + | 
 | 132 | +  return call_stack_pcs;  | 
 | 133 | +}  | 
0 commit comments