LLDB mainline
ProcessWasm.cpp
Go to the documentation of this file.
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"
13#include "lldb/Core/Value.h"
15#include "llvm/Support/ErrorExtras.h"
16#include <cstring>
17
19
20using namespace lldb;
21using namespace lldb_private;
23using namespace lldb_private::wasm;
24
26
28 : ProcessGDBRemote(target_sp, listener_sp) {
29 assert(target_sp);
30 // Wasm doesn't have any Unix-like signals as a platform concept, but pretend
31 // like it does to appease LLDB.
32 m_unix_signals_sp = UnixSignals::Create(target_sp->GetArchitecture());
33 // FIXME: LLVM's RuntimeDyld doesn't support the Wasm object format, so we
34 // can't JIT expressions for this target.
35 SetCanJIT(false);
36}
37
43
47
48llvm::StringRef ProcessWasm::GetPluginName() { return GetPluginNameStatic(); }
49
51 return "GDB Remote protocol based WebAssembly debugging plug-in.";
52}
53
57
59 ListenerSP listener_sp,
60 const FileSpec *crash_file_path,
61 bool can_connect) {
62 if (crash_file_path == nullptr)
63 return std::make_shared<ProcessWasm>(target_sp, listener_sp);
64 return {};
65}
66
68 bool plugin_specified_by_name) {
69 if (plugin_specified_by_name)
70 return true;
71
72 if (Module *exe_module = target_sp->GetExecutableModulePointer()) {
73 if (ObjectFile *exe_objfile = exe_module->GetObjectFile())
74 return exe_objfile->GetArchitecture().GetTriple().isWasm();
75 }
76
77 // However, if there is no wasm module, we return false, otherwise,
78 // we might use ProcessWasm to attach gdb remote.
79 return false;
80}
81
82std::shared_ptr<ThreadGDBRemote> ProcessWasm::CreateThread(lldb::tid_t tid) {
83 if (!GetTarget().GetArchitecture().GetTriple().isWasm())
85
86 return std::make_shared<ThreadWasm>(*this, tid);
87}
88
89static size_t CopyGlobal(llvm::Expected<lldb::DataBufferSP> global, void *buf,
90 size_t size, Status &error) {
91 if (!global) {
92 error = Status::FromError(global.takeError());
93 return 0;
94 }
95
96 // A global comes back whole. Reading more than it holds would have to come
97 // from somewhere else, and the next index is not adjacent storage.
98 const size_t global_size = (*global)->GetByteSize();
99 if (size > global_size) {
101 "Wasm global read failed: requested {0} bytes from a {1}-byte global",
102 size, global_size);
103 return 0;
104 }
105
106 std::memcpy(buf, (*global)->GetBytes(), size);
107 return size;
108}
109
110size_t ProcessWasm::ReadGlobal(uint32_t module_id, uint32_t index, void *buf,
111 size_t size, Status &error) {
112 if (CanNameInstance(module_id))
113 return CopyGlobal(GetWasmGlobalForModule(module_id, index), buf, size,
114 error);
115
116 // Looking for a frame drives the unwinder, so only pay for it where the
117 // instance cannot be named.
118 llvm::Expected<uint32_t> frame_index = GetFallbackFrameIndex(module_id);
119 if (!frame_index) {
120 error = Status::FromError(frame_index.takeError());
121 return 0;
122 }
123
124 return CopyGlobal(GetWasmGlobalForFrame(*frame_index, index), buf, size,
125 error);
126}
127
128llvm::Expected<uint32_t>
130 if (module_id == kWasmInvalidModuleID)
131 return llvm::createStringError(
132 "the global belongs to no known module instance");
133
134 // Use the real thread list in case an OS plugin is providing threads.
135 if (ThreadSP thread = m_thread_list_real.GetSelectedThread()) {
136 ThreadWasm &wasm_thread = static_cast<ThreadWasm &>(*thread);
137 // A frame can only stand in for the module the stub reports it executing.
138 for (uint32_t frame_index = 0;; ++frame_index) {
139 lldb::addr_t pc = wasm_thread.GetConcreteFramePC(frame_index);
141 break;
142 if (GetWasmModuleID(pc) == module_id)
143 return frame_index;
144 }
145 }
146
147 return llvm::createStringErrorV(
148 "the Wasm stub can only read a global through a frame, and no frame is "
149 "executing module {0:x}",
150 module_id);
151}
152
154 wasm_addr_t wasm_addr(process_addr.GetValue());
155
156 switch (wasm_addr.GetType()) {
159 return ProcessGDBRemote::ShouldUseMemoryCache(process_addr);
162 // A global's address holds an index, not an offset into addressable
163 // storage, so the cache's aligned reads would name a different global.
164 return false;
165 }
166 llvm_unreachable("Fully covered switch above!");
167}
168
169size_t ProcessWasm::DoReadMemory(const ProcessAddress &process_addr, void *buf,
170 size_t size, Status &error) {
171 lldb::addr_t vm_addr = process_addr.GetValue();
172 wasm_addr_t wasm_addr(vm_addr);
173
174 switch (wasm_addr.GetType()) {
177 return ProcessGDBRemote::DoReadMemory(vm_addr, buf, size, error);
179 return ReadGlobal(wasm_addr.GetModuleID(), wasm_addr.GetOffset(), buf, size,
180 error);
182 break;
183 }
184
186 "Wasm read failed for invalid address {0:x} (type = {1:x}, module = "
187 "{2:x}, offset = {3:x})",
188 vm_addr, wasm_addr.GetType(), wasm_addr.GetModuleID(),
189 wasm_addr.GetOffset());
190 return 0;
191}
192
193llvm::Expected<std::vector<lldb::addr_t>>
195 StreamString packet;
196 packet.Printf("qWasmCallStack:");
197 packet.Printf("%" PRIx64, tid);
198
200 if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=
202 return llvm::createStringError("failed to send qWasmCallStack");
203
204 if (!response.IsNormalResponse())
205 return llvm::createStringError("failed to get response for qWasmCallStack");
206
207 WritableDataBufferSP data_buffer_sp =
208 std::make_shared<DataBufferHeap>(response.GetStringRef().size() / 2, 0);
209 const size_t bytes = response.GetHexBytes(data_buffer_sp->GetData(), '\xcc');
210 if (bytes == 0 || bytes % sizeof(uint64_t) != 0)
211 return llvm::createStringError("invalid response for qWasmCallStack");
212
213 // To match the Wasm specification, the addresses are encoded in little endian
214 // byte order.
215 DataExtractor data(data_buffer_sp, lldb::eByteOrderLittle,
217 lldb::offset_t offset = 0;
218 std::vector<lldb::addr_t> call_stack_pcs;
219 while (offset < bytes)
220 call_stack_pcs.push_back(data.GetU64(&offset));
221
222 return call_stack_pcs;
223}
224
225llvm::Expected<lldb::DataBufferSP>
226ProcessWasm::SendWasmValueQuery(llvm::StringRef packet) {
228 if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response) !=
230 return llvm::createStringErrorV("failed to send {0}", packet);
231
232 if (!response.IsNormalResponse())
233 return llvm::createStringErrorV("failed to get response for {0}", packet);
234
235 WritableDataBufferSP buffer_sp(
236 new DataBufferHeap(response.GetStringRef().size() / 2, 0));
237 response.GetHexBytes(buffer_sp->GetData(), '\xcc');
238 return buffer_sp;
239}
240
241llvm::Expected<lldb::DataBufferSP>
243 uint32_t frame_index, uint32_t index) {
244 switch (kind) {
245 case eWasmTagLocal:
246 return SendWasmValueQuery(
247 llvm::formatv("qWasmLocal:{0};{1}", frame_index, index).str());
249 return SendWasmValueQuery(
250 llvm::formatv("qWasmStackValue:{0};{1}", frame_index, index).str());
251 case eWasmTagGlobal:
252 return llvm::createStringError("a Wasm global does not belong to a frame");
254 return llvm::createStringError("not a Wasm location");
255 }
256 llvm_unreachable("unhandled Wasm virtual register kind");
257}
258
259llvm::Expected<lldb::DataBufferSP>
260ProcessWasm::GetWasmGlobalForModule(uint32_t module_id, uint32_t index) {
261 return SendWasmValueQuery(
262 llvm::formatv("qWasmGlobal:{0};instance:{1};", index, module_id).str());
263}
264
265llvm::Expected<lldb::DataBufferSP>
266ProcessWasm::GetWasmGlobalForFrame(uint32_t frame_index, uint32_t index) {
267 return SendWasmValueQuery(
268 llvm::formatv("qWasmGlobal:{0};{1}", frame_index, index).str());
269}
270
271bool ProcessWasm::CanNameInstance(uint32_t module_id) {
272 return module_id != kWasmInvalidModuleID &&
273 m_gdb_comm.GetWasmInstanceSupported();
274}
static llvm::raw_ostream & error(Stream &strm)
#define LLDB_PLUGIN_DEFINE(PluginName)
static size_t CopyGlobal(llvm::Expected< lldb::DataBufferSP > global, void *buf, size_t size, Status &error)
size_t GetHexBytes(llvm::MutableArrayRef< uint8_t > dest, uint8_t fail_fill_value)
llvm::StringRef GetStringRef() const
A subclass of DataBuffer that stores a data buffer on the heap.
An data extractor class.
uint64_t GetU64(lldb::offset_t *offset_ptr) const
Extract a uint64_t value from *offset_ptr.
A file utility class.
Definition FileSpec.h:56
A class that describes an executable image and its associated object and symbol files.
Definition Module.h:92
A plug-in interface definition class for object file parsers.
Definition ObjectFile.h:46
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback)
static bool UnregisterPlugin(ABICreateInstance create_callback)
An address in a process, qualified by an address space.
lldb::addr_t GetValue() const
void SetCanJIT(bool can_jit)
Sets whether executing JIT-compiled code in this process is possible.
Definition Process.cpp:2774
lldb::UnixSignalsSP m_unix_signals_sp
Definition Process.h:3554
virtual bool ShouldUseMemoryCache(const ProcessAddress &process_addr)
Whether ReadMemory should serve reads of process_addr out of the memory cache.
Definition Process.h:3025
ThreadList m_thread_list_real
The threads for this process as are known to the protocol we are debugging with.
Definition Process.h:3512
uint32_t GetAddressByteSize() const
Definition Process.cpp:3973
friend class Debugger
Definition Process.h:371
Target & GetTarget()
Get the target object pointer for this module.
Definition Process.h:1270
An error handling class.
Definition Status.h:118
static Status static Status FromErrorStringWithFormatv(const char *format, Args &&...args)
Definition Status.h:151
static Status FromError(llvm::Error error)
Avoid using this in new code. Migrate APIs to llvm::Expected instead.
Definition Status.cpp:136
llvm::StringRef GetString() const
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition Stream.cpp:134
static lldb::UnixSignalsSP Create(const ArchSpec &arch)
virtual std::shared_ptr< ThreadGDBRemote > CreateThread(lldb::tid_t tid)
size_t DoReadMemory(const ProcessAddress &process_addr, void *buf, size_t size, Status &error) override
Actually do the reading of memory from a process.
ProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
ProcessWasm provides the access to the Wasm program state retrieved from the Wasm engine.
Definition ProcessWasm.h:21
llvm::Expected< uint32_t > GetFallbackFrameIndex(uint32_t module_id)
The frame to read a global of module_id through, for a stub that cannot be told which instance to rea...
size_t ReadGlobal(uint32_t module_id, uint32_t index, void *buf, size_t size, Status &error)
Read a WebAssembly global of the module instance module_id names, through whichever scope that stub c...
llvm::Expected< lldb::DataBufferSP > GetWasmGlobalForModule(uint32_t module_id, uint32_t index)
Query the value of the global at index in the global index space of the module instance module_id nam...
llvm::Expected< lldb::DataBufferSP > GetWasmVariable(WasmVirtualRegisterKinds kind, uint32_t frame_index, uint32_t index)
Query the value of a frame-scoped WebAssembly variable, which is a local or a value on the operand st...
bool ShouldUseMemoryCache(const ProcessAddress &process_addr) override
Whether ReadMemory should serve reads of process_addr out of the memory cache.
llvm::StringRef GetPluginName() override
std::shared_ptr< process_gdb_remote::ThreadGDBRemote > CreateThread(lldb::tid_t tid) override
llvm::Expected< lldb::DataBufferSP > GetWasmGlobalForFrame(uint32_t frame_index, uint32_t index)
Query the value of a WebAssembly global through the frame at frame_index, which reaches only the glob...
bool CanNameInstance(uint32_t module_id)
Whether the instance holding a global can be named to the stub, which needs both a valid id to name i...
static llvm::StringRef GetPluginNameStatic()
Definition ProcessWasm.h:35
bool CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override
Check if a plug-in instance can debug the file in module.
static void DebuggerInitialize(Debugger &debugger)
static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec *crash_file_path, bool can_connect)
llvm::Expected< std::vector< lldb::addr_t > > GetWasmCallStack(lldb::tid_t tid)
Retrieve the current call stack from the WebAssembly remote process.
llvm::Expected< lldb::DataBufferSP > SendWasmValueQuery(llvm::StringRef packet)
Ask the WebAssembly stub for a single value, which comes back as the hex-encoded bytes of the whole v...
size_t DoReadMemory(const ProcessAddress &vm_addr, void *buf, size_t size, Status &error) override
Actually do the reading of memory from a process.
ProcessWasm(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
static llvm::StringRef GetPluginDescriptionStatic()
lldb::addr_t GetConcreteFramePC(uint32_t concrete_frame_idx)
Return the program counter the Wasm unwinder recorded for the given concrete frame index,...
#define LLDB_INVALID_ADDRESS
static constexpr uint32_t kWasmInvalidModuleID
A value that names no module.
Definition WasmAddress.h:62
uint32_t GetWasmModuleID(lldb::addr_t addr)
The module an address belongs to, or kWasmInvalidModuleID for an invalid address.
Definition WasmAddress.h:94
A class that represents a running process on the host machine.
std::shared_ptr< lldb_private::Thread > ThreadSP
uint64_t offset_t
Definition lldb-types.h:86
std::shared_ptr< lldb_private::Process > ProcessSP
std::shared_ptr< lldb_private::Listener > ListenerSP
std::shared_ptr< lldb_private::WritableDataBuffer > WritableDataBufferSP
uint64_t addr_t
Definition lldb-types.h:80
std::shared_ptr< lldb_private::Target > TargetSP
uint64_t tid_t
Definition lldb-types.h:85
For the purpose of debugging, we can represent all these separated 32-bit address spaces with a singl...
Definition WasmAddress.h:70
WasmAddressType GetType() const
Definition WasmAddress.h:83