aboutsummaryrefslogtreecommitdiff
path: root/src/wasm/wasm-debug.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm/wasm-debug.cc')
-rw-r--r--src/wasm/wasm-debug.cc530
1 files changed, 416 insertions, 114 deletions
diff --git a/src/wasm/wasm-debug.cc b/src/wasm/wasm-debug.cc
index 11c2ef8a..c00a4f1d 100644
--- a/src/wasm/wasm-debug.cc
+++ b/src/wasm/wasm-debug.cc
@@ -2,144 +2,446 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "src/assembler-inl.h"
#include "src/assert-scope.h"
+#include "src/compiler/wasm-compiler.h"
#include "src/debug/debug.h"
#include "src/factory.h"
+#include "src/frames-inl.h"
#include "src/isolate.h"
#include "src/wasm/module-decoder.h"
+#include "src/wasm/wasm-interpreter.h"
+#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
+#include "src/zone/accounting-allocator.h"
using namespace v8::internal;
using namespace v8::internal::wasm;
namespace {
-enum {
- kWasmDebugInfoWasmObj,
- kWasmDebugInfoWasmBytesHash,
- kWasmDebugInfoAsmJsOffsets,
- kWasmDebugInfoNumEntries
-};
+// Forward declaration.
+class InterpreterHandle;
+InterpreterHandle* GetInterpreterHandle(WasmDebugInfo* debug_info);
+
+class InterpreterHandle {
+ AccountingAllocator allocator_;
+ WasmInstance instance_;
+ WasmInterpreter interpreter_;
+ Isolate* isolate_;
+ StepAction next_step_action_ = StepNone;
+ int last_step_stack_depth_ = 0;
+
+ public:
+ // Initialize in the right order, using helper methods to make this possible.
+ // WasmInterpreter has to be allocated in place, since it is not movable.
+ InterpreterHandle(Isolate* isolate, WasmDebugInfo* debug_info)
+ : instance_(debug_info->wasm_instance()->compiled_module()->module()),
+ interpreter_(GetBytesEnv(&instance_, debug_info), &allocator_),
+ isolate_(isolate) {
+ if (debug_info->wasm_instance()->has_memory_buffer()) {
+ JSArrayBuffer* mem_buffer = debug_info->wasm_instance()->memory_buffer();
+ instance_.mem_start =
+ reinterpret_cast<byte*>(mem_buffer->backing_store());
+ CHECK(mem_buffer->byte_length()->ToUint32(&instance_.mem_size));
+ } else {
+ DCHECK_EQ(0, instance_.module->min_mem_pages);
+ instance_.mem_start = nullptr;
+ instance_.mem_size = 0;
+ }
+ }
+
+ static ModuleBytesEnv GetBytesEnv(WasmInstance* instance,
+ WasmDebugInfo* debug_info) {
+ // Return raw pointer into heap. The WasmInterpreter will make its own copy
+ // of this data anyway, and there is no heap allocation in-between.
+ SeqOneByteString* bytes_str =
+ debug_info->wasm_instance()->compiled_module()->module_bytes();
+ Vector<const byte> bytes(bytes_str->GetChars(), bytes_str->length());
+ return ModuleBytesEnv(instance->module, instance, bytes);
+ }
+
+ WasmInterpreter* interpreter() { return &interpreter_; }
+ const WasmModule* module() { return instance_.module; }
+
+ void PrepareStep(StepAction step_action) {
+ next_step_action_ = step_action;
+ last_step_stack_depth_ = CurrentStackDepth();
+ }
+
+ void ClearStepping() { next_step_action_ = StepNone; }
+
+ int CurrentStackDepth() {
+ DCHECK_EQ(1, interpreter()->GetThreadCount());
+ return interpreter()->GetThread(0)->GetFrameCount();
+ }
+
+ void Execute(uint32_t func_index, uint8_t* arg_buffer) {
+ DCHECK_GE(module()->functions.size(), func_index);
+ FunctionSig* sig = module()->functions[func_index].sig;
+ DCHECK_GE(kMaxInt, sig->parameter_count());
+ int num_params = static_cast<int>(sig->parameter_count());
+ ScopedVector<WasmVal> wasm_args(num_params);
+ uint8_t* arg_buf_ptr = arg_buffer;
+ for (int i = 0; i < num_params; ++i) {
+ int param_size = 1 << ElementSizeLog2Of(sig->GetParam(i));
+#define CASE_ARG_TYPE(type, ctype) \
+ case type: \
+ DCHECK_EQ(param_size, sizeof(ctype)); \
+ wasm_args[i] = WasmVal(*reinterpret_cast<ctype*>(arg_buf_ptr)); \
+ break;
+ switch (sig->GetParam(i)) {
+ CASE_ARG_TYPE(kWasmI32, uint32_t)
+ CASE_ARG_TYPE(kWasmI64, uint64_t)
+ CASE_ARG_TYPE(kWasmF32, float)
+ CASE_ARG_TYPE(kWasmF64, double)
+#undef CASE_ARG_TYPE
+ default:
+ UNREACHABLE();
+ }
+ arg_buf_ptr += RoundUpToMultipleOfPowOf2(param_size, 8);
+ }
+
+ WasmInterpreter::Thread* thread = interpreter_.GetThread(0);
+ // We do not support reentering an already running interpreter at the moment
+ // (like INTERPRETER -> JS -> WASM -> INTERPRETER).
+ DCHECK(thread->state() == WasmInterpreter::STOPPED ||
+ thread->state() == WasmInterpreter::FINISHED);
+ thread->Reset();
+ thread->PushFrame(&module()->functions[func_index], wasm_args.start());
+ bool finished = false;
+ while (!finished) {
+ // TODO(clemensh): Add occasional StackChecks.
+ WasmInterpreter::State state = ContinueExecution(thread);
+ switch (state) {
+ case WasmInterpreter::State::PAUSED:
+ NotifyDebugEventListeners(thread);
+ break;
+ case WasmInterpreter::State::FINISHED:
+ // Perfect, just break the switch and exit the loop.
+ finished = true;
+ break;
+ case WasmInterpreter::State::TRAPPED:
+ // TODO(clemensh): Generate appropriate JS exception.
+ UNIMPLEMENTED();
+ break;
+ // STOPPED and RUNNING should never occur here.
+ case WasmInterpreter::State::STOPPED:
+ case WasmInterpreter::State::RUNNING:
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ // Copy back the return value
+ DCHECK_GE(kV8MaxWasmFunctionReturns, sig->return_count());
+ // TODO(wasm): Handle multi-value returns.
+ DCHECK_EQ(1, kV8MaxWasmFunctionReturns);
+ if (sig->return_count()) {
+ WasmVal ret_val = thread->GetReturnValue(0);
+#define CASE_RET_TYPE(type, ctype) \
+ case type: \
+ DCHECK_EQ(1 << ElementSizeLog2Of(sig->GetReturn(0)), sizeof(ctype)); \
+ *reinterpret_cast<ctype*>(arg_buffer) = ret_val.to<ctype>(); \
+ break;
+ switch (sig->GetReturn(0)) {
+ CASE_RET_TYPE(kWasmI32, uint32_t)
+ CASE_RET_TYPE(kWasmI64, uint64_t)
+ CASE_RET_TYPE(kWasmF32, float)
+ CASE_RET_TYPE(kWasmF64, double)
+#undef CASE_RET_TYPE
+ default:
+ UNREACHABLE();
+ }
+ }
+ }
+
+ WasmInterpreter::State ContinueExecution(WasmInterpreter::Thread* thread) {
+ switch (next_step_action_) {
+ case StepNone:
+ return thread->Run();
+ case StepIn:
+ return thread->Step();
+ case StepOut:
+ thread->AddBreakFlags(WasmInterpreter::BreakFlag::AfterReturn);
+ return thread->Run();
+ case StepNext: {
+ int stack_depth = thread->GetFrameCount();
+ if (stack_depth == last_step_stack_depth_) return thread->Step();
+ thread->AddBreakFlags(stack_depth > last_step_stack_depth_
+ ? WasmInterpreter::BreakFlag::AfterReturn
+ : WasmInterpreter::BreakFlag::AfterCall);
+ return thread->Run();
+ }
+ default:
+ UNREACHABLE();
+ return WasmInterpreter::STOPPED;
+ }
+ }
+
+ Handle<WasmInstanceObject> GetInstanceObject() {
+ StackTraceFrameIterator it(isolate_);
+ WasmInterpreterEntryFrame* frame =
+ WasmInterpreterEntryFrame::cast(it.frame());
+ Handle<WasmInstanceObject> instance_obj(frame->wasm_instance(), isolate_);
+ DCHECK_EQ(this, GetInterpreterHandle(instance_obj->debug_info()));
+ return instance_obj;
+ }
+
+ void NotifyDebugEventListeners(WasmInterpreter::Thread* thread) {
+ // Enter the debugger.
+ DebugScope debug_scope(isolate_->debug());
+ if (debug_scope.failed()) return;
+
+ // Postpone interrupt during breakpoint processing.
+ PostponeInterruptsScope postpone(isolate_);
+
+ // Check whether we hit a breakpoint.
+ if (isolate_->debug()->break_points_active()) {
+ Handle<WasmCompiledModule> compiled_module(
+ GetInstanceObject()->compiled_module(), isolate_);
+ int position = GetTopPosition(compiled_module);
+ Handle<FixedArray> breakpoints;
+ if (compiled_module->CheckBreakPoints(position).ToHandle(&breakpoints)) {
+ // We hit one or several breakpoints. Clear stepping, notify the
+ // listeners and return.
+ ClearStepping();
+ Handle<Object> hit_breakpoints_js =
+ isolate_->factory()->NewJSArrayWithElements(breakpoints);
+ isolate_->debug()->OnDebugBreak(hit_breakpoints_js);
+ return;
+ }
+ }
+
+ // We did not hit a breakpoint, so maybe this pause is related to stepping.
+ bool hit_step = false;
+ switch (next_step_action_) {
+ case StepNone:
+ break;
+ case StepIn:
+ hit_step = true;
+ break;
+ case StepOut:
+ hit_step = thread->GetFrameCount() < last_step_stack_depth_;
+ break;
+ case StepNext: {
+ hit_step = thread->GetFrameCount() == last_step_stack_depth_;
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ if (!hit_step) return;
+ ClearStepping();
+ isolate_->debug()->OnDebugBreak(isolate_->factory()->undefined_value());
+ }
-// TODO(clemensh): Move asm.js offset tables to the compiled module.
-FixedArray *GetAsmJsOffsetTables(Handle<WasmDebugInfo> debug_info,
- Isolate *isolate) {
- Object *offset_tables = debug_info->get(kWasmDebugInfoAsmJsOffsets);
- if (!offset_tables->IsUndefined(isolate)) {
- return FixedArray::cast(offset_tables);
- }
-
- Handle<JSObject> wasm_instance(debug_info->wasm_instance(), isolate);
- Handle<WasmCompiledModule> compiled_module(GetCompiledModule(*wasm_instance),
- isolate);
- DCHECK(compiled_module->has_asm_js_offset_tables());
-
- AsmJsOffsetsResult asm_offsets;
- {
- Handle<ByteArray> asm_offset_tables =
- compiled_module->asm_js_offset_tables();
- DisallowHeapAllocation no_gc;
- const byte *bytes_start = asm_offset_tables->GetDataStartAddress();
- const byte *bytes_end = bytes_start + asm_offset_tables->length();
- asm_offsets = wasm::DecodeAsmJsOffsets(bytes_start, bytes_end);
- }
- // Wasm bytes must be valid and must contain asm.js offset table.
- DCHECK(asm_offsets.ok());
- DCHECK_GE(static_cast<size_t>(kMaxInt), asm_offsets.val.size());
- int num_functions = static_cast<int>(asm_offsets.val.size());
- DCHECK_EQ(
- wasm::GetNumberOfFunctions(handle(debug_info->wasm_instance())),
- static_cast<int>(num_functions +
- compiled_module->module()->num_imported_functions));
- Handle<FixedArray> all_tables =
- isolate->factory()->NewFixedArray(num_functions);
- debug_info->set(kWasmDebugInfoAsmJsOffsets, *all_tables);
- for (int func = 0; func < num_functions; ++func) {
- std::vector<std::pair<int, int>> &func_asm_offsets = asm_offsets.val[func];
- if (func_asm_offsets.empty()) continue;
- size_t array_size = 2 * kIntSize * func_asm_offsets.size();
- CHECK_LE(array_size, static_cast<size_t>(kMaxInt));
- ByteArray *arr =
- *isolate->factory()->NewByteArray(static_cast<int>(array_size));
- all_tables->set(func, arr);
- int idx = 0;
- for (std::pair<int, int> p : func_asm_offsets) {
- // Byte offsets must be strictly monotonously increasing:
- DCHECK(idx == 0 || p.first > arr->get_int(idx - 2));
- arr->set_int(idx++, p.first);
- arr->set_int(idx++, p.second);
+ int GetTopPosition(Handle<WasmCompiledModule> compiled_module) {
+ DCHECK_EQ(1, interpreter()->GetThreadCount());
+ WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
+ DCHECK_LT(0, thread->GetFrameCount());
+
+ wasm::InterpretedFrame frame =
+ thread->GetFrame(thread->GetFrameCount() - 1);
+ return compiled_module->GetFunctionOffset(frame.function()->func_index) +
+ frame.pc();
+ }
+
+ std::vector<std::pair<uint32_t, int>> GetInterpretedStack(
+ Address frame_pointer) {
+ // TODO(clemensh): Use frame_pointer.
+ USE(frame_pointer);
+
+ DCHECK_EQ(1, interpreter()->GetThreadCount());
+ WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
+ std::vector<std::pair<uint32_t, int>> stack(thread->GetFrameCount());
+ for (int i = 0, e = thread->GetFrameCount(); i < e; ++i) {
+ wasm::InterpretedFrame frame = thread->GetFrame(i);
+ stack[i] = {frame.function()->func_index, frame.pc()};
}
- DCHECK_EQ(arr->length(), idx * kIntSize);
+ return stack;
+ }
+
+ std::unique_ptr<wasm::InterpretedFrame> GetInterpretedFrame(
+ Address frame_pointer, int idx) {
+ // TODO(clemensh): Use frame_pointer.
+ USE(frame_pointer);
+
+ DCHECK_EQ(1, interpreter()->GetThreadCount());
+ WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
+ return std::unique_ptr<wasm::InterpretedFrame>(
+ new wasm::InterpretedFrame(thread->GetMutableFrame(idx)));
+ }
+
+ uint64_t NumInterpretedCalls() {
+ DCHECK_EQ(1, interpreter()->GetThreadCount());
+ return interpreter()->GetThread(0)->NumInterpretedCalls();
+ }
+};
+
+InterpreterHandle* GetOrCreateInterpreterHandle(
+ Isolate* isolate, Handle<WasmDebugInfo> debug_info) {
+ Handle<Object> handle(debug_info->get(WasmDebugInfo::kInterpreterHandle),
+ isolate);
+ if (handle->IsUndefined(isolate)) {
+ InterpreterHandle* cpp_handle = new InterpreterHandle(isolate, *debug_info);
+ handle = Managed<InterpreterHandle>::New(isolate, cpp_handle);
+ debug_info->set(WasmDebugInfo::kInterpreterHandle, *handle);
+ }
+
+ return Handle<Managed<InterpreterHandle>>::cast(handle)->get();
+}
+
+InterpreterHandle* GetInterpreterHandle(WasmDebugInfo* debug_info) {
+ Object* handle_obj = debug_info->get(WasmDebugInfo::kInterpreterHandle);
+ DCHECK(!handle_obj->IsUndefined(debug_info->GetIsolate()));
+ return Managed<InterpreterHandle>::cast(handle_obj)->get();
+}
+
+InterpreterHandle* GetInterpreterHandleOrNull(WasmDebugInfo* debug_info) {
+ Object* handle_obj = debug_info->get(WasmDebugInfo::kInterpreterHandle);
+ if (handle_obj->IsUndefined(debug_info->GetIsolate())) return nullptr;
+ return Managed<InterpreterHandle>::cast(handle_obj)->get();
+}
+
+int GetNumFunctions(WasmInstanceObject* instance) {
+ size_t num_functions =
+ instance->compiled_module()->module()->functions.size();
+ DCHECK_GE(kMaxInt, num_functions);
+ return static_cast<int>(num_functions);
+}
+
+Handle<FixedArray> GetOrCreateInterpretedFunctions(
+ Isolate* isolate, Handle<WasmDebugInfo> debug_info) {
+ Handle<Object> obj(debug_info->get(WasmDebugInfo::kInterpretedFunctions),
+ isolate);
+ if (!obj->IsUndefined(isolate)) return Handle<FixedArray>::cast(obj);
+
+ Handle<FixedArray> new_arr = isolate->factory()->NewFixedArray(
+ GetNumFunctions(debug_info->wasm_instance()));
+ debug_info->set(WasmDebugInfo::kInterpretedFunctions, *new_arr);
+ return new_arr;
+}
+
+void RedirectCallsitesInCode(Code* code, Code* old_target, Code* new_target) {
+ DisallowHeapAllocation no_gc;
+ for (RelocIterator it(code, RelocInfo::kCodeTargetMask); !it.done();
+ it.next()) {
+ DCHECK(RelocInfo::IsCodeTarget(it.rinfo()->rmode()));
+ Code* target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
+ if (target != old_target) continue;
+ it.rinfo()->set_target_address(new_target->instruction_start());
}
- return *all_tables;
}
-} // namespace
-Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<JSObject> wasm) {
- Isolate *isolate = wasm->GetIsolate();
- Factory *factory = isolate->factory();
- Handle<FixedArray> arr =
- factory->NewFixedArray(kWasmDebugInfoNumEntries, TENURED);
- arr->set(kWasmDebugInfoWasmObj, *wasm);
- int hash = 0;
- Handle<SeqOneByteString> wasm_bytes = GetWasmBytes(wasm);
- {
- DisallowHeapAllocation no_gc;
- hash = StringHasher::HashSequentialString(
- wasm_bytes->GetChars(), wasm_bytes->length(), kZeroHashSeed);
- }
- Handle<Object> hash_obj = factory->NewNumberFromInt(hash, TENURED);
- arr->set(kWasmDebugInfoWasmBytesHash, *hash_obj);
+void RedirectCallsitesInInstance(Isolate* isolate, WasmInstanceObject* instance,
+ Code* old_target, Code* new_target) {
+ DisallowHeapAllocation no_gc;
+ // Redirect all calls in wasm functions.
+ FixedArray* code_table = instance->compiled_module()->ptr_to_code_table();
+ for (int i = 0, e = GetNumFunctions(instance); i < e; ++i) {
+ RedirectCallsitesInCode(Code::cast(code_table->get(i)), old_target,
+ new_target);
+ }
+ // Redirect all calls in exported functions.
+ FixedArray* weak_exported_functions =
+ instance->compiled_module()->ptr_to_weak_exported_functions();
+ for (int i = 0, e = weak_exported_functions->length(); i != e; ++i) {
+ WeakCell* weak_function = WeakCell::cast(weak_exported_functions->get(i));
+ if (weak_function->cleared()) continue;
+ Code* code = JSFunction::cast(weak_function->value())->code();
+ RedirectCallsitesInCode(code, old_target, new_target);
+ }
+}
+
+} // namespace
+
+Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<WasmInstanceObject> instance) {
+ Isolate* isolate = instance->GetIsolate();
+ Factory* factory = isolate->factory();
+ Handle<FixedArray> arr = factory->NewFixedArray(kFieldCount, TENURED);
+ arr->set(kInstance, *instance);
return Handle<WasmDebugInfo>::cast(arr);
}
-bool WasmDebugInfo::IsDebugInfo(Object *object) {
+bool WasmDebugInfo::IsDebugInfo(Object* object) {
if (!object->IsFixedArray()) return false;
- FixedArray *arr = FixedArray::cast(object);
- return arr->length() == kWasmDebugInfoNumEntries &&
- IsWasmInstance(arr->get(kWasmDebugInfoWasmObj)) &&
- arr->get(kWasmDebugInfoWasmBytesHash)->IsNumber();
+ FixedArray* arr = FixedArray::cast(object);
+ if (arr->length() != kFieldCount) return false;
+ if (!IsWasmInstance(arr->get(kInstance))) return false;
+ Isolate* isolate = arr->GetIsolate();
+ if (!arr->get(kInterpreterHandle)->IsUndefined(isolate) &&
+ !arr->get(kInterpreterHandle)->IsForeign())
+ return false;
+ return true;
}
-WasmDebugInfo *WasmDebugInfo::cast(Object *object) {
+WasmDebugInfo* WasmDebugInfo::cast(Object* object) {
DCHECK(IsDebugInfo(object));
- return reinterpret_cast<WasmDebugInfo *>(object);
-}
-
-JSObject *WasmDebugInfo::wasm_instance() {
- return JSObject::cast(get(kWasmDebugInfoWasmObj));
-}
-
-int WasmDebugInfo::GetAsmJsSourcePosition(Handle<WasmDebugInfo> debug_info,
- int func_index, int byte_offset) {
- Isolate *isolate = debug_info->GetIsolate();
- Handle<JSObject> instance(debug_info->wasm_instance(), isolate);
- FixedArray *offset_tables = GetAsmJsOffsetTables(debug_info, isolate);
-
- WasmCompiledModule *compiled_module = wasm::GetCompiledModule(*instance);
- int num_imported_functions =
- compiled_module->module()->num_imported_functions;
- DCHECK_LE(num_imported_functions, func_index);
- func_index -= num_imported_functions;
- DCHECK_LT(func_index, offset_tables->length());
- ByteArray *offset_table = ByteArray::cast(offset_tables->get(func_index));
-
- // Binary search for the current byte offset.
- int left = 0; // inclusive
- int right = offset_table->length() / kIntSize / 2; // exclusive
- DCHECK_LT(left, right);
- while (right - left > 1) {
- int mid = left + (right - left) / 2;
- if (offset_table->get_int(2 * mid) <= byte_offset) {
- left = mid;
- } else {
- right = mid;
- }
- }
- // There should be an entry for each position that could show up on the stack
- // trace:
- DCHECK_EQ(byte_offset, offset_table->get_int(2 * left));
- return offset_table->get_int(2 * left + 1);
+ return reinterpret_cast<WasmDebugInfo*>(object);
+}
+
+WasmInstanceObject* WasmDebugInfo::wasm_instance() {
+ return WasmInstanceObject::cast(get(kInstance));
+}
+
+void WasmDebugInfo::SetBreakpoint(Handle<WasmDebugInfo> debug_info,
+ int func_index, int offset) {
+ Isolate* isolate = debug_info->GetIsolate();
+ InterpreterHandle* handle = GetOrCreateInterpreterHandle(isolate, debug_info);
+ RedirectToInterpreter(debug_info, func_index);
+ const WasmFunction* func = &handle->module()->functions[func_index];
+ handle->interpreter()->SetBreakpoint(func, offset, true);
+}
+
+void WasmDebugInfo::RedirectToInterpreter(Handle<WasmDebugInfo> debug_info,
+ int func_index) {
+ Isolate* isolate = debug_info->GetIsolate();
+ DCHECK_LE(0, func_index);
+ DCHECK_GT(debug_info->wasm_instance()->module()->functions.size(),
+ func_index);
+ Handle<FixedArray> interpreted_functions =
+ GetOrCreateInterpretedFunctions(isolate, debug_info);
+ if (!interpreted_functions->get(func_index)->IsUndefined(isolate)) return;
+
+ // Ensure that the interpreter is instantiated.
+ GetOrCreateInterpreterHandle(isolate, debug_info);
+ Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
+ Handle<Code> new_code = compiler::CompileWasmInterpreterEntry(
+ isolate, func_index,
+ instance->compiled_module()->module()->functions[func_index].sig,
+ instance);
+
+ Handle<FixedArray> code_table = instance->compiled_module()->code_table();
+ Handle<Code> old_code(Code::cast(code_table->get(func_index)), isolate);
+ interpreted_functions->set(func_index, *new_code);
+
+ RedirectCallsitesInInstance(isolate, *instance, *old_code, *new_code);
+}
+
+void WasmDebugInfo::PrepareStep(StepAction step_action) {
+ GetInterpreterHandle(this)->PrepareStep(step_action);
+}
+
+void WasmDebugInfo::RunInterpreter(int func_index, uint8_t* arg_buffer) {
+ DCHECK_LE(0, func_index);
+ GetInterpreterHandle(this)->Execute(static_cast<uint32_t>(func_index),
+ arg_buffer);
+}
+
+std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack(
+ Address frame_pointer) {
+ return GetInterpreterHandle(this)->GetInterpretedStack(frame_pointer);
+}
+
+std::unique_ptr<wasm::InterpretedFrame> WasmDebugInfo::GetInterpretedFrame(
+ Address frame_pointer, int idx) {
+ return GetInterpreterHandle(this)->GetInterpretedFrame(frame_pointer, idx);
+}
+
+uint64_t WasmDebugInfo::NumInterpretedCalls() {
+ auto handle = GetInterpreterHandleOrNull(this);
+ return handle ? handle->NumInterpretedCalls() : 0;
}