diff options
Diffstat (limited to 'deps/v8/src/debug/debug.cc')
-rw-r--r-- | deps/v8/src/debug/debug.cc | 159 |
1 files changed, 89 insertions, 70 deletions
diff --git a/deps/v8/src/debug/debug.cc b/deps/v8/src/debug/debug.cc index 9c6c5f4ca5..ebbbe4d9a2 100644 --- a/deps/v8/src/debug/debug.cc +++ b/deps/v8/src/debug/debug.cc @@ -119,6 +119,11 @@ BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info, return it.GetBreakLocation(); } +bool BreakLocation::IsPausedInJsFunctionEntry(JavaScriptFrame* frame) { + auto summary = FrameSummary::GetTop(frame); + return summary.code_offset() == kFunctionEntryBytecodeOffset; +} + MaybeHandle<FixedArray> Debug::CheckBreakPointsForLocations( Handle<DebugInfo> debug_info, std::vector<BreakLocation>& break_locations, bool* has_break_points) { @@ -204,7 +209,10 @@ int BreakLocation::BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info, bool BreakLocation::HasBreakPoint(Isolate* isolate, Handle<DebugInfo> debug_info) const { // First check whether there is a break point with the same source position. - if (!debug_info->HasBreakPoint(isolate, position_)) return false; + if (!debug_info->HasBreakInfo() || + !debug_info->HasBreakPoint(isolate, position_)) { + return false; + } if (debug_info->CanBreakAtEntry()) { DCHECK_EQ(Debug::kBreakAtEntryPosition, position_); return debug_info->BreakAtEntry(); @@ -382,6 +390,7 @@ void Debug::ThreadInit() { thread_local_.break_frame_id_ = StackFrameId::NO_ID; thread_local_.last_step_action_ = StepNone; thread_local_.last_statement_position_ = kNoSourcePosition; + thread_local_.last_bytecode_offset_ = kFunctionEntryBytecodeOffset; thread_local_.last_frame_count_ = -1; thread_local_.fast_forward_to_return_ = false; thread_local_.ignore_step_into_function_ = Smi::zero(); @@ -419,7 +428,7 @@ char* Debug::RestoreDebug(char* storage) { int current_frame_count = CurrentFrameCount(); int target_frame_count = thread_local_.target_frame_count_; DCHECK(current_frame_count >= target_frame_count); - StackTraceFrameIterator frames_it(isolate_); + DebuggableStackFrameIterator frames_it(isolate_); while (current_frame_count > target_frame_count) { current_frame_count -= frames_it.FrameFunctionCount(); frames_it.Advance(); @@ -480,11 +489,12 @@ void Debug::Unload() { debug_delegate_ = nullptr; } -debug::DebugDelegate::PauseAfterInstrumentation +debug::DebugDelegate::ActionAfterInstrumentation Debug::OnInstrumentationBreak() { RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); if (!debug_delegate_) { - return debug::DebugDelegate::kNoPauseAfterInstrumentationRequested; + return debug::DebugDelegate::ActionAfterInstrumentation:: + kPauseIfBreakpointsHit; } DCHECK(in_debug_scope()); HandleScope scope(isolate_); @@ -517,11 +527,19 @@ void Debug::Break(JavaScriptFrame* frame, Handle<JSFunction> break_target) { IsBreakOnInstrumentation(debug_info, location); bool shouldPauseAfterInstrumentation = false; if (hitInstrumentationBreak) { - debug::DebugDelegate::PauseAfterInstrumentation pauseDuringInstrumentation = + debug::DebugDelegate::ActionAfterInstrumentation action = OnInstrumentationBreak(); - shouldPauseAfterInstrumentation = - pauseDuringInstrumentation == - debug::DebugDelegate::kPauseAfterInstrumentationRequested; + switch (action) { + case debug::DebugDelegate::ActionAfterInstrumentation::kPause: + shouldPauseAfterInstrumentation = true; + break; + case debug::DebugDelegate::ActionAfterInstrumentation:: + kPauseIfBreakpointsHit: + shouldPauseAfterInstrumentation = false; + break; + case debug::DebugDelegate::ActionAfterInstrumentation::kContinue: + return; + } } // Find actual break points, if any, and trigger debug break event. @@ -533,8 +551,6 @@ void Debug::Break(JavaScriptFrame* frame, Handle<JSFunction> break_target) { if (!break_points_hit.is_null() || break_on_next_function_call() || scheduled_break) { StepAction lastStepAction = last_step_action(); - DCHECK_IMPLIES(scheduled_break_on_function_call(), - lastStepAction == StepNone); debug::BreakReasons break_reasons; if (scheduled_break) { break_reasons.Add(debug::BreakReason::kScheduled); @@ -606,10 +622,19 @@ void Debug::Break(JavaScriptFrame* frame, Handle<JSFunction> break_target) { return; } FrameSummary summary = FrameSummary::GetTop(frame); + const bool frame_or_statement_changed = + current_frame_count != last_frame_count || + thread_local_.last_statement_position_ != + summary.SourceStatementPosition(); + // If we stayed on the same frame and reached the same bytecode offset + // since the last step, we are in a loop and should pause. Otherwise + // we keep "stepping" through the loop without ever acutally pausing. + const bool potential_single_statement_loop = + current_frame_count == last_frame_count && + thread_local_.last_bytecode_offset_ == summary.code_offset(); step_break = step_break || location.IsReturn() || - current_frame_count != last_frame_count || - thread_local_.last_statement_position_ != - summary.SourceStatementPosition(); + potential_single_statement_loop || + frame_or_statement_changed; break; } } @@ -734,13 +759,27 @@ bool Debug::CheckBreakPoint(Handle<BreakPoint> break_point, condition, throw_on_side_effect); } - if (!maybe_result.ToHandle(&result)) { - if (isolate_->has_pending_exception()) { - isolate_->clear_pending_exception(); - } - return false; + Handle<Object> maybe_exception; + bool exception_thrown = true; + if (maybe_result.ToHandle(&result)) { + exception_thrown = false; + } else if (isolate_->has_pending_exception()) { + maybe_exception = handle(isolate_->pending_exception(), isolate_); + isolate_->clear_pending_exception(); + } + + CHECK(in_debug_scope()); + DisableBreak no_recursive_break(this); + + { + RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback); + Handle<Context> native_context(isolate_->native_context()); + debug_delegate_->BreakpointConditionEvaluated( + v8::Utils::ToLocal(native_context), break_point->id(), exception_thrown, + v8::Utils::ToLocal(maybe_exception)); } - return result->BooleanValue(isolate_); + + return !result.is_null() ? result->BooleanValue(isolate_) : false; } bool Debug::SetBreakpoint(Handle<SharedFunctionInfo> shared, @@ -1132,7 +1171,7 @@ void Debug::PrepareStepOnThrow() { int current_frame_count = CurrentFrameCount(); // Iterate through the JavaScript stack looking for handlers. - JavaScriptFrameIterator it(isolate_); + JavaScriptStackFrameIterator it(isolate_); while (!it.done()) { JavaScriptFrame* frame = it.frame(); if (frame->LookupExceptionHandlerInTable(nullptr, nullptr) > 0) break; @@ -1210,7 +1249,7 @@ void Debug::PrepareStep(StepAction step_action) { thread_local_.last_step_action_ = step_action; - StackTraceFrameIterator frames_it(isolate_, frame_id); + DebuggableStackFrameIterator frames_it(isolate_, frame_id); CommonFrame* frame = frames_it.frame(); BreakLocation location = BreakLocation::Invalid(); @@ -1253,9 +1292,8 @@ void Debug::PrepareStep(StepAction step_action) { // A step-next in blackboxed function is a step-out. if (step_action == StepOver && IsBlackboxed(shared)) step_action = StepOut; - thread_local_.last_statement_position_ = - summary.abstract_code()->SourceStatementPosition(isolate_, - summary.code_offset()); + thread_local_.last_statement_position_ = summary.SourceStatementPosition(); + thread_local_.last_bytecode_offset_ = summary.code_offset(); thread_local_.last_frame_count_ = current_frame_count; // No longer perform the current async step. clear_suspended_generator(); @@ -1282,6 +1320,7 @@ void Debug::PrepareStep(StepAction step_action) { case StepOut: { // Clear last position info. For stepping out it does not matter. thread_local_.last_statement_position_ = kNoSourcePosition; + thread_local_.last_bytecode_offset_ = kFunctionEntryBytecodeOffset; thread_local_.last_frame_count_ = -1; if (!shared.is_null()) { if (!location.IsReturnOrSuspend() && !IsBlackboxed(shared)) { @@ -1397,6 +1436,7 @@ void Debug::ClearStepping() { thread_local_.last_step_action_ = StepNone; thread_local_.last_statement_position_ = kNoSourcePosition; + thread_local_.last_bytecode_offset_ = kFunctionEntryBytecodeOffset; thread_local_.ignore_step_into_function_ = Smi::zero(); thread_local_.fast_forward_to_return_ = false; thread_local_.last_frame_count_ = -1; @@ -1433,7 +1473,8 @@ class DiscardBaselineCodeVisitor : public ThreadVisitor { void VisitThread(Isolate* isolate, ThreadLocalTop* top) override { DisallowGarbageCollection diallow_gc; bool deopt_all = shared_ == SharedFunctionInfo(); - for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { + for (JavaScriptStackFrameIterator it(isolate, top); !it.done(); + it.Advance()) { if (!deopt_all && it.frame()->function().shared() != shared_) continue; if (it.frame()->type() == StackFrame::BASELINE) { BaselineFrame* frame = BaselineFrame::cast(it.frame()); @@ -1482,13 +1523,12 @@ class DiscardBaselineCodeVisitor : public ThreadVisitor { void Debug::DiscardBaselineCode(SharedFunctionInfo shared) { RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); DCHECK(shared.HasBaselineCode()); - Isolate* isolate = shared.GetIsolate(); DiscardBaselineCodeVisitor visitor(shared); - visitor.VisitThread(isolate, isolate->thread_local_top()); - isolate->thread_manager()->IterateArchivedThreads(&visitor); + visitor.VisitThread(isolate_, isolate_->thread_local_top()); + isolate_->thread_manager()->IterateArchivedThreads(&visitor); // TODO(v8:11429): Avoid this heap walk somehow. - HeapObjectIterator iterator(isolate->heap()); - auto trampoline = BUILTIN_CODE(isolate, InterpreterEntryTrampoline); + HeapObjectIterator iterator(isolate_->heap()); + auto trampoline = BUILTIN_CODE(isolate_, InterpreterEntryTrampoline); shared.FlushBaselineCode(); for (HeapObject obj = iterator.Next(); !obj.is_null(); obj = iterator.Next()) { @@ -1526,29 +1566,11 @@ void Debug::DiscardAllBaselineCode() { void Debug::DeoptimizeFunction(Handle<SharedFunctionInfo> shared) { RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); - // Deoptimize all code compiled from this shared function info including - // inlining. - isolate_->AbortConcurrentOptimization(BlockingBehavior::kBlock); if (shared->HasBaselineCode()) { DiscardBaselineCode(*shared); } - - bool found_something = false; - Code::OptimizedCodeIterator iterator(isolate_); - do { - Code code = iterator.Next(); - if (code.is_null()) break; - if (code.Inlines(*shared)) { - code.set_marked_for_deoptimization(true); - found_something = true; - } - } while (true); - - if (found_something) { - // Only go through with the deoptimization if something was found. - Deoptimizer::DeoptimizeMarkedCode(isolate_); - } + Deoptimizer::DeoptimizeAllOptimizedCodeWithFunction(isolate_, shared); } void Debug::PrepareFunctionForDebugExecution( @@ -1628,7 +1650,7 @@ void Debug::InstallDebugBreakTrampoline() { if (!needs_to_use_trampoline) return; - Handle<CodeT> trampoline = BUILTIN_CODE(isolate_, DebugBreakTrampoline); + Handle<Code> trampoline = BUILTIN_CODE(isolate_, DebugBreakTrampoline); std::vector<Handle<JSFunction>> needs_compile; using AccessorPairWithContext = std::pair<Handle<AccessorPair>, Handle<NativeContext>>; @@ -1932,7 +1954,8 @@ bool Debug::FindSharedFunctionInfosIntersectingRange( for (const auto& candidate : candidates) { IsCompiledScope is_compiled_scope(candidate->is_compiled_scope(isolate_)); if (!is_compiled_scope.is_compiled()) { - // Code that cannot be compiled lazily are internal and not debuggable. + // InstructionStream that cannot be compiled lazily are internal and not + // debuggable. DCHECK(candidate->allows_lazy_compilation()); if (!Compiler::Compile(isolate_, candidate, Compiler::CLEAR_EXCEPTION, &is_compiled_scope)) { @@ -1997,7 +2020,8 @@ Handle<Object> Debug::FindInnermostContainingFunctionInfo(Handle<Script> script, } // If not, compile to reveal inner functions. HandleScope scope(isolate_); - // Code that cannot be compiled lazily are internal and not debuggable. + // InstructionStream that cannot be compiled lazily are internal and not + // debuggable. DCHECK(shared.allows_lazy_compilation()); if (!Compiler::Compile(isolate_, handle(shared, isolate_), Compiler::CLEAR_EXCEPTION, &is_compiled_scope)) { @@ -2236,7 +2260,7 @@ bool Debug::IsExceptionBlackboxed(bool uncaught) { RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); // Uncaught exception is blackboxed if all current frames are blackboxed, // caught exception if top frame is blackboxed. - StackTraceFrameIterator it(isolate_); + DebuggableStackFrameIterator it(isolate_); #if V8_ENABLE_WEBASSEMBLY while (!it.done() && it.is_wasm()) it.Advance(); #endif // V8_ENABLE_WEBASSEMBLY @@ -2305,7 +2329,7 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise, } { - JavaScriptFrameIterator it(isolate_); + JavaScriptStackFrameIterator it(isolate_); // Check whether the top frame is blackboxed or the break location is muted. if (!it.done() && (IsMutedAtCurrentLocation(it.frame()) || IsExceptionBlackboxed(uncaught))) { @@ -2419,7 +2443,7 @@ bool Debug::ShouldBeSkipped() { PostponeInterruptsScope no_interrupts(isolate_); DisableBreak no_recursive_break(this); - StackTraceFrameIterator iterator(isolate_); + DebuggableStackFrameIterator iterator(isolate_); FrameSummary summary = iterator.GetTopValidFrame(); Handle<Object> script_obj = summary.script(); if (!script_obj->IsScript()) return false; @@ -2440,7 +2464,7 @@ bool Debug::ShouldBeSkipped() { bool Debug::AllFramesOnStackAreBlackboxed() { RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); HandleScope scope(isolate_); - for (StackTraceFrameIterator it(isolate_); !it.done(); it.Advance()) { + for (DebuggableStackFrameIterator it(isolate_); !it.done(); it.Advance()) { if (!it.is_javascript()) continue; if (!IsFrameBlackboxed(it.javascript_frame())) return false; } @@ -2490,13 +2514,7 @@ void Debug::ProcessCompileEvent(bool has_compile_error, Handle<Script> script) { // inspector to filter scripts by native context. script->set_context_data(isolate_->native_context()->debug_context_id()); if (ignore_events()) return; -#if V8_ENABLE_WEBASSEMBLY - if (!script->IsUserJavaScript() && script->type() != i::Script::TYPE_WASM) { - return; - } -#else - if (!script->IsUserJavaScript()) return; -#endif // V8_ENABLE_WEBASSEMBLY + if (!script->IsSubjectToDebugging()) return; if (!debug_delegate_) return; SuppressDebug while_processing(this); DebugScope debug_scope(this); @@ -2511,7 +2529,7 @@ void Debug::ProcessCompileEvent(bool has_compile_error, Handle<Script> script) { } int Debug::CurrentFrameCount() { - StackTraceFrameIterator it(isolate_); + DebuggableStackFrameIterator it(isolate_); if (break_frame_id() != StackFrameId::NO_ID) { // Skip to break frame. DCHECK(in_debug_scope()); @@ -2572,7 +2590,7 @@ void Debug::HandleDebugBreak(IgnoreBreakMode ignore_break_mode, HandleScope scope(isolate_); MaybeHandle<FixedArray> break_points; { - StackTraceFrameIterator it(isolate_); + DebuggableStackFrameIterator it(isolate_); DCHECK(!it.done()); JavaScriptFrame* frame = it.frame()->is_java_script() ? JavaScriptFrame::cast(it.frame()) @@ -2586,8 +2604,8 @@ void Debug::HandleDebugBreak(IgnoreBreakMode ignore_break_mode, // it's context. Instead, we step into the function and pause at the // first official breakable position. // This behavior mirrors "BreakOnNextFunctionCall". - if (break_reasons.contains(v8::debug::BreakReason::kScheduled)) { - CHECK_EQ(last_step_action(), StepAction::StepNone); + if (break_reasons.contains(v8::debug::BreakReason::kScheduled) && + BreakLocation::IsPausedInJsFunctionEntry(frame)) { thread_local_.scheduled_break_on_next_function_call_ = true; PrepareStepIn(function); return; @@ -2644,7 +2662,7 @@ void Debug::PrintBreakLocation() { if (!v8_flags.print_break_location) return; RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); HandleScope scope(isolate_); - StackTraceFrameIterator iterator(isolate_); + DebuggableStackFrameIterator iterator(isolate_); if (iterator.done()) return; CommonFrame* frame = iterator.frame(); std::vector<FrameSummary> frames; @@ -2697,7 +2715,7 @@ DebugScope::DebugScope(Debug* debug) // Create the new break info. If there is no proper frames there is no break // frame id. - StackTraceFrameIterator it(isolate()); + DebuggableStackFrameIterator it(isolate()); bool has_frames = !it.done(); debug_->thread_local_.break_frame_id_ = has_frames ? it.frame()->id() : StackFrameId::NO_ID; @@ -2793,7 +2811,8 @@ void Debug::StopSideEffectCheckMode() { DCHECK(isolate_->debug_execution_mode() == DebugInfo::kSideEffects); if (side_effect_check_failed_) { DCHECK(isolate_->has_pending_exception()); - DCHECK(isolate_->is_execution_termination_pending()); + DCHECK_IMPLIES(v8_flags.strict_termination_checks, + isolate_->is_execution_termination_pending()); // Convert the termination exception into a regular exception. isolate_->CancelTerminateExecution(); isolate_->Throw(*isolate_->factory()->NewEvalError( |