Commit d9399cc3 authored by yangguo's avatar yangguo Committed by Commit bot

[debugger] account for inlined functions when stepping.

- Remove obsolete BreakLocatorType.
- Perform PrepareStepOnThrow after OnException event, in case stepping
  was scheduled in the exception event.
- Use frame count instead of frame pointer for stepping. Frame pointer
  is not reliable due to possible deopts.
- Consistently check for inlined functions in inlined frames.
- Use SharedFunctionInfo in FloodWithOneshot and EnsureDebugInfo.

R=jgruber@chromium.org
BUG=v8:5901

Review-Url: https://codereview.chromium.org/2664793002
Cr-Commit-Position: refs/heads/master@{#42878}
parent 9432eb5c
...@@ -116,35 +116,32 @@ bool BreakLocation::HasBreakPoint(Handle<DebugInfo> debug_info) const { ...@@ -116,35 +116,32 @@ bool BreakLocation::HasBreakPoint(Handle<DebugInfo> debug_info) const {
// step to, but not actually a location where we can put a break point. // step to, but not actually a location where we can put a break point.
if (abstract_code_->IsCode()) { if (abstract_code_->IsCode()) {
DCHECK_EQ(debug_info->DebugCode(), abstract_code_->GetCode()); DCHECK_EQ(debug_info->DebugCode(), abstract_code_->GetCode());
CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); CodeBreakIterator it(debug_info);
it.SkipToPosition(position_, BREAK_POSITION_ALIGNED); it.SkipToPosition(position_, BREAK_POSITION_ALIGNED);
return it.code_offset() == code_offset_; return it.code_offset() == code_offset_;
} else { } else {
DCHECK(abstract_code_->IsBytecodeArray()); DCHECK(abstract_code_->IsBytecodeArray());
BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); BytecodeArrayBreakIterator it(debug_info);
it.SkipToPosition(position_, BREAK_POSITION_ALIGNED); it.SkipToPosition(position_, BREAK_POSITION_ALIGNED);
return it.code_offset() == code_offset_; return it.code_offset() == code_offset_;
} }
} }
std::unique_ptr<BreakIterator> BreakIterator::GetIterator( std::unique_ptr<BreakIterator> BreakIterator::GetIterator(
Handle<DebugInfo> debug_info, Handle<AbstractCode> abstract_code, Handle<DebugInfo> debug_info, Handle<AbstractCode> abstract_code) {
BreakLocatorType type) {
if (abstract_code->IsBytecodeArray()) { if (abstract_code->IsBytecodeArray()) {
DCHECK(debug_info->HasDebugBytecodeArray()); DCHECK(debug_info->HasDebugBytecodeArray());
return std::unique_ptr<BreakIterator>( return std::unique_ptr<BreakIterator>(
new BytecodeArrayBreakIterator(debug_info, type)); new BytecodeArrayBreakIterator(debug_info));
} else { } else {
DCHECK(abstract_code->IsCode()); DCHECK(abstract_code->IsCode());
DCHECK(debug_info->HasDebugCode()); DCHECK(debug_info->HasDebugCode());
return std::unique_ptr<BreakIterator>( return std::unique_ptr<BreakIterator>(new CodeBreakIterator(debug_info));
new CodeBreakIterator(debug_info, type));
} }
} }
BreakIterator::BreakIterator(Handle<DebugInfo> debug_info, BreakIterator::BreakIterator(Handle<DebugInfo> debug_info)
BreakLocatorType type) : debug_info_(debug_info), break_index_(-1) {
: debug_info_(debug_info), break_index_(-1), break_locator_type_(type) {
position_ = debug_info->shared()->start_position(); position_ = debug_info->shared()->start_position();
statement_position_ = position_; statement_position_ = position_;
} }
...@@ -173,10 +170,9 @@ int BreakIterator::BreakIndexFromPosition(int source_position, ...@@ -173,10 +170,9 @@ int BreakIterator::BreakIndexFromPosition(int source_position,
return closest_break; return closest_break;
} }
CodeBreakIterator::CodeBreakIterator(Handle<DebugInfo> debug_info, CodeBreakIterator::CodeBreakIterator(Handle<DebugInfo> debug_info)
BreakLocatorType type) : BreakIterator(debug_info),
: BreakIterator(debug_info, type), reloc_iterator_(debug_info->DebugCode(), GetModeMask()),
reloc_iterator_(debug_info->DebugCode(), GetModeMask(type)),
source_position_iterator_( source_position_iterator_(
debug_info->DebugCode()->source_position_table()) { debug_info->DebugCode()->source_position_table()) {
// There is at least one break location. // There is at least one break location.
...@@ -184,17 +180,13 @@ CodeBreakIterator::CodeBreakIterator(Handle<DebugInfo> debug_info, ...@@ -184,17 +180,13 @@ CodeBreakIterator::CodeBreakIterator(Handle<DebugInfo> debug_info,
Next(); Next();
} }
int CodeBreakIterator::GetModeMask(BreakLocatorType type) { int CodeBreakIterator::GetModeMask() {
int mask = 0; int mask = 0;
mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN); mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN);
mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CALL); mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CALL);
if (isolate()->is_tail_call_elimination_enabled()) { mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_TAIL_CALL);
mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_TAIL_CALL); mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION);
} mask |= RelocInfo::ModeMask(RelocInfo::DEBUGGER_STATEMENT);
if (type == ALL_BREAK_LOCATIONS) {
mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION);
mask |= RelocInfo::ModeMask(RelocInfo::DEBUGGER_STATEMENT);
}
return mask; return mask;
} }
...@@ -244,7 +236,7 @@ DebugBreakType CodeBreakIterator::GetDebugBreakType() { ...@@ -244,7 +236,7 @@ DebugBreakType CodeBreakIterator::GetDebugBreakType() {
void CodeBreakIterator::SkipToPosition(int position, void CodeBreakIterator::SkipToPosition(int position,
BreakPositionAlignment alignment) { BreakPositionAlignment alignment) {
CodeBreakIterator it(debug_info_, break_locator_type_); CodeBreakIterator it(debug_info_);
SkipTo(it.BreakIndexFromPosition(position, alignment)); SkipTo(it.BreakIndexFromPosition(position, alignment));
} }
...@@ -279,8 +271,8 @@ BreakLocation CodeBreakIterator::GetBreakLocation() { ...@@ -279,8 +271,8 @@ BreakLocation CodeBreakIterator::GetBreakLocation() {
} }
BytecodeArrayBreakIterator::BytecodeArrayBreakIterator( BytecodeArrayBreakIterator::BytecodeArrayBreakIterator(
Handle<DebugInfo> debug_info, BreakLocatorType type) Handle<DebugInfo> debug_info)
: BreakIterator(debug_info, type), : BreakIterator(debug_info),
source_position_iterator_( source_position_iterator_(
debug_info->DebugBytecodeArray()->source_position_table()) { debug_info->DebugBytecodeArray()->source_position_table()) {
// There is at least one break location. // There is at least one break location.
...@@ -304,13 +296,7 @@ void BytecodeArrayBreakIterator::Next() { ...@@ -304,13 +296,7 @@ void BytecodeArrayBreakIterator::Next() {
DCHECK(statement_position_ >= 0); DCHECK(statement_position_ >= 0);
DebugBreakType type = GetDebugBreakType(); DebugBreakType type = GetDebugBreakType();
if (type == NOT_DEBUG_BREAK) continue; if (type != NOT_DEBUG_BREAK) break;
if (break_locator_type_ == ALL_BREAK_LOCATIONS) break;
DCHECK_EQ(CALLS_AND_RETURNS, break_locator_type_);
if (type == DEBUG_BREAK_SLOT_AT_CALL) break;
if (type == DEBUG_BREAK_SLOT_AT_RETURN) break;
} }
break_index_++; break_index_++;
} }
...@@ -339,7 +325,7 @@ DebugBreakType BytecodeArrayBreakIterator::GetDebugBreakType() { ...@@ -339,7 +325,7 @@ DebugBreakType BytecodeArrayBreakIterator::GetDebugBreakType() {
void BytecodeArrayBreakIterator::SkipToPosition( void BytecodeArrayBreakIterator::SkipToPosition(
int position, BreakPositionAlignment alignment) { int position, BreakPositionAlignment alignment) {
BytecodeArrayBreakIterator it(debug_info_, break_locator_type_); BytecodeArrayBreakIterator it(debug_info_);
SkipTo(it.BreakIndexFromPosition(position, alignment)); SkipTo(it.BreakIndexFromPosition(position, alignment));
} }
...@@ -399,8 +385,8 @@ void Debug::ThreadInit() { ...@@ -399,8 +385,8 @@ void Debug::ThreadInit() {
thread_local_.break_frame_id_ = StackFrame::NO_ID; thread_local_.break_frame_id_ = StackFrame::NO_ID;
thread_local_.last_step_action_ = StepNone; thread_local_.last_step_action_ = StepNone;
thread_local_.last_statement_position_ = kNoSourcePosition; thread_local_.last_statement_position_ = kNoSourcePosition;
thread_local_.last_fp_ = 0; thread_local_.last_frame_count_ = -1;
thread_local_.target_fp_ = 0; thread_local_.target_frame_count_ = -1;
thread_local_.return_value_ = Smi::kZero; thread_local_.return_value_ = Smi::kZero;
thread_local_.async_task_count_ = 0; thread_local_.async_task_count_ = 0;
clear_suspended_generator(); clear_suspended_generator();
...@@ -515,7 +501,7 @@ void Debug::Break(JavaScriptFrame* frame) { ...@@ -515,7 +501,7 @@ void Debug::Break(JavaScriptFrame* frame) {
// Return if we fail to retrieve debug info. // Return if we fail to retrieve debug info.
Handle<JSFunction> function(frame->function()); Handle<JSFunction> function(frame->function());
Handle<SharedFunctionInfo> shared(function->shared()); Handle<SharedFunctionInfo> shared(function->shared());
if (!EnsureDebugInfo(shared, function)) return; if (!EnsureDebugInfo(shared)) return;
Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_); Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
// Find the break location where execution has stopped. // Find the break location where execution has stopped.
...@@ -536,28 +522,29 @@ void Debug::Break(JavaScriptFrame* frame) { ...@@ -536,28 +522,29 @@ void Debug::Break(JavaScriptFrame* frame) {
// No break point. Check for stepping. // No break point. Check for stepping.
StepAction step_action = last_step_action(); StepAction step_action = last_step_action();
Address current_fp = frame->UnpaddedFP(); int current_frame_count = CurrentFrameCount();
Address target_fp = thread_local_.target_fp_; int target_frame_count = thread_local_.target_frame_count_;
Address last_fp = thread_local_.last_fp_; int last_frame_count = thread_local_.last_frame_count_;
bool step_break = false; bool step_break = false;
switch (step_action) { switch (step_action) {
case StepNone: case StepNone:
return; return;
case StepOut: case StepOut:
// Step out has not reached the target frame yet. // Step out should not break in a deeper frame than target frame.
if (current_fp < target_fp) return; if (current_frame_count > target_frame_count) return;
step_break = true; step_break = true;
break; break;
case StepNext: case StepNext:
// Step next should not break in a deeper frame. // Step next should not break in a deeper frame than target frame.
if (current_fp < target_fp) return; if (current_frame_count > target_frame_count) return;
// For step-next, a tail call is like a return and should break. // For step-next, a tail call is like a return and should break.
step_break = location.IsTailCall(); step_break = location.IsTailCall();
// Fall through. // Fall through.
case StepIn: { case StepIn: {
FrameSummary summary = FrameSummary::GetTop(frame); FrameSummary summary = FrameSummary::GetTop(frame);
step_break = step_break || location.IsReturn() || current_fp != last_fp || step_break = step_break || location.IsReturn() ||
current_frame_count != last_frame_count ||
thread_local_.last_statement_position_ != thread_local_.last_statement_position_ !=
summary.SourceStatementPosition(); summary.SourceStatementPosition();
break; break;
...@@ -595,16 +582,16 @@ MaybeHandle<FixedArray> Debug::CheckBreakPoints(Handle<DebugInfo> debug_info, ...@@ -595,16 +582,16 @@ MaybeHandle<FixedArray> Debug::CheckBreakPoints(Handle<DebugInfo> debug_info,
bool Debug::IsMutedAtCurrentLocation(JavaScriptFrame* frame) { bool Debug::IsMutedAtCurrentLocation(JavaScriptFrame* frame) {
HandleScope scope(isolate_);
// A break location is considered muted if break locations on the current // A break location is considered muted if break locations on the current
// statement have at least one break point, and all of these break points // statement have at least one break point, and all of these break points
// evaluate to false. Aside from not triggering a debug break event at the // evaluate to false. Aside from not triggering a debug break event at the
// break location, we also do not trigger one for debugger statements, nor // break location, we also do not trigger one for debugger statements, nor
// an exception event on exception at this location. // an exception event on exception at this location.
Object* fun = frame->function(); FrameSummary summary = FrameSummary::GetTop(frame);
if (!fun->IsJSFunction()) return false; DCHECK(!summary.IsWasm());
JSFunction* function = JSFunction::cast(fun); Handle<JSFunction> function = summary.AsJavaScript().function();
if (!function->shared()->HasDebugInfo()) return false; if (!function->shared()->HasDebugInfo()) return false;
HandleScope scope(isolate_);
Handle<DebugInfo> debug_info(function->shared()->GetDebugInfo()); Handle<DebugInfo> debug_info(function->shared()->GetDebugInfo());
// Enter the debugger. // Enter the debugger.
DebugScope debug_scope(this); DebugScope debug_scope(this);
...@@ -670,11 +657,7 @@ bool Debug::SetBreakPoint(Handle<JSFunction> function, ...@@ -670,11 +657,7 @@ bool Debug::SetBreakPoint(Handle<JSFunction> function,
// Make sure the function is compiled and has set up the debug info. // Make sure the function is compiled and has set up the debug info.
Handle<SharedFunctionInfo> shared(function->shared()); Handle<SharedFunctionInfo> shared(function->shared());
if (!EnsureDebugInfo(shared, function)) { if (!EnsureDebugInfo(shared)) return true;
// Return if retrieving debug info failed.
return true;
}
Handle<DebugInfo> debug_info(shared->GetDebugInfo()); Handle<DebugInfo> debug_info(shared->GetDebugInfo());
// Source positions starts with zero. // Source positions starts with zero.
DCHECK(*source_position >= 0); DCHECK(*source_position >= 0);
...@@ -714,10 +697,7 @@ bool Debug::SetBreakPointForScript(Handle<Script> script, ...@@ -714,10 +697,7 @@ bool Debug::SetBreakPointForScript(Handle<Script> script,
// Make sure the function has set up the debug info. // Make sure the function has set up the debug info.
Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result); Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result);
if (!EnsureDebugInfo(shared, Handle<JSFunction>::null())) { if (!EnsureDebugInfo(shared)) return false;
// Return if retrieving debug info failed.
return false;
}
// Find position within function. The script position might be before the // Find position within function. The script position might be before the
// source position of the first function. // source position of the first function.
...@@ -747,13 +727,13 @@ int Debug::FindBreakablePosition(Handle<DebugInfo> debug_info, ...@@ -747,13 +727,13 @@ int Debug::FindBreakablePosition(Handle<DebugInfo> debug_info,
int statement_position; int statement_position;
int position; int position;
if (debug_info->HasDebugCode()) { if (debug_info->HasDebugCode()) {
CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); CodeBreakIterator it(debug_info);
it.SkipToPosition(source_position, alignment); it.SkipToPosition(source_position, alignment);
statement_position = it.statement_position(); statement_position = it.statement_position();
position = it.position(); position = it.position();
} else { } else {
DCHECK(debug_info->HasDebugBytecodeArray()); DCHECK(debug_info->HasDebugBytecodeArray());
BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); BytecodeArrayBreakIterator it(debug_info);
it.SkipToPosition(source_position, alignment); it.SkipToPosition(source_position, alignment);
statement_position = it.statement_position(); statement_position = it.statement_position();
position = it.position(); position = it.position();
...@@ -770,12 +750,12 @@ void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) { ...@@ -770,12 +750,12 @@ void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) {
BreakPointInfo* info = BreakPointInfo::cast(break_points->get(i)); BreakPointInfo* info = BreakPointInfo::cast(break_points->get(i));
if (info->GetBreakPointCount() == 0) continue; if (info->GetBreakPointCount() == 0) continue;
if (debug_info->HasDebugCode()) { if (debug_info->HasDebugCode()) {
CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); CodeBreakIterator it(debug_info);
it.SkipToPosition(info->source_position(), BREAK_POSITION_ALIGNED); it.SkipToPosition(info->source_position(), BREAK_POSITION_ALIGNED);
it.SetDebugBreak(); it.SetDebugBreak();
} }
if (debug_info->HasDebugBytecodeArray()) { if (debug_info->HasDebugBytecodeArray()) {
BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); BytecodeArrayBreakIterator it(debug_info);
it.SkipToPosition(info->source_position(), BREAK_POSITION_ALIGNED); it.SkipToPosition(info->source_position(), BREAK_POSITION_ALIGNED);
it.SetDebugBreak(); it.SetDebugBreak();
} }
...@@ -785,14 +765,12 @@ void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) { ...@@ -785,14 +765,12 @@ void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) {
void Debug::ClearBreakPoints(Handle<DebugInfo> debug_info) { void Debug::ClearBreakPoints(Handle<DebugInfo> debug_info) {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
if (debug_info->HasDebugCode()) { if (debug_info->HasDebugCode()) {
for (CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); !it.Done(); for (CodeBreakIterator it(debug_info); !it.Done(); it.Next()) {
it.Next()) {
it.ClearDebugBreak(); it.ClearDebugBreak();
} }
} }
if (debug_info->HasDebugBytecodeArray()) { if (debug_info->HasDebugBytecodeArray()) {
for (BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); for (BytecodeArrayBreakIterator it(debug_info); !it.Done(); it.Next()) {
!it.Done(); it.Next()) {
it.ClearDebugBreak(); it.ClearDebugBreak();
} }
} }
...@@ -833,36 +811,19 @@ void Debug::ClearAllBreakPoints() { ...@@ -833,36 +811,19 @@ void Debug::ClearAllBreakPoints() {
} }
} }
void Debug::FloodWithOneShot(Handle<JSFunction> function, void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared) {
BreakLocatorType type) { if (!shared->IsSubjectToDebugging() || IsBlackboxed(shared)) return;
// Debug utility functions are not subject to debugging.
if (function->native_context() == *debug_context()) return;
if (!function->shared()->IsSubjectToDebugging() ||
IsBlackboxed(function->shared())) {
// Builtin functions are not subject to stepping, but need to be
// deoptimized, because optimized code does not check for debug
// step in at call sites.
Deoptimizer::DeoptimizeFunction(*function);
return;
}
// Make sure the function is compiled and has set up the debug info. // Make sure the function is compiled and has set up the debug info.
Handle<SharedFunctionInfo> shared(function->shared()); if (!EnsureDebugInfo(shared)) return;
if (!EnsureDebugInfo(shared, function)) {
// Return if we failed to retrieve the debug info.
return;
}
// Flood the function with break points.
Handle<DebugInfo> debug_info(shared->GetDebugInfo()); Handle<DebugInfo> debug_info(shared->GetDebugInfo());
// Flood the function with break points.
if (debug_info->HasDebugCode()) { if (debug_info->HasDebugCode()) {
for (CodeBreakIterator it(debug_info, type); !it.Done(); it.Next()) { for (CodeBreakIterator it(debug_info); !it.Done(); it.Next()) {
it.SetDebugBreak(); it.SetDebugBreak();
} }
} }
if (debug_info->HasDebugBytecodeArray()) { if (debug_info->HasDebugBytecodeArray()) {
for (BytecodeArrayBreakIterator it(debug_info, type); !it.Done(); for (BytecodeArrayBreakIterator it(debug_info); !it.Done(); it.Next()) {
it.Next()) {
it.SetDebugBreak(); it.SetDebugBreak();
} }
} }
...@@ -916,7 +877,7 @@ void Debug::PrepareStepIn(Handle<JSFunction> function) { ...@@ -916,7 +877,7 @@ void Debug::PrepareStepIn(Handle<JSFunction> function) {
if (ignore_events()) return; if (ignore_events()) return;
if (in_debug_scope()) return; if (in_debug_scope()) return;
if (break_disabled()) return; if (break_disabled()) return;
FloodWithOneShot(function); FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
} }
void Debug::PrepareStepInSuspendedGenerator() { void Debug::PrepareStepInSuspendedGenerator() {
...@@ -928,7 +889,7 @@ void Debug::PrepareStepInSuspendedGenerator() { ...@@ -928,7 +889,7 @@ void Debug::PrepareStepInSuspendedGenerator() {
UpdateHookOnFunctionCall(); UpdateHookOnFunctionCall();
Handle<JSFunction> function( Handle<JSFunction> function(
JSGeneratorObject::cast(thread_local_.suspended_generator_)->function()); JSGeneratorObject::cast(thread_local_.suspended_generator_)->function());
FloodWithOneShot(function); FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
clear_suspended_generator(); clear_suspended_generator();
} }
...@@ -940,32 +901,68 @@ void Debug::PrepareStepOnThrow() { ...@@ -940,32 +901,68 @@ void Debug::PrepareStepOnThrow() {
ClearOneShot(); ClearOneShot();
int current_frame_count = CurrentFrameCount();
// Iterate through the JavaScript stack looking for handlers. // Iterate through the JavaScript stack looking for handlers.
JavaScriptFrameIterator it(isolate_); JavaScriptFrameIterator it(isolate_);
while (!it.done()) { while (!it.done()) {
JavaScriptFrame* frame = it.frame(); JavaScriptFrame* frame = it.frame();
if (frame->LookupExceptionHandlerInTable(nullptr, nullptr) > 0) break; if (frame->LookupExceptionHandlerInTable(nullptr, nullptr) > 0) break;
List<SharedFunctionInfo*> infos;
frame->GetFunctions(&infos);
current_frame_count -= infos.length();
it.Advance(); it.Advance();
} }
if (last_step_action() == StepNext || last_step_action() == StepOut) { // No handler found. Nothing to instrument.
while (!it.done()) { if (it.done()) return;
Address current_fp = it.frame()->UnpaddedFP();
if (current_fp >= thread_local_.target_fp_) break; bool found_handler = false;
it.Advance(); // Iterate frames, including inlined frames. First, find the handler frame.
// Then skip to the frame we want to break in, then instrument for stepping.
for (; !it.done(); it.Advance()) {
JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame());
if (last_step_action() == StepIn) {
// Deoptimize frame to ensure calls are checked for step-in.
Deoptimizer::DeoptimizeFunction(frame->function());
} }
} List<FrameSummary> summaries;
frame->Summarize(&summaries);
for (int i = summaries.length() - 1; i >= 0; i--, current_frame_count--) {
if (!found_handler) {
// We have yet to find the handler. If the frame inlines multiple
// functions, we have to check each one for the handler.
// If it only contains one function, we already found the handler.
if (summaries.length() > 1) {
Handle<AbstractCode> code =
summaries[i].AsJavaScript().abstract_code();
CHECK_EQ(AbstractCode::INTERPRETED_FUNCTION, code->kind());
BytecodeArray* bytecode = code->GetBytecodeArray();
HandlerTable* table = HandlerTable::cast(bytecode->handler_table());
int code_offset = summaries[i].code_offset();
HandlerTable::CatchPrediction prediction;
int index = table->LookupRange(code_offset, nullptr, &prediction);
if (index > 0) found_handler = true;
} else {
found_handler = true;
}
}
// Find the closest Javascript frame we can flood with one-shots. if (found_handler) {
while (!it.done() && // We found the handler. If we are stepping next or out, we need to
(!it.frame()->function()->shared()->IsSubjectToDebugging() || // iterate until we found the suitable target frame to break in.
IsBlackboxed(it.frame()->function()->shared()))) { if ((last_step_action() == StepNext || last_step_action() == StepOut) &&
it.Advance(); current_frame_count > thread_local_.target_frame_count_) {
continue;
}
Handle<SharedFunctionInfo> info(
summaries[i].AsJavaScript().function()->shared());
if (!info->IsSubjectToDebugging() || IsBlackboxed(info)) continue;
FloodWithOneShot(info);
return;
}
}
} }
if (it.done()) return; // No suitable Javascript catch handler.
FloodWithOneShot(Handle<JSFunction>(it.frame()->function()));
} }
...@@ -982,14 +979,14 @@ void Debug::PrepareStep(StepAction step_action) { ...@@ -982,14 +979,14 @@ void Debug::PrepareStep(StepAction step_action) {
// If there is no JavaScript stack don't do anything. // If there is no JavaScript stack don't do anything.
if (frame_id == StackFrame::NO_ID) return; if (frame_id == StackFrame::NO_ID) return;
StackTraceFrameIterator frames_it(isolate_, frame_id);
StandardFrame* frame = frames_it.frame();
feature_tracker()->Track(DebugFeatureTracker::kStepping); feature_tracker()->Track(DebugFeatureTracker::kStepping);
thread_local_.last_step_action_ = step_action; thread_local_.last_step_action_ = step_action;
UpdateHookOnFunctionCall(); UpdateHookOnFunctionCall();
StackTraceFrameIterator frames_it(isolate_, frame_id);
StandardFrame* frame = frames_it.frame();
// Handle stepping in wasm functions via the wasm interpreter. // Handle stepping in wasm functions via the wasm interpreter.
if (frame->is_wasm()) { if (frame->is_wasm()) {
// If the top frame is compiled, we cannot step. // If the top frame is compiled, we cannot step.
...@@ -1001,30 +998,15 @@ void Debug::PrepareStep(StepAction step_action) { ...@@ -1001,30 +998,15 @@ void Debug::PrepareStep(StepAction step_action) {
} }
JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame); JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame);
DCHECK(js_frame->function()->IsJSFunction());
// If the function on the top frame is unresolved perform step out. This will
// be the case when calling unknown function and having the debugger stopped
// in an unhandled exception.
if (!js_frame->function()->IsJSFunction()) {
// Step out: Find the calling JavaScript frame and flood it with
// breakpoints.
frames_it.Advance();
// Fill the function to return to with one-shot break points.
JSFunction* function = JavaScriptFrame::cast(frames_it.frame())->function();
FloodWithOneShot(handle(function, isolate_));
return;
}
// Get the debug info (create it if it does not exist). // Get the debug info (create it if it does not exist).
auto summary = FrameSummary::GetTop(frame).AsJavaScript(); auto summary = FrameSummary::GetTop(frame).AsJavaScript();
Handle<JSFunction> function(summary.function()); Handle<JSFunction> function(summary.function());
Handle<SharedFunctionInfo> shared(function->shared()); Handle<SharedFunctionInfo> shared(function->shared());
if (!EnsureDebugInfo(shared, function)) { if (!EnsureDebugInfo(shared)) return;
// Return if ensuring debug info failed.
return;
}
Handle<DebugInfo> debug_info(shared->GetDebugInfo()); Handle<DebugInfo> debug_info(shared->GetDebugInfo());
BreakLocation location = BreakLocation::FromFrame(debug_info, js_frame); BreakLocation location = BreakLocation::FromFrame(debug_info, js_frame);
// Any step at a return is a step-out. // Any step at a return is a step-out.
...@@ -1036,7 +1018,8 @@ void Debug::PrepareStep(StepAction step_action) { ...@@ -1036,7 +1018,8 @@ void Debug::PrepareStep(StepAction step_action) {
thread_local_.last_statement_position_ = thread_local_.last_statement_position_ =
summary.abstract_code()->SourceStatementPosition(summary.code_offset()); summary.abstract_code()->SourceStatementPosition(summary.code_offset());
thread_local_.last_fp_ = frame->UnpaddedFP(); int current_frame_count = CurrentFrameCount();
thread_local_.last_frame_count_ = current_frame_count;
// No longer perform the current async step. // No longer perform the current async step.
clear_suspended_generator(); clear_suspended_generator();
...@@ -1044,41 +1027,45 @@ void Debug::PrepareStep(StepAction step_action) { ...@@ -1044,41 +1027,45 @@ void Debug::PrepareStep(StepAction step_action) {
case StepNone: case StepNone:
UNREACHABLE(); UNREACHABLE();
break; break;
case StepOut: case StepOut: {
// Advance to caller frame. // Clear last position info. For stepping out it does not matter.
frames_it.Advance(); thread_local_.last_statement_position_ = kNoSourcePosition;
// Find top-most function which is subject to debugging. thread_local_.last_frame_count_ = -1;
while (!frames_it.done()) { // Skip the current frame, find the first frame we want to step out to
StandardFrame* caller_frame = frames_it.frame(); // and deoptimize every frame along the way.
if (caller_frame->is_wasm()) { bool in_current_frame = true;
// TODO(clemensh): Implement stepping out from JS to WASM. for (; !frames_it.done(); frames_it.Advance()) {
break; // TODO(clemensh): Implement stepping out from JS to WASM.
if (frames_it.frame()->is_wasm()) continue;
JavaScriptFrame* frame = JavaScriptFrame::cast(frames_it.frame());
if (last_step_action() == StepIn) {
// Deoptimize frame to ensure calls are checked for step-in.
Deoptimizer::DeoptimizeFunction(frame->function());
} }
Handle<JSFunction> js_caller_function( HandleScope scope(isolate_);
JavaScriptFrame::cast(caller_frame)->function(), isolate_); List<Handle<SharedFunctionInfo>> infos;
if (js_caller_function->shared()->IsSubjectToDebugging() && frame->GetFunctions(&infos);
!IsBlackboxed(js_caller_function->shared())) { for (; !infos.is_empty(); current_frame_count--) {
// Fill the caller function to return to with one-shot break points. Handle<SharedFunctionInfo> info = infos.RemoveLast();
FloodWithOneShot(js_caller_function); if (in_current_frame) {
thread_local_.target_fp_ = frames_it.frame()->UnpaddedFP(); // We want to skip out, so skip the current frame.
break; in_current_frame = false;
continue;
}
if (!info->IsSubjectToDebugging() || IsBlackboxed(info)) continue;
FloodWithOneShot(info);
thread_local_.target_frame_count_ = current_frame_count;
return;
} }
// Builtin functions are not subject to stepping, but need to be
// deoptimized to include checks for step-in at call sites.
Deoptimizer::DeoptimizeFunction(*js_caller_function);
frames_it.Advance();
} }
// Clear last position info. For stepping out it does not matter.
thread_local_.last_statement_position_ = kNoSourcePosition;
thread_local_.last_fp_ = 0;
break; break;
}
case StepNext: case StepNext:
thread_local_.target_fp_ = frame->UnpaddedFP(); thread_local_.target_frame_count_ = current_frame_count;
FloodWithOneShot(function); // Fall through.
break;
case StepIn: case StepIn:
// TODO(clemensh): Implement stepping from JS into WASM. // TODO(clemensh): Implement stepping from JS into WASM.
FloodWithOneShot(function); FloodWithOneShot(shared);
break; break;
} }
} }
...@@ -1107,13 +1094,13 @@ Handle<Object> Debug::GetSourceBreakLocations( ...@@ -1107,13 +1094,13 @@ Handle<Object> Debug::GetSourceBreakLocations(
Smi* position = NULL; Smi* position = NULL;
if (position_alignment == STATEMENT_ALIGNED) { if (position_alignment == STATEMENT_ALIGNED) {
if (debug_info->HasDebugCode()) { if (debug_info->HasDebugCode()) {
CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); CodeBreakIterator it(debug_info);
it.SkipToPosition(break_point_info->source_position(), it.SkipToPosition(break_point_info->source_position(),
BREAK_POSITION_ALIGNED); BREAK_POSITION_ALIGNED);
position = Smi::FromInt(it.statement_position()); position = Smi::FromInt(it.statement_position());
} else { } else {
DCHECK(debug_info->HasDebugBytecodeArray()); DCHECK(debug_info->HasDebugBytecodeArray());
BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); BytecodeArrayBreakIterator it(debug_info);
it.SkipToPosition(break_point_info->source_position(), it.SkipToPosition(break_point_info->source_position(),
BREAK_POSITION_ALIGNED); BREAK_POSITION_ALIGNED);
position = Smi::FromInt(it.statement_position()); position = Smi::FromInt(it.statement_position());
...@@ -1134,8 +1121,8 @@ void Debug::ClearStepping() { ...@@ -1134,8 +1121,8 @@ void Debug::ClearStepping() {
thread_local_.last_step_action_ = StepNone; thread_local_.last_step_action_ = StepNone;
thread_local_.last_statement_position_ = kNoSourcePosition; thread_local_.last_statement_position_ = kNoSourcePosition;
thread_local_.last_fp_ = 0; thread_local_.last_frame_count_ = -1;
thread_local_.target_fp_ = 0; thread_local_.target_frame_count_ = -1;
UpdateHookOnFunctionCall(); UpdateHookOnFunctionCall();
} }
...@@ -1354,12 +1341,12 @@ void FindBreakablePositions(Handle<DebugInfo> debug_info, int start_position, ...@@ -1354,12 +1341,12 @@ void FindBreakablePositions(Handle<DebugInfo> debug_info, int start_position,
int end_position, BreakPositionAlignment alignment, int end_position, BreakPositionAlignment alignment,
std::set<int>* positions) { std::set<int>* positions) {
if (debug_info->HasDebugCode()) { if (debug_info->HasDebugCode()) {
CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); CodeBreakIterator it(debug_info);
GetBreakablePositions(&it, start_position, end_position, alignment, GetBreakablePositions(&it, start_position, end_position, alignment,
positions); positions);
} else { } else {
DCHECK(debug_info->HasDebugBytecodeArray()); DCHECK(debug_info->HasDebugBytecodeArray());
BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); BytecodeArrayBreakIterator it(debug_info);
GetBreakablePositions(&it, start_position, end_position, alignment, GetBreakablePositions(&it, start_position, end_position, alignment,
positions); positions);
} }
...@@ -1394,8 +1381,7 @@ bool Debug::GetPossibleBreakpoints(Handle<Script> script, int start_position, ...@@ -1394,8 +1381,7 @@ bool Debug::GetPossibleBreakpoints(Handle<Script> script, int start_position,
was_compiled = true; was_compiled = true;
} }
} }
if (!EnsureDebugInfo(candidates[i], Handle<JSFunction>::null())) if (!EnsureDebugInfo(candidates[i])) return false;
return false;
} }
if (was_compiled) continue; if (was_compiled) continue;
...@@ -1416,9 +1402,7 @@ void Debug::RecordGenerator(Handle<JSGeneratorObject> generator_object) { ...@@ -1416,9 +1402,7 @@ void Debug::RecordGenerator(Handle<JSGeneratorObject> generator_object) {
if (last_step_action() == StepNext) { if (last_step_action() == StepNext) {
// Only consider this generator a step-next target if not stepping in. // Only consider this generator a step-next target if not stepping in.
JavaScriptFrameIterator stack_iterator(isolate_); if (thread_local_.target_frame_count_ < CurrentFrameCount()) return;
JavaScriptFrame* frame = stack_iterator.frame();
if (frame->UnpaddedFP() < thread_local_.target_fp_) return;
} }
DCHECK(!has_suspended_generator()); DCHECK(!has_suspended_generator());
...@@ -1526,16 +1510,11 @@ Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script, ...@@ -1526,16 +1510,11 @@ Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
// Ensures the debug information is present for shared. // Ensures the debug information is present for shared.
bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared, bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared) {
Handle<JSFunction> function) {
if (!shared->IsSubjectToDebugging()) return false;
// Return if we already have the debug info for shared. // Return if we already have the debug info for shared.
if (shared->HasDebugInfo()) return true; if (shared->HasDebugInfo()) return true;
if (!shared->IsSubjectToDebugging()) return false;
if (function.is_null()) { if (!shared->is_compiled() && !Compiler::CompileDebugCode(shared)) {
DCHECK(shared->HasDebugCode());
} else if (!Compiler::Compile(function, Compiler::CLEAR_EXCEPTION)) {
return false; return false;
} }
...@@ -1706,7 +1685,6 @@ MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<Smi> type, ...@@ -1706,7 +1685,6 @@ MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<Smi> type,
void Debug::OnThrow(Handle<Object> exception) { void Debug::OnThrow(Handle<Object> exception) {
if (in_debug_scope() || ignore_events()) return; if (in_debug_scope() || ignore_events()) return;
PrepareStepOnThrow();
// Temporarily clear any scheduled_exception to allow evaluating // Temporarily clear any scheduled_exception to allow evaluating
// JavaScript from the debug event handler. // JavaScript from the debug event handler.
HandleScope scope(isolate_); HandleScope scope(isolate_);
...@@ -1719,6 +1697,7 @@ void Debug::OnThrow(Handle<Object> exception) { ...@@ -1719,6 +1697,7 @@ void Debug::OnThrow(Handle<Object> exception) {
if (!scheduled_exception.is_null()) { if (!scheduled_exception.is_null()) {
isolate_->thread_local_top()->scheduled_exception_ = *scheduled_exception; isolate_->thread_local_top()->scheduled_exception_ = *scheduled_exception;
} }
PrepareStepOnThrow();
} }
void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) { void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) {
...@@ -1762,17 +1741,13 @@ bool Debug::IsExceptionBlackboxed(bool uncaught) { ...@@ -1762,17 +1741,13 @@ bool Debug::IsExceptionBlackboxed(bool uncaught) {
bool Debug::IsFrameBlackboxed(JavaScriptFrame* frame) { bool Debug::IsFrameBlackboxed(JavaScriptFrame* frame) {
HandleScope scope(isolate_); HandleScope scope(isolate_);
if (!frame->HasInlinedFrames()) { if (!frame->HasInlinedFrames()) {
return IsBlackboxed(frame->function()->shared()); Handle<SharedFunctionInfo> shared(frame->function()->shared(), isolate_);
} return IsBlackboxed(shared);
List<SharedFunctionInfo*> raw_shareds;
frame->GetFunctions(&raw_shareds);
List<Handle<SharedFunctionInfo>> shareds;
for (int i = 0; i < raw_shareds.length(); ++i) {
shareds.Add(handle(raw_shareds[i]));
}
for (int i = 0; i < shareds.length(); ++i) {
if (!IsBlackboxed(shareds[i])) return false;
} }
List<Handle<SharedFunctionInfo>> infos;
frame->GetFunctions(&infos);
for (const auto& info : infos)
if (!IsBlackboxed(info)) return false;
return true; return true;
} }
...@@ -2013,12 +1988,6 @@ debug::Location GetDebugLocation(Handle<Script> script, int source_position) { ...@@ -2013,12 +1988,6 @@ debug::Location GetDebugLocation(Handle<Script> script, int source_position) {
} }
} // namespace } // namespace
bool Debug::IsBlackboxed(SharedFunctionInfo* shared) {
HandleScope scope(isolate_);
Handle<SharedFunctionInfo> shared_function_info(shared);
return IsBlackboxed(shared_function_info);
}
bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) { bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
if (!debug_delegate_) return false; if (!debug_delegate_) return false;
if (!shared->computed_debug_is_blackboxed()) { if (!shared->computed_debug_is_blackboxed()) {
...@@ -2162,6 +2131,27 @@ void Debug::SetEventListener(Handle<Object> callback, ...@@ -2162,6 +2131,27 @@ void Debug::SetEventListener(Handle<Object> callback,
UpdateState(); UpdateState();
} }
int Debug::CurrentFrameCount() {
StackTraceFrameIterator it(isolate_);
if (break_frame_id() != StackFrame::NO_ID) {
// Skip to break frame.
DCHECK(in_debug_scope());
while (!it.done() && it.frame()->id() != break_frame_id()) it.Advance();
}
int counter = 0;
while (!it.done()) {
if (it.frame()->is_optimized()) {
List<SharedFunctionInfo*> infos;
OptimizedFrame::cast(it.frame())->GetFunctions(&infos);
counter += infos.length();
} else {
counter++;
}
it.Advance();
}
return counter;
}
void Debug::SetDebugDelegate(debug::DebugDelegate* delegate) { void Debug::SetDebugDelegate(debug::DebugDelegate* delegate) {
debug_delegate_ = delegate; debug_delegate_ = delegate;
UpdateState(); UpdateState();
...@@ -2225,9 +2215,11 @@ void Debug::HandleDebugBreak() { ...@@ -2225,9 +2215,11 @@ void Debug::HandleDebugBreak() {
DCHECK(!it.done()); DCHECK(!it.done());
Object* fun = it.frame()->function(); Object* fun = it.frame()->function();
if (fun && fun->IsJSFunction()) { if (fun && fun->IsJSFunction()) {
HandleScope scope(isolate_);
// Don't stop in builtin and blackboxed functions. // Don't stop in builtin and blackboxed functions.
if (!JSFunction::cast(fun)->shared()->IsSubjectToDebugging() || Handle<SharedFunctionInfo> shared(JSFunction::cast(fun)->shared(),
IsBlackboxed(JSFunction::cast(fun)->shared())) { isolate_);
if (!shared->IsSubjectToDebugging() || IsBlackboxed(shared)) {
// Inspector uses pause on next statement for asynchronous breakpoints. // Inspector uses pause on next statement for asynchronous breakpoints.
// When breakpoint is fired we try to break on first not blackboxed // When breakpoint is fired we try to break on first not blackboxed
// statement. To achieve this goal we need to deoptimize current // statement. To achieve this goal we need to deoptimize current
......
...@@ -50,10 +50,6 @@ enum ExceptionBreakType { ...@@ -50,10 +50,6 @@ enum ExceptionBreakType {
}; };
// Type of exception break.
enum BreakLocatorType { ALL_BREAK_LOCATIONS, CALLS_AND_RETURNS };
// The different types of breakpoint position alignments. // The different types of breakpoint position alignments.
// Must match Debug.BreakPositionAlignment in debug.js // Must match Debug.BreakPositionAlignment in debug.js
enum BreakPositionAlignment { enum BreakPositionAlignment {
...@@ -122,8 +118,7 @@ class BreakLocation { ...@@ -122,8 +118,7 @@ class BreakLocation {
class BreakIterator { class BreakIterator {
public: public:
static std::unique_ptr<BreakIterator> GetIterator( static std::unique_ptr<BreakIterator> GetIterator(
Handle<DebugInfo> debug_info, Handle<AbstractCode> abstract_code, Handle<DebugInfo> debug_info, Handle<AbstractCode> abstract_code);
BreakLocatorType type = ALL_BREAK_LOCATIONS);
virtual ~BreakIterator() {} virtual ~BreakIterator() {}
...@@ -145,8 +140,7 @@ class BreakIterator { ...@@ -145,8 +140,7 @@ class BreakIterator {
virtual void SetDebugBreak() = 0; virtual void SetDebugBreak() = 0;
protected: protected:
explicit BreakIterator(Handle<DebugInfo> debug_info, explicit BreakIterator(Handle<DebugInfo> debug_info);
BreakLocatorType break_locator_type);
int BreakIndexFromPosition(int position, BreakPositionAlignment alignment); int BreakIndexFromPosition(int position, BreakPositionAlignment alignment);
...@@ -156,7 +150,6 @@ class BreakIterator { ...@@ -156,7 +150,6 @@ class BreakIterator {
int break_index_; int break_index_;
int position_; int position_;
int statement_position_; int statement_position_;
BreakLocatorType break_locator_type_;
private: private:
DisallowHeapAllocation no_gc_; DisallowHeapAllocation no_gc_;
...@@ -165,7 +158,7 @@ class BreakIterator { ...@@ -165,7 +158,7 @@ class BreakIterator {
class CodeBreakIterator : public BreakIterator { class CodeBreakIterator : public BreakIterator {
public: public:
CodeBreakIterator(Handle<DebugInfo> debug_info, BreakLocatorType type); explicit CodeBreakIterator(Handle<DebugInfo> debug_info);
~CodeBreakIterator() override {} ~CodeBreakIterator() override {}
BreakLocation GetBreakLocation() override; BreakLocation GetBreakLocation() override;
...@@ -184,7 +177,7 @@ class CodeBreakIterator : public BreakIterator { ...@@ -184,7 +177,7 @@ class CodeBreakIterator : public BreakIterator {
} }
private: private:
int GetModeMask(BreakLocatorType type); int GetModeMask();
DebugBreakType GetDebugBreakType(); DebugBreakType GetDebugBreakType();
RelocInfo::Mode rmode() { return reloc_iterator_.rinfo()->rmode(); } RelocInfo::Mode rmode() { return reloc_iterator_.rinfo()->rmode(); }
...@@ -197,8 +190,7 @@ class CodeBreakIterator : public BreakIterator { ...@@ -197,8 +190,7 @@ class CodeBreakIterator : public BreakIterator {
class BytecodeArrayBreakIterator : public BreakIterator { class BytecodeArrayBreakIterator : public BreakIterator {
public: public:
BytecodeArrayBreakIterator(Handle<DebugInfo> debug_info, explicit BytecodeArrayBreakIterator(Handle<DebugInfo> debug_info);
BreakLocatorType type);
~BytecodeArrayBreakIterator() override {} ~BytecodeArrayBreakIterator() override {}
BreakLocation GetBreakLocation() override; BreakLocation GetBreakLocation() override;
...@@ -357,11 +349,8 @@ class Debug { ...@@ -357,11 +349,8 @@ class Debug {
void SetDebugDelegate(debug::DebugDelegate* delegate); void SetDebugDelegate(debug::DebugDelegate* delegate);
// Returns whether the operation succeeded. Compilation can only be triggered // Returns whether the operation succeeded.
// if a valid closure is passed as the second argument, otherwise the shared bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared);
// function needs to be compiled already.
bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared,
Handle<JSFunction> function);
void CreateDebugInfo(Handle<SharedFunctionInfo> shared); void CreateDebugInfo(Handle<SharedFunctionInfo> shared);
static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared); static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared);
...@@ -466,6 +455,9 @@ class Debug { ...@@ -466,6 +455,9 @@ class Debug {
thread_local_.break_id_ = ++thread_local_.break_count_; thread_local_.break_id_ = ++thread_local_.break_count_;
} }
// Return the number of virtual frames below debugger entry.
int CurrentFrameCount();
inline bool ignore_events() const { inline bool ignore_events() const {
return is_suppressed_ || !is_active_ || isolate_->needs_side_effect_check(); return is_suppressed_ || !is_active_ || isolate_->needs_side_effect_check();
} }
...@@ -492,7 +484,6 @@ class Debug { ...@@ -492,7 +484,6 @@ class Debug {
return !event_listener_.is_null() && !event_listener_->IsForeign(); return !event_listener_.is_null() && !event_listener_->IsForeign();
} }
bool IsBlackboxed(SharedFunctionInfo* shared);
bool IsExceptionBlackboxed(bool uncaught); bool IsExceptionBlackboxed(bool uncaught);
void OnException(Handle<Object> exception, Handle<Object> promise); void OnException(Handle<Object> exception, Handle<Object> promise);
...@@ -526,8 +517,7 @@ class Debug { ...@@ -526,8 +517,7 @@ class Debug {
// Clear all code from instrumentation. // Clear all code from instrumentation.
void ClearAllBreakPoints(); void ClearAllBreakPoints();
// Instrument a function with one-shots. // Instrument a function with one-shots.
void FloodWithOneShot(Handle<JSFunction> function, void FloodWithOneShot(Handle<SharedFunctionInfo> function);
BreakLocatorType type = ALL_BREAK_LOCATIONS);
// Clear all one-shot instrumentations, but restore break points. // Clear all one-shot instrumentations, but restore break points.
void ClearOneShot(); void ClearOneShot();
...@@ -607,10 +597,10 @@ class Debug { ...@@ -607,10 +597,10 @@ class Debug {
int last_statement_position_; int last_statement_position_;
// Frame pointer from last step next or step frame action. // Frame pointer from last step next or step frame action.
Address last_fp_; int last_frame_count_;
// Frame pointer of the target frame we want to arrive at. // Frame pointer of the target frame we want to arrive at.
Address target_fp_; int target_frame_count_;
// Value of the accumulator at the point of entering the debugger. // Value of the accumulator at the point of entering the debugger.
Object* return_value_; Object* return_value_;
......
...@@ -954,6 +954,16 @@ void JavaScriptFrame::GetFunctions(List<SharedFunctionInfo*>* functions) const { ...@@ -954,6 +954,16 @@ void JavaScriptFrame::GetFunctions(List<SharedFunctionInfo*>* functions) const {
functions->Add(function()->shared()); functions->Add(function()->shared());
} }
void JavaScriptFrame::GetFunctions(
List<Handle<SharedFunctionInfo>>* functions) const {
DCHECK(functions->length() == 0);
List<SharedFunctionInfo*> raw_functions;
GetFunctions(&raw_functions);
for (const auto& raw_function : raw_functions) {
functions->Add(Handle<SharedFunctionInfo>(raw_function));
}
}
void JavaScriptFrame::Summarize(List<FrameSummary>* functions, void JavaScriptFrame::Summarize(List<FrameSummary>* functions,
FrameSummary::Mode mode) const { FrameSummary::Mode mode) const {
DCHECK(functions->length() == 0); DCHECK(functions->length() == 0);
......
...@@ -1029,6 +1029,8 @@ class JavaScriptFrame : public StandardFrame { ...@@ -1029,6 +1029,8 @@ class JavaScriptFrame : public StandardFrame {
// Return a list with {SharedFunctionInfo} objects of this frame. // Return a list with {SharedFunctionInfo} objects of this frame.
virtual void GetFunctions(List<SharedFunctionInfo*>* functions) const; virtual void GetFunctions(List<SharedFunctionInfo*>* functions) const;
void GetFunctions(List<Handle<SharedFunctionInfo>>* functions) const;
// Lookup exception handler for current {pc}, returns -1 if none found. Also // Lookup exception handler for current {pc}, returns -1 if none found. Also
// returns data associated with the handler site specific to the frame type: // returns data associated with the handler site specific to the frame type:
// - OptimizedFrame : Data is the stack slot count of the entire frame. // - OptimizedFrame : Data is the stack slot count of the entire frame.
......
...@@ -6456,14 +6456,13 @@ TEST(BreakLocationIterator) { ...@@ -6456,14 +6456,13 @@ TEST(BreakLocationIterator) {
Handle<i::SharedFunctionInfo> shared(function->shared()); Handle<i::SharedFunctionInfo> shared(function->shared());
EnableDebugger(isolate); EnableDebugger(isolate);
CHECK(i_isolate->debug()->EnsureDebugInfo(shared, function)); CHECK(i_isolate->debug()->EnsureDebugInfo(shared));
Handle<i::DebugInfo> debug_info(shared->GetDebugInfo()); Handle<i::DebugInfo> debug_info(shared->GetDebugInfo());
Handle<i::AbstractCode> abstract_code(shared->abstract_code()); Handle<i::AbstractCode> abstract_code(shared->abstract_code());
{ {
auto iterator = i::BreakIterator::GetIterator(debug_info, abstract_code, auto iterator = i::BreakIterator::GetIterator(debug_info, abstract_code);
i::ALL_BREAK_LOCATIONS);
CHECK(iterator->GetBreakLocation().IsDebuggerStatement()); CHECK(iterator->GetBreakLocation().IsDebuggerStatement());
CHECK_EQ(17, iterator->GetBreakLocation().position()); CHECK_EQ(17, iterator->GetBreakLocation().position());
iterator->Next(); iterator->Next();
...@@ -6482,18 +6481,6 @@ TEST(BreakLocationIterator) { ...@@ -6482,18 +6481,6 @@ TEST(BreakLocationIterator) {
CHECK(iterator->Done()); CHECK(iterator->Done());
} }
{
auto iterator = i::BreakIterator::GetIterator(debug_info, abstract_code,
i::CALLS_AND_RETURNS);
CHECK(iterator->GetBreakLocation().IsCall());
CHECK_EQ(32, iterator->GetBreakLocation().position());
iterator->Next();
CHECK(iterator->GetBreakLocation().IsReturn());
CHECK_EQ(60, iterator->GetBreakLocation().position());
iterator->Next();
CHECK(iterator->Done());
}
DisableDebugger(isolate); DisableDebugger(isolate);
} }
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --ignition --turbo
function f() {
throw new Error();
}
function g() {
try {
f();
} catch (e) {
return 1; // Break
}
}
function h() {
return g();
}
h();
h();
var Debug = debug.Debug;
var step_count = 0;
var exception = null;
function listener(event, exec_state, event_data, data) {
if (event == Debug.DebugEvent.Exception) {
step_count++;
exec_state.prepareStep(Debug.StepAction.StepNext);
} else if (event == Debug.DebugEvent.Break) {
step_count++;
try {
assertTrue(exec_state.frame().sourceLineText().includes('Break'));
} catch (e) {
exception = e;
print(e);
}
}
}
Debug.setListener(listener);
Debug.setBreakOnException();
% OptimizeFunctionOnNextCall(h);
h();
Debug.setListener(null);
assertNull(exception);
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
function f() {
debugger;
return 1;
}
function g() {
return f();
} // Break
function h() {
return g();
}
h();
h();
var Debug = debug.Debug;
var step_count = 0;
var exception = null;
function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return;
try {
if (step_count == 0) {
exec_state.prepareStep(Debug.StepAction.StepOut);
} else {
assertTrue(exec_state.frame().sourceLineText().includes('Break'));
}
step_count++;
} catch (e) {
exception = e;
print(e);
}
}
Debug.setListener(listener);
% OptimizeFunctionOnNextCall(h);
h();
Debug.setListener(null);
assertNull(exception);
assertEquals(2, step_count);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment