v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
wasm-module-debug.cc
Go to the documentation of this file.
1// Copyright 2020 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include "src/api/api-inl.h"
8#include "src/api/api.h"
11#include "src/objects/script.h"
13#include "src/wasm/wasm-debug.h"
14#include "src/wasm/wasm-value.h"
15
16namespace v8 {
17namespace internal {
18namespace wasm {
19namespace gdb_server {
20
22 Local<debug::WasmScript> wasm_script) {
23 DCHECK_EQ(Script::Type::kWasm, Utils::OpenHandle(*wasm_script)->type());
24
26 wasm_script_ = Global<debug::WasmScript>(isolate, wasm_script);
27}
28
29std::string WasmModuleDebug::GetModuleName() const {
32 std::string module_name;
33 if (wasm_script->Name().ToLocal(&name)) {
34 module_name = *(v8::String::Utf8Value(isolate_, name));
35 }
36 return module_name;
37}
38
41 Handle<Script> script = Utils::OpenHandle(*wasm_script);
42
43 Handle<WeakArrayList> weak_instance_list(script->wasm_weak_instance_list(),
44 GetIsolate());
45 if (weak_instance_list->length() > 0) {
46 Tagged<MaybeObject> maybe_instance = weak_instance_list->Get(0);
47 if (maybe_instance.IsWeak()) {
50 GetIsolate());
51 return instance;
52 }
53 }
55}
56
58 int index = offset;
59 while (module_bytes[index] & 0x80) index++;
60 return index + 1 - offset;
61}
62
63int ReturnPc(const NativeModule* native_module, int pc) {
64 base::Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
65 uint8_t opcode = wire_bytes[pc];
66 switch (opcode) {
67 case kExprCallFunction: {
68 // skip opcode
69 pc++;
70 // skip function index
71 return pc + GetLEB128Size(wire_bytes, pc);
72 }
73 case kExprCallIndirect: {
74 // skip opcode
75 pc++;
76 // skip signature index
77 pc += GetLEB128Size(wire_bytes, pc);
78 // skip table index
79 return pc + GetLEB128Size(wire_bytes, pc);
80 }
81 default:
83 }
84}
85
86// static
87std::vector<wasm_addr_t> WasmModuleDebug::GetCallStack(
88 uint32_t debug_context_id, Isolate* isolate) {
89 std::vector<wasm_addr_t> call_stack;
90 for (StackFrameIterator frame_it(isolate); !frame_it.done();
91 frame_it.Advance()) {
92 StackFrame* const frame = frame_it.frame();
93 switch (frame->type()) {
94 case StackFrame::JAVASCRIPT_BUILTIN_CONTINUATION:
95 case StackFrame::JAVASCRIPT_BUILTIN_CONTINUATION_WITH_CATCH:
96 case StackFrame::INTERPRETED:
97 case StackFrame::BASELINE:
98 case StackFrame::MAGLEV:
99 case StackFrame::TURBOFAN_JS:
100 case StackFrame::BUILTIN:
101 case StackFrame::WASM: {
102 // A standard frame may include many summarized frames, due to inlining.
103 FrameSummaries summaries = CommonFrame::cast(frame)->Summarize();
104 for (size_t i = summaries.size(); i-- != 0;) {
105 int offset = 0;
107
108 auto& summary = summaries.frames[i];
109 if (summary.IsJavaScript()) {
110 FrameSummary::JavaScriptFrameSummary const& javascript =
111 summary.AsJavaScript();
112 offset = javascript.code_offset();
113 script = Cast<Script>(javascript.script());
114 } else if (summary.IsWasm()) {
115 FrameSummary::WasmFrameSummary const& wasm = summary.AsWasm();
116 offset = GetWasmFunctionOffset(wasm.wasm_instance()->module(),
117 wasm.function_index()) +
118 wasm.code_offset();
119 script = wasm.script();
120
121 bool zeroth_frame = call_stack.empty();
122 if (!zeroth_frame) {
123 const NativeModule* native_module =
124 wasm.wasm_instance()->module_object().native_module();
125 offset = ReturnPc(native_module, offset);
126 }
127 }
128
129 if (offset > 0) {
130 call_stack.push_back(
131 {debug_context_id << 16 | script->id(), uint32_t(offset)});
132 }
133 }
134 break;
135 }
136
137 case StackFrame::BUILTIN_EXIT:
138 default:
139 // ignore the frame.
140 break;
141 }
142 }
143 if (call_stack.empty()) call_stack.push_back({1, 0});
144 return call_stack;
145}
146
147// static
148std::vector<FrameSummary> WasmModuleDebug::FindWasmFrame(
149 DebuggableStackFrameIterator* frame_it, uint32_t* frame_index) {
150 while (!frame_it->done()) {
151 StackFrame* const frame = frame_it->frame();
152 switch (frame->type()) {
153 case StackFrame::JAVASCRIPT_BUILTIN_CONTINUATION:
154 case StackFrame::JAVASCRIPT_BUILTIN_CONTINUATION_WITH_CATCH:
155 case StackFrame::INTERPRETED:
156 case StackFrame::BASELINE:
157 case StackFrame::MAGLEV:
158 case StackFrame::TURBOFAN_JS:
159 case StackFrame::BUILTIN:
160 case StackFrame::WASM: {
161 // A standard frame may include many summarized frames, due to inlining.
162 FrameSummaries summaries = CommonFrame::cast(frame)->Summarize();
163 const size_t frame_count = summaries.size();
164 DCHECK_GT(frame_count, 0);
165
166 if (frame_count > *frame_index) {
167#if V8_ENABLE_DRUMBRAKE
168 if (frame_it->is_wasm() && !frame_it->is_wasm_interpreter_entry())
169#else // V8_ENABLE_DRUMBRAKE
170 if (frame_it->is_wasm())
171#endif // V8_ENABLE_DRUMBRAKE
172 return summaries.frames;
173 else
174 return {};
175 } else {
176 *frame_index -= frame_count;
177 frame_it->Advance();
178 }
179 break;
180 }
181
182 case StackFrame::BUILTIN_EXIT:
183 default:
184 // ignore the frame.
185 break;
186 }
187 }
188 return {};
189}
190
191// static
193 Isolate* isolate, uint32_t frame_index) {
194 DebuggableStackFrameIterator frame_it(isolate);
195 std::vector<FrameSummary> frames = FindWasmFrame(&frame_it, &frame_index);
196 if (frames.empty()) {
198 }
199
200 int reversed_index = static_cast<int>(frames.size() - 1 - frame_index);
201 const FrameSummary::WasmFrameSummary& summary =
202 frames[reversed_index].AsWasm();
203 return summary.wasm_instance();
204}
205
206// static
207bool WasmModuleDebug::GetWasmGlobal(Isolate* isolate, uint32_t frame_index,
208 uint32_t index, uint8_t* buffer,
209 uint32_t buffer_size, uint32_t* size) {
210 HandleScope handles(isolate);
211
212 Handle<WasmInstanceObject> instance = GetWasmInstance(isolate, frame_index);
213 if (!instance.is_null()) {
214 Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
215 const wasm::WasmModule* module = module_object->module();
216 if (index < module->globals.size()) {
217 wasm::WasmValue wasm_value =
218 WasmInstanceObject::GetGlobalValue(instance, module->globals[index]);
219 return GetWasmValue(wasm_value, buffer, buffer_size, size);
220 }
221 }
222 return false;
223}
224
225// static
226bool WasmModuleDebug::GetWasmLocal(Isolate* isolate, uint32_t frame_index,
227 uint32_t index, uint8_t* buffer,
228 uint32_t buffer_size, uint32_t* size) {
229 HandleScope handles(isolate);
230
231 DebuggableStackFrameIterator frame_it(isolate);
232 std::vector<FrameSummary> frames = FindWasmFrame(&frame_it, &frame_index);
233 if (frames.empty()) {
234 return false;
235 }
236
237 int reversed_index = static_cast<int>(frames.size() - 1 - frame_index);
238 const FrameSummary& summary = frames[reversed_index];
239 if (summary.IsWasm()) {
240 Handle<WasmInstanceObject> instance = summary.AsWasm().wasm_instance();
241 if (!instance.is_null()) {
242 Handle<WasmModuleObject> module_object(instance->module_object(),
243 isolate);
244 wasm::NativeModule* native_module = module_object->native_module();
245 DebugInfo* debug_info = native_module->GetDebugInfo();
246 if (static_cast<uint32_t>(
247 debug_info->GetNumLocals(frame_it.frame()->pc())) > index) {
248 wasm::WasmValue wasm_value = debug_info->GetLocalValue(
249 index, frame_it.frame()->pc(), frame_it.frame()->fp(),
250 frame_it.frame()->callee_fp());
251 return GetWasmValue(wasm_value, buffer, buffer_size, size);
252 }
253 }
254 }
255 return false;
256}
257
258// static
259bool WasmModuleDebug::GetWasmStackValue(Isolate* isolate, uint32_t frame_index,
260 uint32_t index, uint8_t* buffer,
261 uint32_t buffer_size, uint32_t* size) {
262 HandleScope handles(isolate);
263
264 DebuggableStackFrameIterator frame_it(isolate);
265 std::vector<FrameSummary> frames = FindWasmFrame(&frame_it, &frame_index);
266 if (frames.empty()) {
267 return false;
268 }
269
270 int reversed_index = static_cast<int>(frames.size() - 1 - frame_index);
271 const FrameSummary& summary = frames[reversed_index];
272 if (summary.IsWasm()) {
273 Handle<WasmInstanceObject> instance = summary.AsWasm().wasm_instance();
274 if (!instance.is_null()) {
275 Handle<WasmModuleObject> module_object(instance->module_object(),
276 isolate);
277 wasm::NativeModule* native_module = module_object->native_module();
278 DebugInfo* debug_info = native_module->GetDebugInfo();
279 if (static_cast<uint32_t>(
280 debug_info->GetStackDepth(frame_it.frame()->pc())) > index) {
281 WasmValue wasm_value = debug_info->GetStackValue(
282 index, frame_it.frame()->pc(), frame_it.frame()->fp(),
283 frame_it.frame()->callee_fp());
284 return GetWasmValue(wasm_value, buffer, buffer_size, size);
285 }
286 }
287 }
288 return false;
289}
290
292 uint8_t* buffer, uint32_t size) {
293 HandleScope handles(isolate);
294
295 uint32_t bytes_read = 0;
297 if (!instance.is_null()) {
298 uint8_t* mem_start = instance->memory_start();
299 size_t mem_size = instance->memory_size();
300 if (static_cast<uint64_t>(offset) + size <= mem_size) {
301 memcpy(buffer, mem_start + offset, size);
302 bytes_read = size;
303 } else if (offset < mem_size) {
304 bytes_read = static_cast<uint32_t>(mem_size) - offset;
305 memcpy(buffer, mem_start + offset, bytes_read);
306 }
307 }
308 return bytes_read;
309}
310
311uint32_t WasmModuleDebug::GetWasmData(Isolate* isolate, uint32_t offset,
312 uint8_t* buffer, uint32_t size) {
313 HandleScope handles(isolate);
314
315 uint32_t bytes_read = 0;
317 if (!instance.is_null()) {
318 Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
319 const wasm::WasmModule* module = module_object->module();
320 if (!module->data_segments.empty()) {
321 const WasmDataSegment& segment = module->data_segments[0];
322 uint32_t data_offset = EvalUint32InitExpr(instance, segment.dest_addr);
323 offset += data_offset;
324
325 uint8_t* mem_start = instance->memory_start();
326 size_t mem_size = instance->memory_size();
327 if (static_cast<uint64_t>(offset) + size <= mem_size) {
328 memcpy(buffer, mem_start + offset, size);
329 bytes_read = size;
330 } else if (offset < mem_size) {
331 bytes_read = static_cast<uint32_t>(mem_size) - offset;
332 memcpy(buffer, mem_start + offset, bytes_read);
333 }
334 }
335 }
336 return bytes_read;
337}
338
340 uint8_t* buffer, uint32_t size) {
341 uint32_t bytes_read = 0;
342 // Any instance will work.
344 if (!instance.is_null()) {
345 Handle<WasmModuleObject> module_object(instance->module_object(),
346 GetIsolate());
347 wasm::NativeModule* native_module = module_object->native_module();
348 const wasm::ModuleWireBytes wire_bytes(native_module->wire_bytes());
349 uint32_t offset = wasm_addr.Offset();
350 if (offset < wire_bytes.length()) {
351 uint32_t module_size = static_cast<uint32_t>(wire_bytes.length());
352 bytes_read = module_size - offset >= size ? size : module_size - offset;
353 memcpy(buffer, wire_bytes.start() + offset, bytes_read);
354 }
355 }
356 return bytes_read;
357}
358
359bool WasmModuleDebug::AddBreakpoint(uint32_t offset, int* breakpoint_id) {
361 Handle<Script> script = Utils::OpenHandle(*wasm_script);
362 Handle<String> condition = GetIsolate()->factory()->empty_string();
363 int breakpoint_address = static_cast<int>(offset);
365 script, condition, &breakpoint_address, breakpoint_id);
366}
367
368void WasmModuleDebug::RemoveBreakpoint(uint32_t offset, int breakpoint_id) {
370 Handle<Script> script = Utils::OpenHandle(*wasm_script);
371 GetIsolate()->debug()->RemoveBreakpointForWasmScript(script, breakpoint_id);
372}
373
375 i::Isolate* isolate = GetIsolate();
376 DebugScope debug_scope(isolate->debug());
377 debug::PrepareStep(reinterpret_cast<v8::Isolate*>(isolate),
379}
380
381template <typename T>
382bool StoreValue(const T& value, uint8_t* buffer, uint32_t buffer_size,
383 uint32_t* size) {
384 *size = sizeof(value);
385 if (*size > buffer_size) return false;
386 memcpy(buffer, &value, *size);
387 return true;
388}
389
390// static
392 uint8_t* buffer, uint32_t buffer_size,
393 uint32_t* size) {
394 switch (wasm_value.type().kind()) {
395 case wasm::kI32:
396 return StoreValue(wasm_value.to_i32(), buffer, buffer_size, size);
397 case wasm::kI64:
398 return StoreValue(wasm_value.to_i64(), buffer, buffer_size, size);
399 case wasm::kF32:
400 return StoreValue(wasm_value.to_f32(), buffer, buffer_size, size);
401 case wasm::kF64:
402 return StoreValue(wasm_value.to_f64(), buffer, buffer_size, size);
403 case wasm::kS128:
404 return StoreValue(wasm_value.to_s128(), buffer, buffer_size, size);
405 case wasm::kRef:
406 case wasm::kRefNull:
407 case wasm::kVoid:
408 case wasm::kBottom:
409 // Not supported
410 return false;
411 }
412}
413
414} // namespace gdb_server
415} // namespace wasm
416} // namespace internal
417} // namespace v8
V8_INLINE Local< T > Get(Isolate *isolate) const
static v8::internal::Handle< To > OpenHandle(v8::Local< From > handle)
Definition api.h:274
virtual FrameSummaries Summarize() const
Definition frames.cc:1508
static CommonFrame * cast(StackFrame *frame)
Definition frames.h:662
bool SetBreakPointForScript(Handle< Script > script, DirectHandle< String > condition, int *source_position, int *id)
Definition debug.cc:997
V8_INLINE bool is_null() const
Definition handles.h:69
v8::internal::Factory * factory()
Definition isolate.h:1527
Debug * debug() const
Definition isolate.h:1474
Address callee_fp() const
Definition frames.h:298
virtual Type type() const =0
Address pc() const
Definition frames-inl.h:78
Address fp() const
Definition frames.h:297
constexpr bool IsWeak() const
Tagged< HeapObject > GetHeapObjectAssumeWeak() const
WasmValue GetLocalValue(int local, Address pc, Address fp, Address debug_break_fp, Isolate *isolate)
WasmValue GetStackValue(int index, Address pc, Address fp, Address debug_break_fp, Isolate *isolate)
int GetStackDepth(Address pc, Isolate *isolate)
int GetNumLocals(Address pc, Isolate *isolate)
base::Vector< const uint8_t > wire_bytes() const
constexpr ValueKind kind() const
Definition value-type.h:631
CanonicalValueType type() const
Definition wasm-value.h:91
static std::vector< wasm_addr_t > GetCallStack(uint32_t debug_context_id, Isolate *isolate)
Handle< WasmInstanceObject > GetFirstWasmInstance()
static Handle< WasmInstanceObject > GetWasmInstance(Isolate *isolate, uint32_t frame_index)
static bool GetWasmStackValue(Isolate *isolate, uint32_t frame_index, uint32_t index, uint8_t *buffer, uint32_t buffer_size, uint32_t *size)
static std::vector< FrameSummary > FindWasmFrame(DebuggableStackFrameIterator *frame_it, uint32_t *frame_index)
void RemoveBreakpoint(uint32_t offset, int breakpoint_id)
static bool GetWasmLocal(Isolate *isolate, uint32_t frame_index, uint32_t index, uint8_t *buffer, uint32_t buffer_size, uint32_t *size)
uint32_t GetWasmMemory(Isolate *isolate, uint32_t offset, uint8_t *buffer, uint32_t size)
uint32_t GetWasmModuleBytes(wasm_addr_t wasm_addr, uint8_t *buffer, uint32_t size)
uint32_t GetWasmData(Isolate *isolate, uint32_t offset, uint8_t *buffer, uint32_t size)
static bool GetWasmValue(const wasm::WasmValue &wasm_value, uint8_t *buffer, uint32_t buffer_size, uint32_t *size)
WasmModuleDebug(v8::Isolate *isolate, Local< debug::WasmScript > script)
static bool GetWasmGlobal(Isolate *isolate, uint32_t frame_index, uint32_t index, uint8_t *buffer, uint32_t buffer_size, uint32_t *size)
bool AddBreakpoint(uint32_t offset, int *breakpoint_id)
Isolate * isolate
int32_t offset
void PrepareStep(Isolate *v8_isolate, StepAction action)
int GetLEB128Size(base::Vector< const uint8_t > module_bytes, int offset)
int ReturnPc(const NativeModule *native_module, int pc)
bool StoreValue(const T &value, uint8_t *buffer, uint32_t buffer_size, uint32_t *size)
int GetWasmFunctionOffset(const WasmModule *module, uint32_t func_index)
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in name
Definition flags.cc:2086
return value
Definition map-inl.h:893
kInterpreterTrampolineOffset script
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
Definition c-api.cc:87
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
std::vector< FrameSummary > frames
Definition frames.h:631
const uint8_t * start() const
std::vector< WasmGlobal > globals
std::vector< WasmDataSegment > data_segments