Line data Source code
1 : // Copyright 2016 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 :
5 : #include <unordered_map>
6 :
7 : #include "src/assembler-inl.h"
8 : #include "src/assert-scope.h"
9 : #include "src/base/optional.h"
10 : #include "src/compiler/wasm-compiler.h"
11 : #include "src/debug/debug-scopes.h"
12 : #include "src/debug/debug.h"
13 : #include "src/frames-inl.h"
14 : #include "src/heap/factory.h"
15 : #include "src/identity-map.h"
16 : #include "src/isolate.h"
17 : #include "src/wasm/module-decoder.h"
18 : #include "src/wasm/wasm-code-manager.h"
19 : #include "src/wasm/wasm-interpreter.h"
20 : #include "src/wasm/wasm-limits.h"
21 : #include "src/wasm/wasm-module.h"
22 : #include "src/wasm/wasm-objects-inl.h"
23 : #include "src/zone/accounting-allocator.h"
24 :
25 : namespace v8 {
26 : namespace internal {
27 : namespace wasm {
28 :
29 : namespace {
30 :
31 : template <bool internal, typename... Args>
32 456 : Handle<String> PrintFToOneByteString(Isolate* isolate, const char* format,
33 : Args... args) {
34 : // Maximum length of a formatted value name ("arg#%d", "local#%d",
35 : // "global#%d", i32 constants, i64 constants), including null character.
36 : static constexpr int kMaxStrLen = 21;
37 : EmbeddedVector<char, kMaxStrLen> value;
38 456 : int len = SNPrintF(value, format, args...);
39 912 : CHECK(len > 0 && len < value.length());
40 456 : Vector<uint8_t> name = Vector<uint8_t>::cast(value.SubVector(0, len));
41 : return internal
42 : ? isolate->factory()->InternalizeOneByteString(name)
43 500 : : isolate->factory()->NewStringFromOneByte(name).ToHandleChecked();
44 : }
45 :
46 792 : Handle<Object> WasmValueToValueObject(Isolate* isolate, WasmValue value) {
47 792 : switch (value.type()) {
48 : case kWasmI32:
49 644 : if (Smi::IsValid(value.to<int32_t>()))
50 644 : return handle(Smi::FromInt(value.to<int32_t>()), isolate);
51 0 : return PrintFToOneByteString<false>(isolate, "%d", value.to<int32_t>());
52 : case kWasmI64: {
53 : int64_t i64 = value.to<int64_t>();
54 68 : int32_t i32 = static_cast<int32_t>(i64);
55 92 : if (i32 == i64 && Smi::IsValid(i32))
56 24 : return handle(Smi::FromIntptr(i32), isolate);
57 44 : return PrintFToOneByteString<false>(isolate, "%" PRId64, i64);
58 : }
59 : case kWasmF32:
60 0 : return isolate->factory()->NewNumber(value.to<float>());
61 : case kWasmF64:
62 80 : return isolate->factory()->NewNumber(value.to<double>());
63 : default:
64 0 : UNIMPLEMENTED();
65 : return isolate->factory()->undefined_value();
66 : }
67 : }
68 :
69 592 : MaybeHandle<String> GetLocalName(Isolate* isolate,
70 : Handle<WasmDebugInfo> debug_info,
71 : int func_index, int local_index) {
72 : DCHECK_LE(0, func_index);
73 : DCHECK_LE(0, local_index);
74 592 : if (!debug_info->has_locals_names()) {
75 : Handle<WasmModuleObject> module_object(
76 : debug_info->wasm_instance()->module_object(), isolate);
77 20 : Handle<FixedArray> locals_names = DecodeLocalNames(isolate, module_object);
78 20 : debug_info->set_locals_names(*locals_names);
79 : }
80 :
81 : Handle<FixedArray> locals_names(debug_info->locals_names(), isolate);
82 832 : if (func_index >= locals_names->length() ||
83 : locals_names->get(func_index)->IsUndefined(isolate)) {
84 352 : return {};
85 : }
86 :
87 : Handle<FixedArray> func_locals_names(
88 : FixedArray::cast(locals_names->get(func_index)), isolate);
89 480 : if (local_index >= func_locals_names->length() ||
90 : func_locals_names->get(local_index)->IsUndefined(isolate)) {
91 60 : return {};
92 : }
93 180 : return handle(String::cast(func_locals_names->get(local_index)), isolate);
94 : }
95 :
96 : class InterpreterHandle {
97 : MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(InterpreterHandle);
98 : Isolate* isolate_;
99 : const WasmModule* module_;
100 : WasmInterpreter interpreter_;
101 : StepAction next_step_action_ = StepNone;
102 : int last_step_stack_depth_ = 0;
103 : std::unordered_map<Address, uint32_t> activations_;
104 :
105 34373 : uint32_t StartActivation(Address frame_pointer) {
106 34373 : WasmInterpreter::Thread* thread = interpreter_.GetThread(0);
107 34373 : uint32_t activation_id = thread->StartActivation();
108 : DCHECK_EQ(0, activations_.count(frame_pointer));
109 68746 : activations_.insert(std::make_pair(frame_pointer, activation_id));
110 34373 : return activation_id;
111 : }
112 :
113 34373 : void FinishActivation(Address frame_pointer, uint32_t activation_id) {
114 34373 : WasmInterpreter::Thread* thread = interpreter_.GetThread(0);
115 34373 : thread->FinishActivation(activation_id);
116 : DCHECK_EQ(1, activations_.count(frame_pointer));
117 : activations_.erase(frame_pointer);
118 34373 : }
119 :
120 5668 : std::pair<uint32_t, uint32_t> GetActivationFrameRange(
121 : WasmInterpreter::Thread* thread, Address frame_pointer) {
122 : DCHECK_EQ(1, activations_.count(frame_pointer));
123 5668 : uint32_t activation_id = activations_.find(frame_pointer)->second;
124 5668 : uint32_t num_activations = static_cast<uint32_t>(activations_.size() - 1);
125 5668 : uint32_t frame_base = thread->ActivationFrameBase(activation_id);
126 : uint32_t frame_limit = activation_id == num_activations
127 5564 : ? thread->GetFrameCount()
128 11232 : : thread->ActivationFrameBase(activation_id + 1);
129 : DCHECK_LE(frame_base, frame_limit);
130 : DCHECK_LE(frame_limit, thread->GetFrameCount());
131 5668 : return {frame_base, frame_limit};
132 : }
133 :
134 365686 : static ModuleWireBytes GetBytes(WasmDebugInfo debug_info) {
135 : // Return raw pointer into heap. The WasmInterpreter will make its own copy
136 : // of this data anyway, and there is no heap allocation in-between.
137 : NativeModule* native_module =
138 365686 : debug_info->wasm_instance()->module_object()->native_module();
139 365686 : return ModuleWireBytes{native_module->wire_bytes()};
140 : }
141 :
142 : public:
143 365686 : InterpreterHandle(Isolate* isolate, Handle<WasmDebugInfo> debug_info)
144 : : isolate_(isolate),
145 731372 : module_(debug_info->wasm_instance()->module_object()->module()),
146 731372 : interpreter_(isolate, module_, GetBytes(*debug_info),
147 1828430 : handle(debug_info->wasm_instance(), isolate)) {}
148 :
149 365686 : ~InterpreterHandle() { DCHECK_EQ(0, activations_.size()); }
150 :
151 423074 : WasmInterpreter* interpreter() { return &interpreter_; }
152 : const WasmModule* module() const { return module_; }
153 :
154 364 : void PrepareStep(StepAction step_action) {
155 364 : next_step_action_ = step_action;
156 364 : last_step_stack_depth_ = CurrentStackDepth();
157 364 : }
158 :
159 548 : void ClearStepping() { next_step_action_ = StepNone; }
160 :
161 : int CurrentStackDepth() {
162 : DCHECK_EQ(1, interpreter()->GetThreadCount());
163 364 : return interpreter()->GetThread(0)->GetFrameCount();
164 : }
165 :
166 : // Returns true if exited regularly, false if a trap/exception occurred and
167 : // was not handled inside this activation. In the latter case, a pending
168 : // exception will have been set on the isolate.
169 34373 : bool Execute(Handle<WasmInstanceObject> instance_object,
170 : Address frame_pointer, uint32_t func_index, Address arg_buffer) {
171 : DCHECK_GE(module()->functions.size(), func_index);
172 68746 : FunctionSig* sig = module()->functions[func_index].sig;
173 : DCHECK_GE(kMaxInt, sig->parameter_count());
174 34373 : int num_params = static_cast<int>(sig->parameter_count());
175 : ScopedVector<WasmValue> wasm_args(num_params);
176 : Address arg_buf_ptr = arg_buffer;
177 144159 : for (int i = 0; i < num_params; ++i) {
178 : uint32_t param_size = static_cast<uint32_t>(
179 109786 : ValueTypes::ElementSizeInBytes(sig->GetParam(i)));
180 : #define CASE_ARG_TYPE(type, ctype) \
181 : case type: \
182 : DCHECK_EQ(param_size, sizeof(ctype)); \
183 : wasm_args[i] = WasmValue(ReadUnalignedValue<ctype>(arg_buf_ptr)); \
184 : break;
185 54893 : switch (sig->GetParam(i)) {
186 9857 : CASE_ARG_TYPE(kWasmI32, uint32_t)
187 15024 : CASE_ARG_TYPE(kWasmI64, uint64_t)
188 1396 : CASE_ARG_TYPE(kWasmF32, float)
189 28616 : CASE_ARG_TYPE(kWasmF64, double)
190 : #undef CASE_ARG_TYPE
191 : default:
192 0 : UNREACHABLE();
193 : }
194 54893 : arg_buf_ptr += param_size;
195 : }
196 :
197 34373 : uint32_t activation_id = StartActivation(frame_pointer);
198 :
199 34373 : WasmInterpreter::Thread* thread = interpreter_.GetThread(0);
200 34373 : thread->InitFrame(&module()->functions[func_index], wasm_args.start());
201 : bool finished = false;
202 67557 : while (!finished) {
203 : // TODO(clemensh): Add occasional StackChecks.
204 34945 : WasmInterpreter::State state = ContinueExecution(thread);
205 34945 : switch (state) {
206 : case WasmInterpreter::State::PAUSED:
207 556 : NotifyDebugEventListeners(thread);
208 : break;
209 : case WasmInterpreter::State::FINISHED:
210 : // Perfect, just break the switch and exit the loop.
211 : finished = true;
212 : break;
213 : case WasmInterpreter::State::TRAPPED: {
214 : MessageTemplate message_id =
215 780 : WasmOpcodes::TrapReasonToMessageId(thread->GetTrapReason());
216 : Handle<Object> exception =
217 780 : isolate_->factory()->NewWasmRuntimeError(message_id);
218 780 : auto result = thread->RaiseException(isolate_, exception);
219 780 : if (result == WasmInterpreter::Thread::HANDLED) break;
220 : // If no local handler was found, we fall-thru to {STOPPED}.
221 : DCHECK_EQ(WasmInterpreter::State::STOPPED, thread->state());
222 : V8_FALLTHROUGH;
223 : }
224 : case WasmInterpreter::State::STOPPED:
225 : // An exception happened, and the current activation was unwound
226 : // without hitting a local exception handler. All that remains to be
227 : // done is finish the activation and let the exception propagate.
228 : DCHECK_EQ(thread->ActivationFrameBase(activation_id),
229 : thread->GetFrameCount());
230 : DCHECK(isolate_->has_pending_exception());
231 1761 : FinishActivation(frame_pointer, activation_id);
232 : return false;
233 : // RUNNING should never occur here.
234 : case WasmInterpreter::State::RUNNING:
235 : default:
236 0 : UNREACHABLE();
237 : }
238 : }
239 :
240 : // Copy back the return value
241 : DCHECK_GE(kV8MaxWasmFunctionReturns, sig->return_count());
242 : // TODO(wasm): Handle multi-value returns.
243 : DCHECK_EQ(1, kV8MaxWasmFunctionReturns);
244 32612 : if (sig->return_count()) {
245 32396 : WasmValue ret_val = thread->GetReturnValue(0);
246 : #define CASE_RET_TYPE(type, ctype) \
247 : case type: \
248 : DCHECK_EQ(ValueTypes::ElementSizeInBytes(sig->GetReturn(0)), \
249 : sizeof(ctype)); \
250 : WriteUnalignedValue<ctype>(arg_buffer, ret_val.to<ctype>()); \
251 : break;
252 32396 : switch (sig->GetReturn(0)) {
253 : CASE_RET_TYPE(kWasmI32, uint32_t)
254 : CASE_RET_TYPE(kWasmI64, uint64_t)
255 : CASE_RET_TYPE(kWasmF32, float)
256 : CASE_RET_TYPE(kWasmF64, double)
257 : #undef CASE_RET_TYPE
258 : default:
259 0 : UNREACHABLE();
260 : }
261 : }
262 :
263 32612 : FinishActivation(frame_pointer, activation_id);
264 :
265 : return true;
266 : }
267 :
268 34945 : WasmInterpreter::State ContinueExecution(WasmInterpreter::Thread* thread) {
269 34945 : switch (next_step_action_) {
270 : case StepNone:
271 34573 : return thread->Run();
272 : case StepIn:
273 : return thread->Step();
274 : case StepOut:
275 32 : thread->AddBreakFlags(WasmInterpreter::BreakFlag::AfterReturn);
276 32 : return thread->Run();
277 : case StepNext: {
278 108 : int stack_depth = thread->GetFrameCount();
279 108 : if (stack_depth == last_step_stack_depth_) return thread->Step();
280 8 : thread->AddBreakFlags(stack_depth > last_step_stack_depth_
281 : ? WasmInterpreter::BreakFlag::AfterReturn
282 8 : : WasmInterpreter::BreakFlag::AfterCall);
283 8 : return thread->Run();
284 : }
285 : default:
286 0 : UNREACHABLE();
287 : }
288 : }
289 :
290 556 : Handle<WasmInstanceObject> GetInstanceObject() {
291 556 : StackTraceFrameIterator it(isolate_);
292 : WasmInterpreterEntryFrame* frame =
293 : WasmInterpreterEntryFrame::cast(it.frame());
294 556 : Handle<WasmInstanceObject> instance_obj(frame->wasm_instance(), isolate_);
295 : // Check that this is indeed the instance which is connected to this
296 : // interpreter.
297 : DCHECK_EQ(this, Managed<InterpreterHandle>::cast(
298 : instance_obj->debug_info()->interpreter_handle())
299 : ->raw());
300 1112 : return instance_obj;
301 : }
302 :
303 556 : void NotifyDebugEventListeners(WasmInterpreter::Thread* thread) {
304 : // Enter the debugger.
305 892 : DebugScope debug_scope(isolate_->debug());
306 :
307 : // Check whether we hit a breakpoint.
308 556 : if (isolate_->debug()->break_points_active()) {
309 : Handle<WasmModuleObject> module_object(
310 1112 : GetInstanceObject()->module_object(), isolate_);
311 556 : int position = GetTopPosition(module_object);
312 : Handle<FixedArray> breakpoints;
313 1112 : if (WasmModuleObject::CheckBreakPoints(isolate_, module_object, position)
314 : .ToHandle(&breakpoints)) {
315 : // We hit one or several breakpoints. Clear stepping, notify the
316 : // listeners and return.
317 : ClearStepping();
318 212 : isolate_->debug()->OnDebugBreak(breakpoints);
319 212 : return;
320 : }
321 : }
322 :
323 : // We did not hit a breakpoint, so maybe this pause is related to stepping.
324 : bool hit_step = false;
325 344 : switch (next_step_action_) {
326 : case StepNone:
327 : break;
328 : case StepIn:
329 : hit_step = true;
330 216 : break;
331 : case StepOut:
332 24 : hit_step = thread->GetFrameCount() < last_step_stack_depth_;
333 24 : break;
334 : case StepNext: {
335 104 : hit_step = thread->GetFrameCount() == last_step_stack_depth_;
336 104 : break;
337 : }
338 : default:
339 0 : UNREACHABLE();
340 : }
341 344 : if (!hit_step) return;
342 : ClearStepping();
343 672 : isolate_->debug()->OnDebugBreak(isolate_->factory()->empty_fixed_array());
344 : }
345 :
346 556 : int GetTopPosition(Handle<WasmModuleObject> module_object) {
347 : DCHECK_EQ(1, interpreter()->GetThreadCount());
348 556 : WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
349 : DCHECK_LT(0, thread->GetFrameCount());
350 :
351 556 : auto frame = thread->GetFrame(thread->GetFrameCount() - 1);
352 1668 : return module_object->GetFunctionOffset(frame->function()->func_index) +
353 1112 : frame->pc();
354 : }
355 :
356 3916 : std::vector<std::pair<uint32_t, int>> GetInterpretedStack(
357 : Address frame_pointer) {
358 : DCHECK_EQ(1, interpreter()->GetThreadCount());
359 3916 : WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
360 :
361 : std::pair<uint32_t, uint32_t> frame_range =
362 3916 : GetActivationFrameRange(thread, frame_pointer);
363 :
364 : std::vector<std::pair<uint32_t, int>> stack;
365 3916 : stack.reserve(frame_range.second - frame_range.first);
366 1356884 : for (uint32_t fp = frame_range.first; fp < frame_range.second; ++fp) {
367 676484 : auto frame = thread->GetFrame(fp);
368 1352968 : stack.emplace_back(frame->function()->func_index, frame->pc());
369 : }
370 3916 : return stack;
371 : }
372 :
373 1752 : WasmInterpreter::FramePtr GetInterpretedFrame(Address frame_pointer,
374 : int idx) {
375 : DCHECK_EQ(1, interpreter()->GetThreadCount());
376 1752 : WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
377 :
378 : std::pair<uint32_t, uint32_t> frame_range =
379 1752 : GetActivationFrameRange(thread, frame_pointer);
380 : DCHECK_LE(0, idx);
381 : DCHECK_GT(frame_range.second - frame_range.first, idx);
382 :
383 1752 : return thread->GetFrame(frame_range.first + idx);
384 : }
385 :
386 : uint64_t NumInterpretedCalls() {
387 : DCHECK_EQ(1, interpreter()->GetThreadCount());
388 51568 : return interpreter()->GetThread(0)->NumInterpretedCalls();
389 : }
390 :
391 460 : Handle<JSObject> GetGlobalScopeObject(InterpretedFrame* frame,
392 : Handle<WasmDebugInfo> debug_info) {
393 460 : Isolate* isolate = isolate_;
394 : Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
395 :
396 : // TODO(clemensh): Add globals to the global scope.
397 : Handle<JSObject> global_scope_object =
398 460 : isolate_->factory()->NewJSObjectWithNullProto();
399 460 : if (instance->has_memory_object()) {
400 0 : Handle<String> name = isolate_->factory()->InternalizeOneByteString(
401 0 : StaticCharVector("memory"));
402 : Handle<JSArrayBuffer> memory_buffer(
403 0 : instance->memory_object()->array_buffer(), isolate_);
404 0 : Handle<JSTypedArray> uint8_array = isolate_->factory()->NewJSTypedArray(
405 0 : kExternalUint8Array, memory_buffer, 0, memory_buffer->byte_length());
406 0 : JSObject::SetOwnPropertyIgnoreAttributes(global_scope_object, name,
407 0 : uint8_array, NONE)
408 : .Assert();
409 : }
410 460 : return global_scope_object;
411 : }
412 :
413 460 : Handle<JSObject> GetLocalScopeObject(InterpretedFrame* frame,
414 : Handle<WasmDebugInfo> debug_info) {
415 460 : Isolate* isolate = isolate_;
416 :
417 : Handle<JSObject> local_scope_object =
418 460 : isolate_->factory()->NewJSObjectWithNullProto();
419 : // Fill parameters and locals.
420 460 : int num_params = frame->GetParameterCount();
421 460 : int num_locals = frame->GetLocalCount();
422 : DCHECK_LE(num_params, num_locals);
423 460 : if (num_locals > 0) {
424 : Handle<JSObject> locals_obj =
425 412 : isolate_->factory()->NewJSObjectWithNullProto();
426 : Handle<String> locals_name =
427 412 : isolate_->factory()->InternalizeOneByteString(
428 412 : StaticCharVector("locals"));
429 412 : JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, locals_name,
430 412 : locals_obj, NONE)
431 : .Assert();
432 1596 : for (int i = 0; i < num_locals; ++i) {
433 : MaybeHandle<String> name =
434 592 : GetLocalName(isolate, debug_info, frame->function()->func_index, i);
435 592 : if (name.is_null()) {
436 : // Parameters should come before locals in alphabetical ordering, so
437 : // we name them "args" here.
438 412 : const char* label = i < num_params ? "arg#%d" : "local#%d";
439 412 : name = PrintFToOneByteString<true>(isolate_, label, i);
440 : }
441 592 : WasmValue value = frame->GetLocalValue(i);
442 592 : Handle<Object> value_obj = WasmValueToValueObject(isolate_, value);
443 592 : JSObject::SetOwnPropertyIgnoreAttributes(
444 592 : locals_obj, name.ToHandleChecked(), value_obj, NONE)
445 : .Assert();
446 : }
447 : }
448 :
449 : // Fill stack values.
450 460 : int stack_count = frame->GetStackHeight();
451 : // Use an object without prototype instead of an Array, for nicer displaying
452 : // in DevTools. For Arrays, the length field and prototype is displayed,
453 : // which does not make too much sense here.
454 : Handle<JSObject> stack_obj =
455 460 : isolate_->factory()->NewJSObjectWithNullProto();
456 460 : Handle<String> stack_name = isolate_->factory()->InternalizeOneByteString(
457 460 : StaticCharVector("stack"));
458 460 : JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, stack_name,
459 460 : stack_obj, NONE)
460 : .Assert();
461 860 : for (int i = 0; i < stack_count; ++i) {
462 200 : WasmValue value = frame->GetStackValue(i);
463 200 : Handle<Object> value_obj = WasmValueToValueObject(isolate_, value);
464 200 : JSObject::SetOwnElementIgnoreAttributes(
465 200 : stack_obj, static_cast<uint32_t>(i), value_obj, NONE)
466 : .Assert();
467 : }
468 460 : return local_scope_object;
469 : }
470 : };
471 :
472 : } // namespace
473 :
474 : } // namespace wasm
475 :
476 : namespace {
477 :
478 34789 : wasm::InterpreterHandle* GetOrCreateInterpreterHandle(
479 : Isolate* isolate, Handle<WasmDebugInfo> debug_info) {
480 : Handle<Object> handle(debug_info->interpreter_handle(), isolate);
481 34789 : if (handle->IsUndefined(isolate)) {
482 : // Use the maximum stack size to estimate the maximum size of the
483 : // interpreter. The interpreter keeps its own stack internally, and the size
484 : // of the stack should dominate the overall size of the interpreter. We
485 : // multiply by '2' to account for the growing strategy for the backing store
486 : // of the stack.
487 912 : size_t interpreter_size = FLAG_stack_size * KB * 2;
488 : handle = Managed<wasm::InterpreterHandle>::Allocate(
489 912 : isolate, interpreter_size, isolate, debug_info);
490 912 : debug_info->set_interpreter_handle(*handle);
491 : }
492 :
493 34789 : return Handle<Managed<wasm::InterpreterHandle>>::cast(handle)->raw();
494 : }
495 :
496 : wasm::InterpreterHandle* GetInterpreterHandle(WasmDebugInfo debug_info) {
497 : Object handle_obj = debug_info->interpreter_handle();
498 : DCHECK(!handle_obj->IsUndefined());
499 : return Managed<wasm::InterpreterHandle>::cast(handle_obj)->raw();
500 : }
501 :
502 51568 : wasm::InterpreterHandle* GetInterpreterHandleOrNull(WasmDebugInfo debug_info) {
503 : Object handle_obj = debug_info->interpreter_handle();
504 51568 : if (handle_obj->IsUndefined()) return nullptr;
505 : return Managed<wasm::InterpreterHandle>::cast(handle_obj)->raw();
506 : }
507 :
508 272 : Handle<FixedArray> GetOrCreateInterpretedFunctions(
509 : Isolate* isolate, Handle<WasmDebugInfo> debug_info) {
510 : Handle<FixedArray> arr(debug_info->interpreted_functions(), isolate);
511 : int num_functions = debug_info->wasm_instance()
512 544 : ->module_object()
513 : ->native_module()
514 544 : ->num_functions();
515 272 : if (arr->length() == 0 && num_functions > 0) {
516 172 : arr = isolate->factory()->NewFixedArray(num_functions);
517 172 : debug_info->set_interpreted_functions(*arr);
518 : }
519 : DCHECK_EQ(num_functions, arr->length());
520 272 : return arr;
521 : }
522 :
523 : } // namespace
524 :
525 365710 : Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<WasmInstanceObject> instance) {
526 : DCHECK(!instance->has_debug_info());
527 : Factory* factory = instance->GetIsolate()->factory();
528 : Handle<WasmDebugInfo> debug_info = Handle<WasmDebugInfo>::cast(
529 365710 : factory->NewStruct(WASM_DEBUG_INFO_TYPE, AllocationType::kOld));
530 365710 : debug_info->set_wasm_instance(*instance);
531 365710 : debug_info->set_interpreted_functions(*factory->empty_fixed_array());
532 365710 : instance->set_debug_info(*debug_info);
533 365710 : return debug_info;
534 : }
535 :
536 364774 : wasm::WasmInterpreter* WasmDebugInfo::SetupForTesting(
537 : Handle<WasmInstanceObject> instance_obj) {
538 364774 : Handle<WasmDebugInfo> debug_info = WasmDebugInfo::New(instance_obj);
539 : Isolate* isolate = instance_obj->GetIsolate();
540 : // Use the maximum stack size to estimate the maximum size of the interpreter.
541 : // The interpreter keeps its own stack internally, and the size of the stack
542 : // should dominate the overall size of the interpreter. We multiply by '2' to
543 : // account for the growing strategy for the backing store of the stack.
544 364774 : size_t interpreter_size = FLAG_stack_size * KB * 2;
545 : auto interp_handle = Managed<wasm::InterpreterHandle>::Allocate(
546 364774 : isolate, interpreter_size, isolate, debug_info);
547 729548 : debug_info->set_interpreter_handle(*interp_handle);
548 364774 : return interp_handle->raw()->interpreter();
549 : }
550 :
551 144 : void WasmDebugInfo::SetBreakpoint(Handle<WasmDebugInfo> debug_info,
552 : int func_index, int offset) {
553 : Isolate* isolate = debug_info->GetIsolate();
554 144 : auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info);
555 144 : RedirectToInterpreter(debug_info, Vector<int>(&func_index, 1));
556 144 : const wasm::WasmFunction* func = &handle->module()->functions[func_index];
557 288 : handle->interpreter()->SetBreakpoint(func, offset, true);
558 144 : }
559 :
560 272 : void WasmDebugInfo::RedirectToInterpreter(Handle<WasmDebugInfo> debug_info,
561 : Vector<int> func_indexes) {
562 : Isolate* isolate = debug_info->GetIsolate();
563 : // Ensure that the interpreter is instantiated.
564 272 : GetOrCreateInterpreterHandle(isolate, debug_info);
565 : Handle<FixedArray> interpreted_functions =
566 272 : GetOrCreateInterpretedFunctions(isolate, debug_info);
567 : Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
568 : wasm::NativeModule* native_module =
569 272 : instance->module_object()->native_module();
570 272 : const wasm::WasmModule* module = instance->module();
571 :
572 : // We may modify the wasm jump table.
573 : wasm::NativeModuleModificationScope native_module_modification_scope(
574 544 : native_module);
575 :
576 816 : for (int func_index : func_indexes) {
577 : DCHECK_LE(0, func_index);
578 : DCHECK_GT(module->functions.size(), func_index);
579 360 : if (!interpreted_functions->get(func_index)->IsUndefined(isolate)) continue;
580 :
581 : wasm::WasmCompilationResult result = compiler::CompileWasmInterpreterEntry(
582 : isolate->wasm_engine(), native_module->enabled_features(), func_index,
583 736 : module->functions[func_index].sig);
584 : std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode(
585 : func_index, result.code_desc, result.frame_slot_count,
586 : result.tagged_parameter_slots, std::move(result.protected_instructions),
587 : std::move(result.source_positions), wasm::WasmCode::kInterpreterEntry,
588 736 : wasm::WasmCode::kOther);
589 : Address instruction_start = wasm_code->instruction_start();
590 184 : native_module->PublishCode(std::move(wasm_code));
591 :
592 : Handle<Foreign> foreign_holder =
593 184 : isolate->factory()->NewForeign(instruction_start, AllocationType::kOld);
594 368 : interpreted_functions->set(func_index, *foreign_holder);
595 : }
596 272 : }
597 :
598 364 : void WasmDebugInfo::PrepareStep(StepAction step_action) {
599 728 : GetInterpreterHandle(*this)->PrepareStep(step_action);
600 364 : }
601 :
602 : // static
603 34373 : bool WasmDebugInfo::RunInterpreter(Isolate* isolate,
604 : Handle<WasmDebugInfo> debug_info,
605 : Address frame_pointer, int func_index,
606 : Address arg_buffer) {
607 : DCHECK_LE(0, func_index);
608 34373 : auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info);
609 : Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
610 34373 : return handle->Execute(instance, frame_pointer,
611 34373 : static_cast<uint32_t>(func_index), arg_buffer);
612 : }
613 :
614 3916 : std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack(
615 : Address frame_pointer) {
616 3916 : return GetInterpreterHandle(*this)->GetInterpretedStack(frame_pointer);
617 : }
618 :
619 832 : wasm::WasmInterpreter::FramePtr WasmDebugInfo::GetInterpretedFrame(
620 : Address frame_pointer, int idx) {
621 832 : return GetInterpreterHandle(*this)->GetInterpretedFrame(frame_pointer, idx);
622 : }
623 :
624 51568 : uint64_t WasmDebugInfo::NumInterpretedCalls() {
625 51568 : auto* handle = GetInterpreterHandleOrNull(*this);
626 103136 : return handle ? handle->NumInterpretedCalls() : 0;
627 : }
628 :
629 : // static
630 460 : Handle<JSObject> WasmDebugInfo::GetGlobalScopeObject(
631 : Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) {
632 : auto* interp_handle = GetInterpreterHandle(*debug_info);
633 460 : auto frame = interp_handle->GetInterpretedFrame(frame_pointer, frame_index);
634 920 : return interp_handle->GetGlobalScopeObject(frame.get(), debug_info);
635 : }
636 :
637 : // static
638 460 : Handle<JSObject> WasmDebugInfo::GetLocalScopeObject(
639 : Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) {
640 : auto* interp_handle = GetInterpreterHandle(*debug_info);
641 460 : auto frame = interp_handle->GetInterpretedFrame(frame_pointer, frame_index);
642 920 : return interp_handle->GetLocalScopeObject(frame.get(), debug_info);
643 : }
644 :
645 : // static
646 6685 : Handle<JSFunction> WasmDebugInfo::GetCWasmEntry(
647 : Handle<WasmDebugInfo> debug_info, wasm::FunctionSig* sig) {
648 : Isolate* isolate = debug_info->GetIsolate();
649 : DCHECK_EQ(debug_info->has_c_wasm_entries(),
650 : debug_info->has_c_wasm_entry_map());
651 6685 : if (!debug_info->has_c_wasm_entries()) {
652 1360 : auto entries = isolate->factory()->NewFixedArray(4, AllocationType::kOld);
653 1360 : debug_info->set_c_wasm_entries(*entries);
654 : size_t map_size = 0; // size estimate not so important here.
655 1360 : auto managed_map = Managed<wasm::SignatureMap>::Allocate(isolate, map_size);
656 1360 : debug_info->set_c_wasm_entry_map(*managed_map);
657 : }
658 : Handle<FixedArray> entries(debug_info->c_wasm_entries(), isolate);
659 : wasm::SignatureMap* map = debug_info->c_wasm_entry_map()->raw();
660 6685 : int32_t index = map->Find(*sig);
661 6685 : if (index == -1) {
662 1376 : index = static_cast<int32_t>(map->FindOrInsert(*sig));
663 1376 : if (index == entries->length()) {
664 : entries = isolate->factory()->CopyFixedArrayAndGrow(
665 0 : entries, entries->length(), AllocationType::kOld);
666 0 : debug_info->set_c_wasm_entries(*entries);
667 : }
668 : DCHECK(entries->get(index)->IsUndefined(isolate));
669 : Handle<Code> new_entry_code =
670 2752 : compiler::CompileCWasmEntry(isolate, sig).ToHandleChecked();
671 : Handle<WasmExportedFunctionData> function_data =
672 : Handle<WasmExportedFunctionData>::cast(isolate->factory()->NewStruct(
673 1376 : WASM_EXPORTED_FUNCTION_DATA_TYPE, AllocationType::kOld));
674 1376 : function_data->set_wrapper_code(*new_entry_code);
675 1376 : function_data->set_instance(debug_info->wasm_instance());
676 : function_data->set_jump_table_offset(-1);
677 : function_data->set_function_index(-1);
678 : Handle<String> name = isolate->factory()->InternalizeOneByteString(
679 1376 : StaticCharVector("c-wasm-entry"));
680 : NewFunctionArgs args = NewFunctionArgs::ForWasm(
681 1376 : name, function_data, isolate->sloppy_function_map());
682 1376 : Handle<JSFunction> new_entry = isolate->factory()->NewFunction(args);
683 2752 : new_entry->set_context(debug_info->wasm_instance()->native_context());
684 : new_entry->shared()->set_internal_formal_parameter_count(
685 : compiler::CWasmEntryParameters::kNumParameters);
686 2752 : entries->set(index, *new_entry);
687 : }
688 6685 : return handle(JSFunction::cast(entries->get(index)), isolate);
689 : }
690 :
691 : } // namespace internal
692 120216 : } // namespace v8
|