diff options
Diffstat (limited to 'deps/v8/src/wasm/wasm-debug.cc')
-rw-r--r-- | deps/v8/src/wasm/wasm-debug.cc | 935 |
1 files changed, 277 insertions, 658 deletions
diff --git a/deps/v8/src/wasm/wasm-debug.cc b/deps/v8/src/wasm/wasm-debug.cc index 10a2e194a7e..a8fd6505f0e 100644 --- a/deps/v8/src/wasm/wasm-debug.cc +++ b/deps/v8/src/wasm/wasm-debug.cc @@ -24,6 +24,7 @@ #include "src/wasm/wasm-limits.h" #include "src/wasm/wasm-module.h" #include "src/wasm/wasm-objects-inl.h" +#include "src/wasm/wasm-value.h" #include "src/zone/accounting-allocator.h" namespace v8 { @@ -90,8 +91,6 @@ class InterpreterHandle { Isolate* isolate_; const WasmModule* module_; WasmInterpreter interpreter_; - StepAction next_step_action_ = StepNone; - int last_step_stack_depth_ = 0; std::unordered_map<Address, uint32_t> activations_; uint32_t StartActivation(Address frame_pointer) { @@ -145,18 +144,6 @@ class InterpreterHandle { WasmInterpreter* interpreter() { return &interpreter_; } const WasmModule* module() const { return 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(); - } - // Returns true if exited regularly, false if a trap/exception occurred and // was not handled inside this activation. In the latter case, a pending // exception will have been set on the isolate. @@ -178,11 +165,10 @@ class InterpreterHandle { bool finished = false; while (!finished) { // TODO(clemensb): Add occasional StackChecks. - WasmInterpreter::State state = ContinueExecution(thread); + WasmInterpreter::State state = thread->Run(); switch (state) { case WasmInterpreter::State::PAUSED: - NotifyDebugEventListeners(thread); - break; + UNREACHABLE(); case WasmInterpreter::State::FINISHED: // Perfect, just break the switch and exit the loop. finished = true; @@ -230,109 +216,9 @@ class InterpreterHandle { FinishActivation(frame_pointer, activation_id); - // If we do stepping and it exits wasm interpreter then debugger need to - // prepare for it. - if (next_step_action_ != StepNone) { - // Enter the debugger. - DebugScope debug_scope(isolate_->debug()); - - isolate_->debug()->PrepareStep(StepOut); - } - ClearStepping(); - return true; } - 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(); - } - } - - Handle<WasmInstanceObject> GetInstanceObject() { - StackTraceFrameIterator it(isolate_); - WasmInterpreterEntryFrame* frame = - WasmInterpreterEntryFrame::cast(it.frame()); - Handle<WasmInstanceObject> instance_obj(frame->wasm_instance(), isolate_); - // Check that this is indeed the instance which is connected to this - // interpreter. - DCHECK_EQ(this, Managed<InterpreterHandle>::cast( - instance_obj->debug_info().interpreter_handle()) - .raw()); - return instance_obj; - } - - void NotifyDebugEventListeners(WasmInterpreter::Thread* thread) { - // Enter the debugger. - DebugScope debug_scope(isolate_->debug()); - - // Check whether we hit a breakpoint. - if (isolate_->debug()->break_points_active()) { - Handle<WasmModuleObject> module_object( - GetInstanceObject()->module_object(), isolate_); - Handle<Script> script(module_object->script(), isolate_); - int position = GetTopPosition(module_object); - Handle<FixedArray> breakpoints; - if (WasmScript::CheckBreakPoints(isolate_, script, position) - .ToHandle(&breakpoints)) { - // We hit one or several breakpoints. Clear stepping, notify the - // listeners and return. - ClearStepping(); - isolate_->debug()->OnDebugBreak(breakpoints); - 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()->empty_fixed_array()); - } - - int GetTopPosition(Handle<WasmModuleObject> module_object) { - DCHECK_EQ(1, interpreter()->GetThreadCount()); - WasmInterpreter::Thread* thread = interpreter()->GetThread(0); - DCHECK_LT(0, thread->GetFrameCount()); - - auto frame = thread->GetFrame(thread->GetFrameCount() - 1); - return GetWasmFunctionOffset(module_object->module(), - frame->function()->func_index) + - frame->pc(); - } - std::vector<std::pair<uint32_t, int>> GetInterpretedStack( Address frame_pointer) { DCHECK_EQ(1, interpreter()->GetThreadCount()); @@ -362,116 +248,10 @@ class InterpreterHandle { return frame_range.second - frame_range.first; } - WasmInterpreter::FramePtr GetInterpretedFrame(Address frame_pointer, - int idx) { - DCHECK_EQ(1, interpreter()->GetThreadCount()); - WasmInterpreter::Thread* thread = interpreter()->GetThread(0); - - std::pair<uint32_t, uint32_t> frame_range = - GetActivationFrameRange(thread, frame_pointer); - DCHECK_LE(0, idx); - DCHECK_GT(frame_range.second - frame_range.first, idx); - - return thread->GetFrame(frame_range.first + idx); - } - - uint64_t NumInterpretedCalls() { - DCHECK_EQ(1, interpreter()->GetThreadCount()); - return interpreter()->GetThread(0)->NumInterpretedCalls(); - } - - Handle<JSObject> GetLocalScopeObject(InterpretedFrame* frame, - Handle<WasmDebugInfo> debug_info) { - Isolate* isolate = isolate_; - - Handle<JSObject> local_scope_object = - isolate_->factory()->NewJSObjectWithNullProto(); - // Fill parameters and locals. - int num_locals = frame->GetLocalCount(); - DCHECK_LE(frame->GetParameterCount(), num_locals); - if (num_locals > 0) { - Handle<JSObject> locals_obj = - isolate_->factory()->NewJSObjectWithNullProto(); - Handle<String> locals_name = - isolate_->factory()->InternalizeString(StaticCharVector("locals")); - JSObject::AddProperty(isolate, local_scope_object, locals_name, - locals_obj, NONE); - NativeModule* native_module = - debug_info->wasm_instance().module_object().native_module(); - for (int i = 0; i < num_locals; ++i) { - Handle<Name> name; - if (!GetLocalNameString(isolate, native_module, - frame->function()->func_index, i) - .ToHandle(&name)) { - name = PrintFToOneByteString<true>(isolate_, "var%d", i); - } - WasmValue value = frame->GetLocalValue(i); - Handle<Object> value_obj = WasmValueToValueObject(isolate_, value); - // {name} can be a string representation of an element index. - LookupIterator::Key lookup_key{isolate, name}; - LookupIterator it(isolate, locals_obj, lookup_key, locals_obj, - LookupIterator::OWN_SKIP_INTERCEPTOR); - if (it.IsFound()) continue; - Object::AddDataProperty(&it, value_obj, NONE, - Just(ShouldThrow::kThrowOnError), - StoreOrigin::kNamed) - .Check(); - } - } - - // Fill stack values. - int stack_count = frame->GetStackHeight(); - // Use an object without prototype instead of an Array, for nicer displaying - // in DevTools. For Arrays, the length field and prototype is displayed, - // which does not make too much sense here. - Handle<JSObject> stack_obj = - isolate_->factory()->NewJSObjectWithNullProto(); - Handle<String> stack_name = - isolate_->factory()->InternalizeString(StaticCharVector("stack")); - JSObject::AddProperty(isolate, local_scope_object, stack_name, stack_obj, - NONE); - for (int i = 0; i < stack_count; ++i) { - WasmValue value = frame->GetStackValue(i); - Handle<Object> value_obj = WasmValueToValueObject(isolate_, value); - JSObject::AddDataElement(stack_obj, static_cast<uint32_t>(i), value_obj, - NONE); - } - return local_scope_object; - } - - Handle<JSObject> GetStackScopeObject(InterpretedFrame* frame, - Handle<WasmDebugInfo> debug_info) { - // Fill stack values. - int stack_count = frame->GetStackHeight(); - // Use an object without prototype instead of an Array, for nicer displaying - // in DevTools. For Arrays, the length field and prototype is displayed, - // which does not make too much sense here. - Handle<JSObject> stack_scope_obj = - isolate_->factory()->NewJSObjectWithNullProto(); - for (int i = 0; i < stack_count; ++i) { - WasmValue value = frame->GetStackValue(i); - Handle<Object> value_obj = WasmValueToValueObject(isolate_, value); - JSObject::AddDataElement(stack_scope_obj, static_cast<uint32_t>(i), - value_obj, NONE); - } - return stack_scope_obj; - } - private: DISALLOW_COPY_AND_ASSIGN(InterpreterHandle); }; -int FindByteOffset(int pc_offset, WasmCode* wasm_code) { - int position = 0; - SourcePositionTableIterator iterator(wasm_code->source_positions()); - for (SourcePositionTableIterator iterator(wasm_code->source_positions()); - !iterator.done() && iterator.code_offset() < pc_offset; - iterator.Advance()) { - position = iterator.source_position().ScriptOffset(); - } - return position; -} - // Generate a sorted and deduplicated list of byte offsets for this function's // current positions on the stack. std::vector<int> StackFramePositions(int func_index, Isolate* isolate) { @@ -479,14 +259,11 @@ std::vector<int> StackFramePositions(int func_index, Isolate* isolate) { WasmCodeRefScope code_ref_scope; for (StackTraceFrameIterator it(isolate); !it.done(); it.Advance()) { if (!it.is_wasm()) continue; - WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame()); + WasmFrame* frame = WasmFrame::cast(it.frame()); if (static_cast<int>(frame->function_index()) != func_index) continue; WasmCode* wasm_code = frame->wasm_code(); if (!wasm_code->is_liftoff()) continue; - int pc_offset = - static_cast<int>(frame->pc() - wasm_code->instruction_start()); - int byte_offset = FindByteOffset(pc_offset, wasm_code); - byte_offsets.push_back(byte_offset); + byte_offsets.push_back(frame->byte_offset()); } std::sort(byte_offsets.begin(), byte_offsets.end()); auto last = std::unique(byte_offsets.begin(), byte_offsets.end()); @@ -527,19 +304,26 @@ Address FindNewPC(WasmCode* wasm_code, int byte_offset, } // namespace -Handle<JSObject> GetGlobalScopeObject(Handle<WasmInstanceObject> instance) { +Handle<JSObject> GetModuleScopeObject(Handle<WasmInstanceObject> instance) { Isolate* isolate = instance->GetIsolate(); - Handle<JSObject> global_scope_object = + Handle<JSObject> module_scope_object = isolate->factory()->NewJSObjectWithNullProto(); if (instance->has_memory_object()) { - Handle<String> name = - isolate->factory()->InternalizeString(StaticCharVector("memory")); + Handle<String> name; + // TODO(duongn): extend the logic when multiple memories are supported. + const uint32_t memory_index = 0; + if (!WasmInstanceObject::GetMemoryNameOrNull(isolate, instance, + memory_index) + .ToHandle(&name)) { + const char* label = "memory%d"; + name = PrintFToOneByteString<true>(isolate, label, memory_index); + } Handle<JSArrayBuffer> memory_buffer( instance->memory_object().array_buffer(), isolate); Handle<JSTypedArray> uint8_array = isolate->factory()->NewJSTypedArray( kExternalUint8Array, memory_buffer, 0, memory_buffer->byte_length()); - JSObject::AddProperty(isolate, global_scope_object, name, uint8_array, + JSObject::AddProperty(isolate, module_scope_object, name, uint8_array, NONE); } @@ -549,7 +333,7 @@ Handle<JSObject> GetGlobalScopeObject(Handle<WasmInstanceObject> instance) { isolate->factory()->NewJSObjectWithNullProto(); Handle<String> globals_name = isolate->factory()->InternalizeString(StaticCharVector("globals")); - JSObject::AddProperty(isolate, global_scope_object, globals_name, + JSObject::AddProperty(isolate, module_scope_object, globals_name, globals_obj, NONE); for (uint32_t i = 0; i < globals.size(); ++i) { @@ -565,8 +349,7 @@ Handle<JSObject> GetGlobalScopeObject(Handle<WasmInstanceObject> instance) { JSObject::AddProperty(isolate, globals_obj, name, value_obj, NONE); } } - - return global_scope_object; + return module_scope_object; } class DebugInfoImpl { @@ -574,26 +357,49 @@ class DebugInfoImpl { explicit DebugInfoImpl(NativeModule* native_module) : native_module_(native_module) {} + int GetNumLocals(Isolate* isolate, Address pc) { + FrameInspectionScope scope(this, isolate, pc); + if (!scope.is_inspectable()) return 0; + return scope.debug_side_table->num_locals(); + } + + WasmValue GetLocalValue(int local, Isolate* isolate, Address pc, Address fp, + Address debug_break_fp) { + FrameInspectionScope scope(this, isolate, pc); + return GetValue(scope.debug_side_table_entry, local, fp, debug_break_fp); + } + + int GetStackDepth(Isolate* isolate, Address pc) { + FrameInspectionScope scope(this, isolate, pc); + if (!scope.is_inspectable()) return 0; + int num_locals = static_cast<int>(scope.debug_side_table->num_locals()); + int value_count = scope.debug_side_table_entry->num_values(); + return value_count - num_locals; + } + + WasmValue GetStackValue(int index, Isolate* isolate, Address pc, Address fp, + Address debug_break_fp) { + FrameInspectionScope scope(this, isolate, pc); + int num_locals = static_cast<int>(scope.debug_side_table->num_locals()); + int value_count = scope.debug_side_table_entry->num_values(); + if (num_locals + index >= value_count) return {}; + return GetValue(scope.debug_side_table_entry, num_locals + index, fp, + debug_break_fp); + } + Handle<JSObject> GetLocalScopeObject(Isolate* isolate, Address pc, Address fp, Address debug_break_fp) { + FrameInspectionScope scope(this, isolate, pc); Handle<JSObject> local_scope_object = isolate->factory()->NewJSObjectWithNullProto(); - wasm::WasmCodeRefScope wasm_code_ref_scope; - wasm::WasmCode* code = - isolate->wasm_engine()->code_manager()->LookupCode(pc); - // Only Liftoff code can be inspected. - if (!code->is_liftoff()) return local_scope_object; + if (!scope.is_inspectable()) return local_scope_object; auto* module = native_module_->module(); - auto* function = &module->functions[code->index()]; - auto* debug_side_table = GetDebugSideTable(code, isolate->allocator()); - int pc_offset = static_cast<int>(pc - code->instruction_start()); - auto* debug_side_table_entry = debug_side_table->GetEntry(pc_offset); - DCHECK_NOT_NULL(debug_side_table_entry); + auto* function = &module->functions[scope.code->index()]; // Fill parameters and locals. - int num_locals = static_cast<int>(debug_side_table->num_locals()); + int num_locals = static_cast<int>(scope.debug_side_table->num_locals()); DCHECK_LE(static_cast<int>(function->sig->parameter_count()), num_locals); if (num_locals > 0) { Handle<JSObject> locals_obj = @@ -610,7 +416,7 @@ class DebugInfoImpl { name = PrintFToOneByteString<true>(isolate, "var%d", i); } WasmValue value = - GetValue(debug_side_table_entry, i, fp, debug_break_fp); + GetValue(scope.debug_side_table_entry, i, fp, debug_break_fp); Handle<Object> value_obj = WasmValueToValueObject(isolate, value); // {name} can be a string representation of an element index. LookupIterator::Key lookup_key{isolate, name}; @@ -623,50 +429,26 @@ class DebugInfoImpl { .Check(); } } - - // Fill stack values. - // Use an object without prototype instead of an Array, for nicer displaying - // in DevTools. For Arrays, the length field and prototype is displayed, - // which does not make too much sense here. - Handle<JSObject> stack_obj = isolate->factory()->NewJSObjectWithNullProto(); - Handle<String> stack_name = - isolate->factory()->InternalizeString(StaticCharVector("stack")); - JSObject::AddProperty(isolate, local_scope_object, stack_name, stack_obj, - NONE); - int value_count = debug_side_table_entry->num_values(); - for (int i = num_locals; i < value_count; ++i) { - WasmValue value = GetValue(debug_side_table_entry, i, fp, debug_break_fp); - Handle<Object> value_obj = WasmValueToValueObject(isolate, value); - JSObject::AddDataElement(stack_obj, static_cast<uint32_t>(i - num_locals), - value_obj, NONE); - } return local_scope_object; } Handle<JSObject> GetStackScopeObject(Isolate* isolate, Address pc, Address fp, Address debug_break_fp) { + FrameInspectionScope scope(this, isolate, pc); Handle<JSObject> stack_scope_obj = isolate->factory()->NewJSObjectWithNullProto(); - wasm::WasmCodeRefScope wasm_code_ref_scope; - - wasm::WasmCode* code = - isolate->wasm_engine()->code_manager()->LookupCode(pc); - // Only Liftoff code can be inspected. - if (!code->is_liftoff()) return stack_scope_obj; - auto* debug_side_table = GetDebugSideTable(code, isolate->allocator()); - int pc_offset = static_cast<int>(pc - code->instruction_start()); - auto* debug_side_table_entry = debug_side_table->GetEntry(pc_offset); - DCHECK_NOT_NULL(debug_side_table_entry); + if (!scope.is_inspectable()) return stack_scope_obj; // Fill stack values. // Use an object without prototype instead of an Array, for nicer displaying // in DevTools. For Arrays, the length field and prototype is displayed, // which does not make too much sense here. - int num_locals = static_cast<int>(debug_side_table->num_locals()); - int value_count = debug_side_table_entry->num_values(); + int num_locals = static_cast<int>(scope.debug_side_table->num_locals()); + int value_count = scope.debug_side_table_entry->num_values(); for (int i = num_locals; i < value_count; ++i) { - WasmValue value = GetValue(debug_side_table_entry, i, fp, debug_break_fp); + WasmValue value = + GetValue(scope.debug_side_table_entry, i, fp, debug_break_fp); Handle<Object> value_obj = WasmValueToValueObject(isolate, value); JSObject::AddDataElement(stack_scope_obj, static_cast<uint32_t>(i - num_locals), value_obj, @@ -684,13 +466,12 @@ class DebugInfoImpl { return local_names_->GetName(func_index, local_index); } - void RecompileLiftoffWithBreakpoints(int func_index, Vector<int> offsets, - Isolate* current_isolate) { - if (func_index == flooded_function_index_) { - // We should not be flooding a function that is already flooded. - DCHECK(!(offsets.size() == 1 && offsets[0] == 0)); - flooded_function_index_ = -1; - } + WasmCode* RecompileLiftoffWithBreakpoints( + int func_index, Vector<int> offsets, Vector<int> extra_source_positions) { + // During compilation, we cannot hold the lock, since compilation takes the + // {NativeModule} lock, which could lead to deadlocks. + mutex_.AssertUnheld(); + // Recompile the function with Liftoff, setting the new breakpoints. // Not thread-safe. The caller is responsible for locking {mutex_}. CompilationEnv env = native_module_->CreateCompilationEnv(); @@ -701,94 +482,110 @@ class DebugInfoImpl { wire_bytes.begin() + function->code.end_offset()}; std::unique_ptr<DebugSideTable> debug_sidetable; - // Generate additional source positions for current stack frame positions. - // These source positions are used to find return addresses in the new code. - std::vector<int> stack_frame_positions = - StackFramePositions(func_index, current_isolate); - + ForDebugging for_debugging = + offsets.size() == 1 && offsets[0] == 0 ? kForStepping : kForDebugging; WasmCompilationResult result = ExecuteLiftoffCompilation( - native_module_->engine()->allocator(), &env, body, func_index, nullptr, - nullptr, offsets, &debug_sidetable, VectorOf(stack_frame_positions)); + native_module_->engine()->allocator(), &env, body, func_index, + for_debugging, nullptr, nullptr, offsets, &debug_sidetable, + extra_source_positions); // Liftoff compilation failure is a FATAL error. We rely on complete Liftoff // support for debugging. if (!result.succeeded()) FATAL("Liftoff compilation failed"); DCHECK_NOT_NULL(debug_sidetable); - WasmCodeRefScope wasm_code_ref_scope; - WasmCode* new_code = native_module_->AddCompiledCode(std::move(result)); + WasmCode* new_code = native_module_->PublishCode( + native_module_->AddCompiledCode(std::move(result))); + + DCHECK(new_code->is_inspectable()); bool added = debug_side_tables_.emplace(new_code, std::move(debug_sidetable)).second; DCHECK(added); USE(added); - UpdateReturnAddresses(current_isolate, new_code); + return new_code; } void SetBreakpoint(int func_index, int offset, Isolate* current_isolate) { - // Hold the mutex while setting the breakpoint. This guards against multiple - // isolates setting breakpoints at the same time. We don't really support - // that scenario yet, but concurrently compiling and installing different - // Liftoff variants of a function would be problematic. - base::MutexGuard guard(&mutex_); - - // offset == 0 indicates flooding and should not happen here. - DCHECK_NE(0, offset); - - std::vector<int>& breakpoints = breakpoints_per_function_[func_index]; - auto insertion_point = - std::lower_bound(breakpoints.begin(), breakpoints.end(), offset); - if (insertion_point != breakpoints.end() && *insertion_point == offset) { - // The breakpoint is already set. - return; + std::vector<int> breakpoints_copy; + { + // Hold the mutex while modifying the set of breakpoints, but release it + // before compiling the new code (see comment in + // {RecompileLiftoffWithBreakpoints}). This needs to be revisited once we + // support setting different breakpoints in different isolates + // (https://crbug.com/v8/10351). + base::MutexGuard guard(&mutex_); + + // offset == 0 indicates flooding and should not happen here. + DCHECK_NE(0, offset); + + std::vector<int>& breakpoints = breakpoints_per_function_[func_index]; + auto insertion_point = + std::lower_bound(breakpoints.begin(), breakpoints.end(), offset); + if (insertion_point != breakpoints.end() && *insertion_point == offset) { + // The breakpoint is already set. + return; + } + breakpoints.insert(insertion_point, offset); + breakpoints_copy = breakpoints; } - breakpoints.insert(insertion_point, offset); - // No need to recompile if the function is already flooded. - if (func_index == flooded_function_index_) return; + UpdateBreakpoints(func_index, VectorOf(breakpoints_copy), current_isolate); + } + + void UpdateBreakpoints(int func_index, Vector<int> breakpoints, + Isolate* current_isolate) { + // Generate additional source positions for current stack frame positions. + // These source positions are used to find return addresses in the new code. + std::vector<int> stack_frame_positions = + StackFramePositions(func_index, current_isolate); - RecompileLiftoffWithBreakpoints(func_index, VectorOf(breakpoints), - current_isolate); + WasmCodeRefScope wasm_code_ref_scope; + WasmCode* new_code = RecompileLiftoffWithBreakpoints( + func_index, breakpoints, VectorOf(stack_frame_positions)); + UpdateReturnAddresses(current_isolate, new_code); } - void FloodWithBreakpoints(int func_index, Isolate* current_isolate) { - base::MutexGuard guard(&mutex_); + void FloodWithBreakpoints(WasmFrame* frame, Isolate* current_isolate, + ReturnLocation return_location) { // 0 is an invalid offset used to indicate flooding. int offset = 0; - RecompileLiftoffWithBreakpoints(func_index, Vector<int>(&offset, 1), - current_isolate); + WasmCodeRefScope wasm_code_ref_scope; + DCHECK(frame->wasm_code()->is_liftoff()); + // Generate an additional source position for the current byte offset. + int byte_offset = frame->byte_offset(); + WasmCode* new_code = RecompileLiftoffWithBreakpoints( + frame->function_index(), VectorOf(&offset, 1), + VectorOf(&byte_offset, 1)); + UpdateReturnAddress(frame, new_code, return_location); } void PrepareStep(Isolate* isolate, StackFrameId break_frame_id) { StackTraceFrameIterator it(isolate, break_frame_id); DCHECK(!it.done()); - DCHECK(it.frame()->is_wasm_compiled()); - WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame()); + DCHECK(it.frame()->is_wasm()); + WasmFrame* frame = WasmFrame::cast(it.frame()); StepAction step_action = isolate->debug()->last_step_action(); + // If we are flooding the top frame, the return location is after a + // breakpoints. Otherwise, it's after a call. + ReturnLocation return_location = kAfterBreakpoint; + // If we are at a return instruction, then any stepping action is equivalent // to StepOut, and we need to flood the parent function. if (IsAtReturn(frame) || step_action == StepOut) { it.Advance(); - if (it.done() || !it.frame()->is_wasm_compiled()) return; - frame = WasmCompiledFrame::cast(it.frame()); + if (it.done() || !it.frame()->is_wasm()) return; + frame = WasmFrame::cast(it.frame()); + return_location = kAfterWasmCall; } - if (static_cast<int>(frame->function_index()) != flooded_function_index_) { - if (flooded_function_index_ != -1) { - std::vector<int>& breakpoints = - breakpoints_per_function_[flooded_function_index_]; - RecompileLiftoffWithBreakpoints(flooded_function_index_, - VectorOf(breakpoints), isolate); - } - FloodWithBreakpoints(frame->function_index(), isolate); - flooded_function_index_ = frame->function_index(); - } + FloodWithBreakpoints(frame, isolate, return_location); stepping_frame_ = frame->id(); } void ClearStepping() { stepping_frame_ = NO_ID; } - bool IsStepping(WasmCompiledFrame* frame) { + bool IsStepping(WasmFrame* frame) { Isolate* isolate = frame->wasm_instance().GetIsolate(); StepAction last_step_action = isolate->debug()->last_step_action(); return stepping_frame_ == frame->id() || last_step_action == StepIn; @@ -796,19 +593,23 @@ class DebugInfoImpl { void RemoveBreakpoint(int func_index, int position, Isolate* current_isolate) { - base::MutexGuard guard(&mutex_); - const auto& function = native_module_->module()->functions[func_index]; - int offset = position - function.code.offset(); - - std::vector<int>& breakpoints = breakpoints_per_function_[func_index]; - DCHECK_LT(0, offset); - auto insertion_point = - std::lower_bound(breakpoints.begin(), breakpoints.end(), offset); - if (insertion_point != breakpoints.end() && *insertion_point == offset) { + std::vector<int> breakpoints_copy; + { + base::MutexGuard guard(&mutex_); + const auto& function = native_module_->module()->functions[func_index]; + int offset = position - function.code.offset(); + + std::vector<int>& breakpoints = breakpoints_per_function_[func_index]; + DCHECK_LT(0, offset); + auto insertion_point = + std::lower_bound(breakpoints.begin(), breakpoints.end(), offset); + if (insertion_point == breakpoints.end()) return; + if (*insertion_point != offset) return; breakpoints.erase(insertion_point); + breakpoints_copy = breakpoints; } - RecompileLiftoffWithBreakpoints(func_index, VectorOf(breakpoints), - current_isolate); + + UpdateBreakpoints(func_index, VectorOf(breakpoints_copy), current_isolate); } void RemoveDebugSideTables(Vector<WasmCode* const> codes) { @@ -819,11 +620,39 @@ class DebugInfoImpl { } private: + struct FrameInspectionScope { + FrameInspectionScope(DebugInfoImpl* debug_info, Isolate* isolate, + Address pc) + : code(isolate->wasm_engine()->code_manager()->LookupCode(pc)), + pc_offset(static_cast<int>(pc - code->instruction_start())), + debug_side_table( + code->is_inspectable() + ? debug_info->GetDebugSideTable(code, isolate->allocator()) + : nullptr), + debug_side_table_entry(debug_side_table + ? debug_side_table->GetEntry(pc_offset) + : nullptr) { + DCHECK_IMPLIES(code->is_inspectable(), debug_side_table_entry != nullptr); + } + + bool is_inspectable() const { return debug_side_table_entry; } + + wasm::WasmCodeRefScope wasm_code_ref_scope; + wasm::WasmCode* code; + int pc_offset; + const DebugSideTable* debug_side_table; + const DebugSideTable::Entry* debug_side_table_entry; + }; + const DebugSideTable* GetDebugSideTable(WasmCode* code, AccountingAllocator* allocator) { - base::MutexGuard guard(&mutex_); - if (auto& existing_table = debug_side_tables_[code]) { - return existing_table.get(); + DCHECK(code->is_inspectable()); + { + // Only hold the mutex temporarily. We can't hold it while generating the + // debug side table, because compilation takes the {NativeModule} lock. + base::MutexGuard guard(&mutex_); + auto it = debug_side_tables_.find(code); + if (it != debug_side_tables_.end()) return it->second.get(); } // Otherwise create the debug side table now. @@ -839,7 +668,10 @@ class DebugInfoImpl { DebugSideTable* ret = debug_side_table.get(); // Install into cache and return. - debug_side_tables_[code] = std::move(debug_side_table); + { + base::MutexGuard guard(&mutex_); + debug_side_tables_[code] = std::move(debug_side_table); + } return ret; } @@ -911,7 +743,6 @@ class DebugInfoImpl { // code. The frame layout itself should be independent of breakpoints. // TODO(thibaudm): update other threads as well. void UpdateReturnAddresses(Isolate* isolate, WasmCode* new_code) { - DCHECK(new_code->is_liftoff()); // The first return location is after the breakpoint, others are after wasm // calls. ReturnLocation return_location = kAfterBreakpoint; @@ -920,25 +751,31 @@ class DebugInfoImpl { // We still need the flooded function for stepping. if (it.frame()->id() == stepping_frame_) continue; if (!it.is_wasm()) continue; - WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame()); + WasmFrame* frame = WasmFrame::cast(it.frame()); if (frame->native_module() != new_code->native_module()) continue; if (frame->function_index() != new_code->index()) continue; - WasmCode* old_code = frame->wasm_code(); - if (!old_code->is_liftoff()) continue; - int pc_offset = - static_cast<int>(frame->pc() - old_code->instruction_start()); - int position = frame->position(); - int byte_offset = FindByteOffset(pc_offset, old_code); - Address new_pc = FindNewPC(new_code, byte_offset, return_location); - PointerAuthentication::ReplacePC(frame->pc_address(), new_pc, - kSystemPointerSize); - USE(position); - // The frame position should still be the same after OSR. - DCHECK_EQ(position, frame->position()); + if (!frame->wasm_code()->is_liftoff()) continue; + UpdateReturnAddress(frame, new_code, return_location); } } - bool IsAtReturn(WasmCompiledFrame* frame) { + void UpdateReturnAddress(WasmFrame* frame, WasmCode* new_code, + ReturnLocation return_location) { + DCHECK(new_code->is_liftoff()); + DCHECK_EQ(frame->function_index(), new_code->index()); + DCHECK_EQ(frame->native_module(), new_code->native_module()); + DCHECK(frame->wasm_code()->is_liftoff()); +#ifdef DEBUG + int old_position = frame->position(); +#endif + Address new_pc = FindNewPC(new_code, frame->byte_offset(), return_location); + PointerAuthentication::ReplacePC(frame->pc_address(), new_pc, + kSystemPointerSize); + // The frame position should still be the same after OSR. + DCHECK_EQ(old_position, frame->position()); + } + + bool IsAtReturn(WasmFrame* frame) { DisallowHeapAllocation no_gc; int position = frame->position(); NativeModule* native_module = @@ -967,10 +804,9 @@ class DebugInfoImpl { // function). std::unordered_map<int, std::vector<int>> breakpoints_per_function_; - // Store the frame ID when stepping, to avoid breaking in recursive calls of - // the same function. + // Store the frame ID when stepping, to avoid overwriting that frame when + // setting or removing a breakpoint. StackFrameId stepping_frame_ = NO_ID; - int flooded_function_index_ = -1; DISALLOW_COPY_AND_ASSIGN(DebugInfoImpl); }; @@ -980,6 +816,24 @@ DebugInfo::DebugInfo(NativeModule* native_module) DebugInfo::~DebugInfo() = default; +int DebugInfo::GetNumLocals(Isolate* isolate, Address pc) { + return impl_->GetNumLocals(isolate, pc); +} + +WasmValue DebugInfo::GetLocalValue(int local, Isolate* isolate, Address pc, + Address fp, Address debug_break_fp) { + return impl_->GetLocalValue(local, isolate, pc, fp, debug_break_fp); +} + +int DebugInfo::GetStackDepth(Isolate* isolate, Address pc) { + return impl_->GetStackDepth(isolate, pc); +} + +WasmValue DebugInfo::GetStackValue(int index, Isolate* isolate, Address pc, + Address fp, Address debug_break_fp) { + return impl_->GetStackValue(index, isolate, pc, fp, debug_break_fp); +} + Handle<JSObject> DebugInfo::GetLocalScopeObject(Isolate* isolate, Address pc, Address fp, Address debug_break_fp) { @@ -1007,7 +861,7 @@ void DebugInfo::PrepareStep(Isolate* isolate, StackFrameId break_frame_id) { void DebugInfo::ClearStepping() { impl_->ClearStepping(); } -bool DebugInfo::IsStepping(WasmCompiledFrame* frame) { +bool DebugInfo::IsStepping(WasmFrame* frame) { return impl_->IsStepping(frame); } @@ -1022,40 +876,6 @@ void DebugInfo::RemoveDebugSideTables(Vector<WasmCode* const> code) { } // namespace wasm -namespace { - -wasm::InterpreterHandle* GetOrCreateInterpreterHandle( - Isolate* isolate, Handle<WasmDebugInfo> debug_info) { - Handle<Object> handle(debug_info->interpreter_handle(), isolate); - if (handle->IsUndefined(isolate)) { - // Use the maximum stack size to estimate the maximum size of the - // interpreter. The interpreter keeps its own stack internally, and the size - // of the stack should dominate the overall size of the interpreter. We - // multiply by '2' to account for the growing strategy for the backing store - // of the stack. - size_t interpreter_size = FLAG_stack_size * KB * 2; - handle = Managed<wasm::InterpreterHandle>::Allocate( - isolate, interpreter_size, isolate, debug_info); - debug_info->set_interpreter_handle(*handle); - } - - return Handle<Managed<wasm::InterpreterHandle>>::cast(handle)->raw(); -} - -wasm::InterpreterHandle* GetInterpreterHandle(WasmDebugInfo debug_info) { - Object handle_obj = debug_info.interpreter_handle(); - DCHECK(!handle_obj.IsUndefined()); - return Managed<wasm::InterpreterHandle>::cast(handle_obj).raw(); -} - -wasm::InterpreterHandle* GetInterpreterHandleOrNull(WasmDebugInfo debug_info) { - Object handle_obj = debug_info.interpreter_handle(); - if (handle_obj.IsUndefined()) return nullptr; - return Managed<wasm::InterpreterHandle>::cast(handle_obj).raw(); -} - -} // namespace - Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<WasmInstanceObject> instance) { DCHECK(!instance->has_debug_info()); Factory* factory = instance->GetIsolate()->factory(); @@ -1084,128 +904,6 @@ wasm::WasmInterpreter* WasmDebugInfo::SetupForTesting( } // static -void WasmDebugInfo::PrepareStepIn(Handle<WasmDebugInfo> debug_info, - int func_index) { - Isolate* isolate = debug_info->GetIsolate(); - auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info); - RedirectToInterpreter(debug_info, Vector<int>(&func_index, 1)); - const wasm::WasmFunction* func = &handle->module()->functions[func_index]; - handle->interpreter()->PrepareStepIn(func); - // Debug break would be considered as a step-in inside wasm. - handle->PrepareStep(StepAction::StepIn); -} - -// static -void WasmDebugInfo::SetBreakpoint(Handle<WasmDebugInfo> debug_info, - int func_index, int offset) { - Isolate* isolate = debug_info->GetIsolate(); - auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info); - RedirectToInterpreter(debug_info, Vector<int>(&func_index, 1)); - const wasm::WasmFunction* func = &handle->module()->functions[func_index]; - handle->interpreter()->SetBreakpoint(func, offset, true); -} - -// static -void WasmDebugInfo::ClearBreakpoint(Handle<WasmDebugInfo> debug_info, - int func_index, int offset) { - Isolate* isolate = debug_info->GetIsolate(); - auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info); - // TODO(leese): If there are no more breakpoints left it would be good to - // undo redirecting to the interpreter. - const wasm::WasmFunction* func = &handle->module()->functions[func_index]; - handle->interpreter()->SetBreakpoint(func, offset, false); -} - -// static -void WasmDebugInfo::RedirectToInterpreter(Handle<WasmDebugInfo> debug_info, - Vector<int> func_indexes) { - Isolate* isolate = debug_info->GetIsolate(); - // Ensure that the interpreter is instantiated. - GetOrCreateInterpreterHandle(isolate, debug_info); - Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate); - wasm::NativeModule* native_module = instance->module_object().native_module(); - const wasm::WasmModule* module = instance->module(); - - // We may modify the wasm jump table. - wasm::NativeModuleModificationScope native_module_modification_scope( - native_module); - - for (int func_index : func_indexes) { - DCHECK_LE(0, func_index); - DCHECK_GT(module->functions.size(), func_index); - // Note that this is just a best effort check. Multiple threads can still - // race at redirecting the same function to the interpreter, which is OK. - if (native_module->IsRedirectedToInterpreter(func_index)) continue; - - wasm::WasmCodeRefScope code_ref_scope; - wasm::WasmCompilationResult result = compiler::CompileWasmInterpreterEntry( - isolate->wasm_engine(), native_module->enabled_features(), func_index, - module->functions[func_index].sig); - std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode( - func_index, result.code_desc, result.frame_slot_count, - result.tagged_parameter_slots, - result.protected_instructions_data.as_vector(), - result.source_positions.as_vector(), wasm::WasmCode::kInterpreterEntry, - wasm::ExecutionTier::kInterpreter); - native_module->PublishCode(std::move(wasm_code)); - DCHECK(native_module->IsRedirectedToInterpreter(func_index)); - } -} - -void WasmDebugInfo::PrepareStep(StepAction step_action) { - GetInterpreterHandle(*this)->PrepareStep(step_action); -} - -// static -bool WasmDebugInfo::RunInterpreter(Isolate* isolate, - Handle<WasmDebugInfo> debug_info, - Address frame_pointer, int func_index, - Vector<wasm::WasmValue> argument_values, - Vector<wasm::WasmValue> return_values) { - DCHECK_LE(0, func_index); - auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info); - Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate); - return handle->Execute(instance, frame_pointer, - static_cast<uint32_t>(func_index), argument_values, - return_values); -} - -std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack( - Address frame_pointer) { - return GetInterpreterHandle(*this)->GetInterpretedStack(frame_pointer); -} - -int WasmDebugInfo::NumberOfActiveFrames(Address frame_pointer) { - return GetInterpreterHandle(*this)->NumberOfActiveFrames(frame_pointer); -} - -wasm::WasmInterpreter::FramePtr 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; -} - -// static -Handle<JSObject> WasmDebugInfo::GetLocalScopeObject( - Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) { - auto* interp_handle = GetInterpreterHandle(*debug_info); - auto frame = interp_handle->GetInterpretedFrame(frame_pointer, frame_index); - return interp_handle->GetLocalScopeObject(frame.get(), debug_info); -} - -// static -Handle<JSObject> WasmDebugInfo::GetStackScopeObject( - Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) { - auto* interp_handle = GetInterpreterHandle(*debug_info); - auto frame = interp_handle->GetInterpretedFrame(frame_pointer, frame_index); - return interp_handle->GetStackScopeObject(frame.get(), debug_info); -} - -// static Handle<Code> WasmDebugInfo::GetCWasmEntry(Handle<WasmDebugInfo> debug_info, const wasm::FunctionSig* sig) { Isolate* isolate = debug_info->GetIsolate(); @@ -1315,61 +1013,71 @@ bool WasmScript::SetBreakPointForFunction(Handle<Script> script, int func_index, WasmScript::AddBreakpointToInfo(script, func.code.offset() + offset, break_point); - if (FLAG_debug_in_liftoff) { - native_module->GetDebugInfo()->SetBreakpoint(func_index, offset, isolate); - } else { - // Iterate over all instances and tell them to set this new breakpoint. - // We do this using the weak list of all instances from the script. - Handle<WeakArrayList> weak_instance_list(script->wasm_weak_instance_list(), - isolate); - for (int i = 0; i < weak_instance_list->length(); ++i) { - MaybeObject maybe_instance = weak_instance_list->Get(i); - if (maybe_instance->IsWeak()) { - Handle<WasmInstanceObject> instance( - WasmInstanceObject::cast(maybe_instance->GetHeapObjectAssumeWeak()), - isolate); - Handle<WasmDebugInfo> debug_info = - WasmInstanceObject::GetOrCreateDebugInfo(instance); - WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset); - } + native_module->GetDebugInfo()->SetBreakpoint(func_index, offset, isolate); + + return true; +} + +namespace { + +int GetBreakpointPos(Isolate* isolate, Object break_point_info_or_undef) { + if (break_point_info_or_undef.IsUndefined(isolate)) return kMaxInt; + return BreakPointInfo::cast(break_point_info_or_undef).source_position(); +} + +int FindBreakpointInfoInsertPos(Isolate* isolate, + Handle<FixedArray> breakpoint_infos, + int position) { + // Find insert location via binary search, taking care of undefined values on + // the right. Position is always greater than zero. + DCHECK_LT(0, position); + + int left = 0; // inclusive + int right = breakpoint_infos->length(); // exclusive + while (right - left > 1) { + int mid = left + (right - left) / 2; + Object mid_obj = breakpoint_infos->get(mid); + if (GetBreakpointPos(isolate, mid_obj) <= position) { + left = mid; + } else { + right = mid; } } - return true; + int left_pos = GetBreakpointPos(isolate, breakpoint_infos->get(left)); + return left_pos < position ? left + 1 : left; } +} // namespace + // static bool WasmScript::ClearBreakPoint(Handle<Script> script, int position, Handle<BreakPoint> break_point) { + if (!script->has_wasm_breakpoint_infos()) return false; + Isolate* isolate = script->GetIsolate(); + Handle<FixedArray> breakpoint_infos(script->wasm_breakpoint_infos(), isolate); - // Find the function for this breakpoint. - const wasm::WasmModule* module = script->wasm_native_module()->module(); - int func_index = GetContainingWasmFunction(module, position); - if (func_index < 0) return false; - const wasm::WasmFunction& func = module->functions[func_index]; - int offset_in_func = position - func.code.offset(); + int pos = FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position); - if (!WasmScript::RemoveBreakpointFromInfo(script, position, break_point)) { - return false; - } + // Does a BreakPointInfo object already exist for this position? + if (pos == breakpoint_infos->length()) return false; - // Iterate over all instances and tell them to remove this breakpoint. - // We do this using the weak list of all instances from the script. - Handle<WeakArrayList> weak_instance_list(script->wasm_weak_instance_list(), - isolate); - for (int i = 0; i < weak_instance_list->length(); ++i) { - MaybeObject maybe_instance = weak_instance_list->Get(i); - if (maybe_instance->IsWeak()) { - Handle<WasmInstanceObject> instance( - WasmInstanceObject::cast(maybe_instance->GetHeapObjectAssumeWeak()), - isolate); - Handle<WasmDebugInfo> debug_info = - WasmInstanceObject::GetOrCreateDebugInfo(instance); - WasmDebugInfo::ClearBreakpoint(debug_info, func_index, offset_in_func); + Handle<BreakPointInfo> info(BreakPointInfo::cast(breakpoint_infos->get(pos)), + isolate); + BreakPointInfo::ClearBreakPoint(isolate, info, break_point); + + // Check if there are no more breakpoints at this location. + if (info->GetBreakPointCount(isolate) == 0) { + // Update array by moving breakpoints up one position. + for (int i = pos; i < breakpoint_infos->length() - 1; i++) { + Object entry = breakpoint_infos->get(i + 1); + breakpoint_infos->set(i, entry); + if (entry.IsUndefined(isolate)) break; } + // Make sure last array element is empty as a result. + breakpoint_infos->set_undefined(breakpoint_infos->length() - 1); } - return true; } @@ -1401,38 +1109,12 @@ bool WasmScript::ClearBreakPointById(Handle<Script> script, int breakpoint_id) { return false; } -namespace { - -int GetBreakpointPos(Isolate* isolate, Object break_point_info_or_undef) { - if (break_point_info_or_undef.IsUndefined(isolate)) return kMaxInt; - return BreakPointInfo::cast(break_point_info_or_undef).source_position(); -} - -int FindBreakpointInfoInsertPos(Isolate* isolate, - Handle<FixedArray> breakpoint_infos, - int position) { - // Find insert location via binary search, taking care of undefined values on - // the right. Position is always greater than zero. - DCHECK_LT(0, position); - - int left = 0; // inclusive - int right = breakpoint_infos->length(); // exclusive - while (right - left > 1) { - int mid = left + (right - left) / 2; - Object mid_obj = breakpoint_infos->get(mid); - if (GetBreakpointPos(isolate, mid_obj) <= position) { - left = mid; - } else { - right = mid; - } - } - - int left_pos = GetBreakpointPos(isolate, breakpoint_infos->get(left)); - return left_pos < position ? left + 1 : left; +// static +void WasmScript::ClearAllBreakpoints(Script script) { + script.set_wasm_breakpoint_infos( + ReadOnlyRoots(script.GetIsolate()).empty_fixed_array()); } -} // namespace - // static void WasmScript::AddBreakpointToInfo(Handle<Script> script, int position, Handle<BreakPoint> break_point) { @@ -1490,69 +1172,6 @@ void WasmScript::AddBreakpointToInfo(Handle<Script> script, int position, } // static -bool WasmScript::RemoveBreakpointFromInfo(Handle<Script> script, int position, - Handle<BreakPoint> break_point) { - if (!script->has_wasm_breakpoint_infos()) return false; - - Isolate* isolate = script->GetIsolate(); - Handle<FixedArray> breakpoint_infos(script->wasm_breakpoint_infos(), isolate); - - int pos = FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position); - - // Does a BreakPointInfo object already exist for this position? - if (pos == breakpoint_infos->length()) return false; - - Handle<BreakPointInfo> info(BreakPointInfo::cast(breakpoint_infos->get(pos)), - isolate); - BreakPointInfo::ClearBreakPoint(isolate, info, break_point); - - // Check if there are no more breakpoints at this location. - if (info->GetBreakPointCount(isolate) == 0) { - // Update array by moving breakpoints up one position. - for (int i = pos; i < breakpoint_infos->length() - 1; i++) { - Object entry = breakpoint_infos->get(i + 1); - breakpoint_infos->set(i, entry); - if (entry.IsUndefined(isolate)) break; - } - // Make sure last array element is empty as a result. - breakpoint_infos->set_undefined(breakpoint_infos->length() - 1); - } - return true; -} - -void WasmScript::SetBreakpointsOnNewInstance( - Handle<Script> script, Handle<WasmInstanceObject> instance) { - if (!script->has_wasm_breakpoint_infos()) return; - Isolate* isolate = script->GetIsolate(); - Handle<WasmDebugInfo> debug_info = - WasmInstanceObject::GetOrCreateDebugInfo(instance); - - Handle<FixedArray> breakpoint_infos(script->wasm_breakpoint_infos(), isolate); - // If the array exists, it should not be empty. - DCHECK_LT(0, breakpoint_infos->length()); - - for (int i = 0, e = breakpoint_infos->length(); i < e; ++i) { - Handle<Object> obj(breakpoint_infos->get(i), isolate); - if (obj->IsUndefined(isolate)) { - for (; i < e; ++i) { - DCHECK(breakpoint_infos->get(i).IsUndefined(isolate)); - } - break; - } - Handle<BreakPointInfo> breakpoint_info = Handle<BreakPointInfo>::cast(obj); - int position = breakpoint_info->source_position(); - - // Find the function for this breakpoint, and set the breakpoint. - const wasm::WasmModule* module = script->wasm_native_module()->module(); - int func_index = GetContainingWasmFunction(module, position); - DCHECK_LE(0, func_index); - const wasm::WasmFunction& func = module->functions[func_index]; - int offset_in_func = position - func.code.offset(); - WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset_in_func); - } -} - -// static bool WasmScript::GetPossibleBreakpoints( wasm::NativeModule* native_module, const v8::debug::Location& start, const v8::debug::Location& end, |