summaryrefslogtreecommitdiff
path: root/deps/v8/src/debug/debug.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/debug/debug.cc')
-rw-r--r--deps/v8/src/debug/debug.cc159
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(