1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
//===----------------------------------------------------------------------===//
//
// 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<ProcessWasm>(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<ThreadGDBRemote> ProcessWasm::CreateThread(lldb::tid_t tid) {
return std::make_shared<ThreadWasm>(*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_unreachable("Fully covered switch above");
}
llvm::Expected<std::vector<lldb::addr_t>>
ProcessWasm::GetWasmCallStack(lldb::tid_t tid) {
StreamString packet;
packet.Printf("qWasmCallStack:");
packet.Printf("%" PRIx64, 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<DataBufferHeap>(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<lldb::addr_t> call_stack_pcs;
while (offset < bytes)
call_stack_pcs.push_back(data.GetU64(&offset));
return call_stack_pcs;
}
llvm::Expected<lldb::DataBufferSP>
ProcessWasm::GetWasmVariable(WasmVirtualRegisterKinds kind, int frame_index,
int index) {
StreamString packet;
switch (kind) {
case eWasmTagLocal:
packet.Printf("qWasmLocal:");
break;
case eWasmTagGlobal:
packet.Printf("qWasmGlobal:");
break;
case eWasmTagOperandStack:
packet.PutCString("qWasmStackValue:");
break;
case eWasmTagNotAWasmLocation:
return llvm::createStringError("not a Wasm location");
}
packet.Printf("%d;%d", frame_index, index);
StringExtractorGDBRemote response;
if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=
GDBRemoteCommunication::PacketResult::Success)
return llvm::createStringError("failed to send Wasm variable");
if (!response.IsNormalResponse())
return llvm::createStringError("failed to get response for Wasm variable");
WritableDataBufferSP buffer_sp(
new DataBufferHeap(response.GetStringRef().size() / 2, 0));
response.GetHexBytes(buffer_sp->GetData(), '\xcc');
return buffer_sp;
}
|