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