| Index: src/debug/debug.cc
|
| diff --git a/src/debug/debug.cc b/src/debug/debug.cc
|
| index b212aad033eb50470073d6c4f4f21ca2cdcca1de..955f86618213482fcee80aa69fc321ddf69ef27a 100644
|
| --- a/src/debug/debug.cc
|
| +++ b/src/debug/debug.cc
|
| @@ -145,9 +145,9 @@ void BreakLocation::Iterator::Next() {
|
| // Find the break point at the supplied address, or the closest one before
|
| // the address.
|
| BreakLocation BreakLocation::FromAddress(Handle<DebugInfo> debug_info,
|
| - BreakLocatorType type, Address pc) {
|
| - Iterator it(debug_info, type);
|
| - it.SkipTo(BreakIndexFromAddress(debug_info, type, pc));
|
| + Address pc) {
|
| + Iterator it(debug_info, ALL_BREAK_LOCATIONS);
|
| + it.SkipTo(BreakIndexFromAddress(debug_info, pc));
|
| return it.GetBreakLocation();
|
| }
|
|
|
| @@ -155,10 +155,10 @@ BreakLocation BreakLocation::FromAddress(Handle<DebugInfo> debug_info,
|
| // Find the break point at the supplied address, or the closest one before
|
| // the address.
|
| void BreakLocation::FromAddressSameStatement(Handle<DebugInfo> debug_info,
|
| - BreakLocatorType type, Address pc,
|
| + Address pc,
|
| List<BreakLocation>* result_out) {
|
| - int break_index = BreakIndexFromAddress(debug_info, type, pc);
|
| - Iterator it(debug_info, type);
|
| + int break_index = BreakIndexFromAddress(debug_info, pc);
|
| + Iterator it(debug_info, ALL_BREAK_LOCATIONS);
|
| it.SkipTo(break_index);
|
| int statement_position = it.statement_position();
|
| while (!it.Done() && it.statement_position() == statement_position) {
|
| @@ -169,11 +169,11 @@ void BreakLocation::FromAddressSameStatement(Handle<DebugInfo> debug_info,
|
|
|
|
|
| int BreakLocation::BreakIndexFromAddress(Handle<DebugInfo> debug_info,
|
| - BreakLocatorType type, Address pc) {
|
| + Address pc) {
|
| // Run through all break points to locate the one closest to the address.
|
| int closest_break = 0;
|
| int distance = kMaxInt;
|
| - for (Iterator it(debug_info, type); !it.Done(); it.Next()) {
|
| + for (Iterator it(debug_info, ALL_BREAK_LOCATIONS); !it.Done(); it.Next()) {
|
| // Check if this break point is closer that what was previously found.
|
| if (it.pc() <= pc && pc - it.pc() < distance) {
|
| closest_break = it.break_index();
|
| @@ -187,14 +187,14 @@ int BreakLocation::BreakIndexFromAddress(Handle<DebugInfo> debug_info,
|
|
|
|
|
| BreakLocation BreakLocation::FromPosition(Handle<DebugInfo> debug_info,
|
| - BreakLocatorType type, int position,
|
| + int position,
|
| BreakPositionAlignment alignment) {
|
| // Run through all break points to locate the one closest to the source
|
| // position.
|
| int closest_break = 0;
|
| int distance = kMaxInt;
|
|
|
| - for (Iterator it(debug_info, type); !it.Done(); it.Next()) {
|
| + for (Iterator it(debug_info, ALL_BREAK_LOCATIONS); !it.Done(); it.Next()) {
|
| int next_position;
|
| if (alignment == STATEMENT_ALIGNED) {
|
| next_position = it.statement_position();
|
| @@ -210,7 +210,7 @@ BreakLocation BreakLocation::FromPosition(Handle<DebugInfo> debug_info,
|
| }
|
| }
|
|
|
| - Iterator it(debug_info, type);
|
| + Iterator it(debug_info, ALL_BREAK_LOCATIONS);
|
| it.SkipTo(closest_break);
|
| return it.GetBreakLocation();
|
| }
|
| @@ -327,9 +327,8 @@ void Debug::ThreadInit() {
|
| thread_local_.break_frame_id_ = StackFrame::NO_ID;
|
| thread_local_.last_step_action_ = StepNone;
|
| thread_local_.last_statement_position_ = RelocInfo::kNoPosition;
|
| - thread_local_.step_count_ = 0;
|
| thread_local_.last_fp_ = 0;
|
| - thread_local_.step_out_fp_ = 0;
|
| + thread_local_.target_fp_ = 0;
|
| thread_local_.step_in_enabled_ = false;
|
| // TODO(isolates): frames_are_dropped_?
|
| base::NoBarrier_Store(&thread_local_.current_debug_scope_,
|
| @@ -419,7 +418,6 @@ void Debug::Unload() {
|
|
|
|
|
| void Debug::Break(Arguments args, JavaScriptFrame* frame) {
|
| - Heap* heap = isolate_->heap();
|
| HandleScope scope(isolate_);
|
| DCHECK(args.length() == 0);
|
|
|
| @@ -445,61 +443,62 @@ void Debug::Break(Arguments args, JavaScriptFrame* frame) {
|
| }
|
| Handle<DebugInfo> debug_info(shared->GetDebugInfo());
|
|
|
| - // Find the break point where execution has stopped.
|
| + // Find the break location where execution has stopped.
|
| // PC points to the instruction after the current one, possibly a break
|
| // location as well. So the "- 1" to exclude it from the search.
|
| Address call_pc = frame->pc() - 1;
|
| - BreakLocation break_location =
|
| - BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
|
| -
|
| - // Check whether step next reached a new statement.
|
| - if (!StepNextContinue(&break_location, frame)) {
|
| - // Decrease steps left if performing multiple steps.
|
| - if (thread_local_.step_count_ > 0) {
|
| - thread_local_.step_count_--;
|
| + BreakLocation location = BreakLocation::FromAddress(debug_info, call_pc);
|
| +
|
| + // Find actual break points, if any, and trigger debug break event.
|
| + if (break_points_active_ && location.HasBreakPoint()) {
|
| + Handle<Object> break_point_objects = location.BreakPointObjects();
|
| + Handle<Object> break_points_hit = CheckBreakPoints(break_point_objects);
|
| + if (!break_points_hit->IsUndefined()) {
|
| + // Clear all current stepping setup.
|
| + ClearStepping();
|
| + // Notify the debug event listeners.
|
| + OnDebugBreak(break_points_hit, false);
|
| + return;
|
| }
|
| }
|
|
|
| - // If there is one or more real break points check whether any of these are
|
| - // triggered.
|
| - Handle<Object> break_points_hit(heap->undefined_value(), isolate_);
|
| - if (break_points_active_ && break_location.HasBreakPoint()) {
|
| - Handle<Object> break_point_objects = break_location.BreakPointObjects();
|
| - break_points_hit = CheckBreakPoints(break_point_objects);
|
| - }
|
| -
|
| - // If step out is active skip everything until the frame where we need to step
|
| - // out to is reached, unless real breakpoint is hit.
|
| - if (StepOutActive() &&
|
| - frame->fp() != thread_local_.step_out_fp_ &&
|
| - break_points_hit->IsUndefined() ) {
|
| - // Step count should always be 0 for StepOut.
|
| - DCHECK(thread_local_.step_count_ == 0);
|
| - } else if (!break_points_hit->IsUndefined() ||
|
| - (thread_local_.last_step_action_ != StepNone &&
|
| - thread_local_.step_count_ == 0)) {
|
| - // Notify debugger if a real break point is triggered or if performing
|
| - // single stepping with no more steps to perform. Otherwise do another step.
|
| -
|
| - // Clear all current stepping setup.
|
| - ClearStepping();
|
| - // Notify the debug event listeners.
|
| - OnDebugBreak(break_points_hit, false);
|
| - } else if (thread_local_.last_step_action_ != StepNone) {
|
| - // Hold on to last step action as it is cleared by the call to
|
| - // ClearStepping.
|
| - StepAction step_action = thread_local_.last_step_action_;
|
| - int step_count = thread_local_.step_count_;
|
| + // No break point. Check for stepping.
|
| + StepAction step_action = last_step_action();
|
| + Address current_fp = frame->UnpaddedFP();
|
| + Address target_fp = thread_local_.target_fp_;
|
| + Address last_fp = thread_local_.last_fp_;
|
|
|
| - // If StepNext goes deeper into code, just return. The functions we need
|
| - // to have flooded with one-shots are already flooded.
|
| - if (step_action == StepNext && frame->fp() < thread_local_.last_fp_) return;
|
| + bool step_break = true;
|
| + switch (step_action) {
|
| + case StepNone:
|
| + return;
|
| + case StepOut:
|
| + // Step out has not reached the target frame yet.
|
| + if (current_fp < target_fp) return;
|
| + break;
|
| + case StepNext:
|
| + // Step next should not break in a deeper frame.
|
| + if (current_fp < target_fp) return;
|
| + // Fall through.
|
| + case StepIn:
|
| + step_break = location.IsReturn() || (current_fp != last_fp) ||
|
| + (thread_local_.last_statement_position_ !=
|
| + location.code()->SourceStatementPosition(frame->pc()));
|
| + break;
|
| + case StepFrame:
|
| + step_break = current_fp != last_fp;
|
| + break;
|
| + }
|
|
|
| - // Clear all current stepping setup.
|
| - ClearStepping();
|
| + // Clear all current stepping setup.
|
| + ClearStepping();
|
|
|
| - // Set up for the remaining steps.
|
| - PrepareStep(step_action, step_count);
|
| + if (step_break) {
|
| + // Notify the debug event listeners.
|
| + OnDebugBreak(isolate_->factory()->undefined_value(), false);
|
| + } else {
|
| + // Re-prepare to continue.
|
| + PrepareStep(step_action);
|
| }
|
| }
|
|
|
| @@ -596,7 +595,7 @@ bool Debug::SetBreakPoint(Handle<JSFunction> function,
|
|
|
| // Find the break point and change it.
|
| BreakLocation location = BreakLocation::FromPosition(
|
| - debug_info, ALL_BREAK_LOCATIONS, *source_position, STATEMENT_ALIGNED);
|
| + debug_info, *source_position, STATEMENT_ALIGNED);
|
| *source_position = location.statement_position();
|
| location.SetBreakPoint(break_point_object);
|
|
|
| @@ -639,8 +638,8 @@ bool Debug::SetBreakPointForScript(Handle<Script> script,
|
| DCHECK(position >= 0);
|
|
|
| // Find the break point and change it.
|
| - BreakLocation location = BreakLocation::FromPosition(
|
| - debug_info, ALL_BREAK_LOCATIONS, position, alignment);
|
| + BreakLocation location =
|
| + BreakLocation::FromPosition(debug_info, position, alignment);
|
| location.SetBreakPoint(break_point_object);
|
|
|
| feature_tracker()->Track(DebugFeatureTracker::kBreakPoint);
|
| @@ -673,8 +672,7 @@ void Debug::ClearBreakPoint(Handle<Object> break_point_object) {
|
| Address pc =
|
| debug_info->code()->entry() + break_point_info->code_position();
|
|
|
| - BreakLocation location =
|
| - BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, pc);
|
| + BreakLocation location = BreakLocation::FromAddress(debug_info, pc);
|
| location.ClearBreakPoint(break_point_object);
|
|
|
| // If there are no more break points left remove the debug info for this
|
| @@ -762,11 +760,9 @@ FrameSummary GetFirstFrameSummary(JavaScriptFrame* frame) {
|
|
|
| void Debug::PrepareStepIn(Handle<JSFunction> function) {
|
| if (!is_active()) return;
|
| - if (!IsStepping()) return;
|
| if (last_step_action() < StepIn) return;
|
| if (in_debug_scope()) return;
|
| if (thread_local_.step_in_enabled_) {
|
| - ClearStepOut();
|
| FloodWithOneShot(function);
|
| }
|
| }
|
| @@ -774,7 +770,6 @@ void Debug::PrepareStepIn(Handle<JSFunction> function) {
|
|
|
| void Debug::PrepareStepOnThrow() {
|
| if (!is_active()) return;
|
| - if (!IsStepping()) return;
|
| if (last_step_action() == StepNone) return;
|
| if (in_debug_scope()) return;
|
|
|
| @@ -801,7 +796,7 @@ void Debug::PrepareStepOnThrow() {
|
| }
|
|
|
|
|
| -void Debug::PrepareStep(StepAction step_action, int step_count) {
|
| +void Debug::PrepareStep(StepAction step_action) {
|
| HandleScope scope(isolate_);
|
|
|
| DCHECK(in_debug_scope());
|
| @@ -823,13 +818,6 @@ void Debug::PrepareStep(StepAction step_action, int step_count) {
|
| thread_local_.last_step_action_ = step_action;
|
| STATIC_ASSERT(StepFrame > StepIn);
|
| thread_local_.step_in_enabled_ = (step_action >= StepIn);
|
| - if (step_action == StepOut) {
|
| - // For step out target frame will be found on the stack so there is no need
|
| - // to set step counter for it. It's expected to always be 0 for StepOut.
|
| - thread_local_.step_count_ = 0;
|
| - } else {
|
| - thread_local_.step_count_ = step_count;
|
| - }
|
|
|
| // 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
|
| @@ -860,98 +848,57 @@ void Debug::PrepareStep(StepAction step_action, int step_count) {
|
| // PC points to the instruction after the current one, possibly a break
|
| // location as well. So the "- 1" to exclude it from the search.
|
| Address call_pc = summary.pc() - 1;
|
| - BreakLocation location =
|
| - BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
|
| + BreakLocation location = BreakLocation::FromAddress(debug_info, call_pc);
|
|
|
| - // If this is the last break code target step out is the only possibility.
|
| - if (location.IsReturn() || step_action == StepOut) {
|
| - if (step_action == StepOut) {
|
| - // Skip step_count frames starting with the current one.
|
| - while (step_count-- > 0 && !frames_it.done()) {
|
| - frames_it.Advance();
|
| - }
|
| - } else {
|
| - DCHECK(location.IsReturn());
|
| - frames_it.Advance();
|
| - }
|
| - // Skip native and extension functions on the stack.
|
| - while (!frames_it.done() &&
|
| - !frames_it.frame()->function()->shared()->IsSubjectToDebugging()) {
|
| - if (step_action >= StepIn) {
|
| - // 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(frames_it.frame()->function());
|
| - }
|
| - frames_it.Advance();
|
| - }
|
| - // Step out: If there is a JavaScript caller frame, we need to
|
| - // flood it with breakpoints.
|
| - if (!frames_it.done()) {
|
| - // Fill the function to return to with one-shot break points.
|
| - JSFunction* function = frames_it.frame()->function();
|
| - FloodWithOneShot(Handle<JSFunction>(function));
|
| - // Set target frame pointer.
|
| - ActivateStepOut(frames_it.frame());
|
| - } else {
|
| - // Stepping out to the embedder. Disable step-in to avoid stepping into
|
| - // the next (unrelated) call that the embedder makes.
|
| - thread_local_.step_in_enabled_ = false;
|
| - }
|
| - return;
|
| - }
|
| + // At a return statement we will step out either way.
|
| + if (location.IsReturn()) step_action = StepOut;
|
|
|
| - // Fill the current function with one-shot break points even for step in on
|
| - // a call target as the function called might be a native function for
|
| - // which step in will not stop. It also prepares for stepping in
|
| - // getters/setters.
|
| - // If we are stepping into another frame, only fill calls and returns.
|
| - FloodWithOneShot(function, step_action == StepFrame ? CALLS_AND_RETURNS
|
| - : ALL_BREAK_LOCATIONS);
|
| -
|
| - // Remember source position and frame to handle step next.
|
| thread_local_.last_statement_position_ =
|
| debug_info->code()->SourceStatementPosition(summary.pc());
|
| thread_local_.last_fp_ = frame->UnpaddedFP();
|
| -}
|
| -
|
| -
|
| -// Check whether the current debug break should be reported to the debugger. It
|
| -// is used to have step next and step in only report break back to the debugger
|
| -// if on a different frame or in a different statement. In some situations
|
| -// there will be several break points in the same statement when the code is
|
| -// flooded with one-shot break points. This function helps to perform several
|
| -// steps before reporting break back to the debugger.
|
| -bool Debug::StepNextContinue(BreakLocation* break_location,
|
| - JavaScriptFrame* frame) {
|
| - // StepNext and StepOut shouldn't bring us deeper in code, so last frame
|
| - // shouldn't be a parent of current frame.
|
| - StepAction step_action = thread_local_.last_step_action_;
|
| -
|
| - if (step_action == StepNext || step_action == StepOut) {
|
| - if (frame->fp() < thread_local_.last_fp_) return true;
|
| - }
|
| -
|
| - // We stepped into a new frame if the frame pointer changed.
|
| - if (step_action == StepFrame) {
|
| - return frame->UnpaddedFP() == thread_local_.last_fp_;
|
| - }
|
| -
|
| - // If the step last action was step next or step in make sure that a new
|
| - // statement is hit.
|
| - if (step_action == StepNext || step_action == StepIn) {
|
| - // Never continue if returning from function.
|
| - if (break_location->IsReturn()) return false;
|
|
|
| - // Continue if we are still on the same frame and in the same statement.
|
| - int current_statement_position =
|
| - break_location->code()->SourceStatementPosition(frame->pc());
|
| - return thread_local_.last_fp_ == frame->UnpaddedFP() &&
|
| - thread_local_.last_statement_position_ == current_statement_position;
|
| + switch (step_action) {
|
| + case StepNone:
|
| + UNREACHABLE();
|
| + break;
|
| + case StepOut:
|
| + // Advance to caller frame.
|
| + frames_it.Advance();
|
| + // Skip native and extension functions on the stack.
|
| + while (!frames_it.done() &&
|
| + !frames_it.frame()->function()->shared()->IsSubjectToDebugging()) {
|
| + // Builtin functions are not subject to stepping, but need to be
|
| + // deoptimized to include checks for step-in at call sites.
|
| + Deoptimizer::DeoptimizeFunction(frames_it.frame()->function());
|
| + frames_it.Advance();
|
| + }
|
| + if (frames_it.done()) {
|
| + // Stepping out to the embedder. Disable step-in to avoid stepping into
|
| + // the next (unrelated) call that the embedder makes.
|
| + thread_local_.step_in_enabled_ = false;
|
| + } else {
|
| + // Fill the caller function to return to with one-shot break points.
|
| + Handle<JSFunction> caller_function(frames_it.frame()->function());
|
| + FloodWithOneShot(caller_function);
|
| + thread_local_.target_fp_ = frames_it.frame()->UnpaddedFP();
|
| + }
|
| + // Clear last position info. For stepping out it does not matter.
|
| + thread_local_.last_statement_position_ = RelocInfo::kNoPosition;
|
| + thread_local_.last_fp_ = 0;
|
| + break;
|
| + case StepNext:
|
| + thread_local_.target_fp_ = frame->UnpaddedFP();
|
| + FloodWithOneShot(function);
|
| + break;
|
| + case StepIn:
|
| + FloodWithOneShot(function);
|
| + break;
|
| + case StepFrame:
|
| + // No point in setting one-shot breaks at places where we are not about
|
| + // to leave the current frame.
|
| + FloodWithOneShot(function, CALLS_AND_RETURNS);
|
| + break;
|
| }
|
| -
|
| - // No step next action - don't continue.
|
| - return false;
|
| }
|
|
|
|
|
| @@ -996,13 +943,12 @@ Handle<Object> Debug::GetSourceBreakLocations(
|
| void Debug::ClearStepping() {
|
| // Clear the various stepping setup.
|
| ClearOneShot();
|
| - ClearStepOut();
|
|
|
| - thread_local_.step_count_ = 0;
|
| thread_local_.last_step_action_ = StepNone;
|
| thread_local_.step_in_enabled_ = false;
|
| thread_local_.last_statement_position_ = RelocInfo::kNoPosition;
|
| thread_local_.last_fp_ = 0;
|
| + thread_local_.target_fp_ = 0;
|
| }
|
|
|
|
|
| @@ -1023,20 +969,12 @@ void Debug::ClearOneShot() {
|
| }
|
|
|
|
|
| -void Debug::ActivateStepOut(StackFrame* frame) {
|
| - thread_local_.step_out_fp_ = frame->UnpaddedFP();
|
| -}
|
| -
|
| -
|
| void Debug::EnableStepIn() {
|
| STATIC_ASSERT(StepFrame > StepIn);
|
| thread_local_.step_in_enabled_ = (last_step_action() >= StepIn);
|
| }
|
|
|
|
|
| -void Debug::ClearStepOut() { thread_local_.step_out_fp_ = 0; }
|
| -
|
| -
|
| bool MatchingCodeTargets(Code* target1, Code* target2) {
|
| if (target1 == target2) return true;
|
| if (target1->kind() != target2->kind()) return false;
|
| @@ -1533,8 +1471,7 @@ void Debug::GetStepinPositions(JavaScriptFrame* frame, StackFrame::Id frame_id,
|
| // has stopped.
|
| Address call_pc = summary.pc() - 1;
|
| List<BreakLocation> locations;
|
| - BreakLocation::FromAddressSameStatement(debug_info, ALL_BREAK_LOCATIONS,
|
| - call_pc, &locations);
|
| + BreakLocation::FromAddressSameStatement(debug_info, call_pc, &locations);
|
|
|
| for (BreakLocation location : locations) {
|
| if (location.pc() <= summary.pc()) {
|
|
|