Index: runtime/vm/debugger.cc |
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc |
index d7f345d15aa6eaf275c45164419bae88ebff8b09..bf1d25cfcbfb1a6b98af169e4a3dfd38c75388bf 100644 |
--- a/runtime/vm/debugger.cc |
+++ b/runtime/vm/debugger.cc |
@@ -909,6 +909,21 @@ void ActivationFrame::ExtractTokenPositionFromAsyncClosure() { |
} |
+bool ActivationFrame::IsAsyncMachinery() const { |
+ Isolate* isolate = Isolate::Current(); |
+ if (function_.raw() == isolate->object_store()->complete_on_async_return()) { |
+ // We are completing an async function's completer. |
+ return true; |
+ } |
+ if (function_.Owner() == |
+ isolate->object_store()->async_star_stream_controller()) { |
+ // We are inside the async* stream controller code. |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+ |
// Get the saved current context of this activation. |
const Context& ActivationFrame::GetSavedCurrentContext() { |
if (!ctx_.IsNull()) return ctx_; |
@@ -1575,6 +1590,8 @@ Debugger::Debugger() |
async_causal_stack_trace_(NULL), |
awaiter_stack_trace_(NULL), |
stepping_fp_(0), |
+ async_stepping_fp_(0), |
+ top_frame_awaiter_(Object::null()), |
skip_next_step_(false), |
needs_breakpoint_cleanup_(false), |
synthetic_async_breakpoint_(NULL), |
@@ -3137,6 +3154,7 @@ void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
cbpt->VisitObjectPointers(visitor); |
cbpt = cbpt->next(); |
} |
+ visitor->VisitPointer(reinterpret_cast<RawObject**>(&top_frame_awaiter_)); |
} |
@@ -3200,15 +3218,60 @@ void Debugger::Pause(ServiceEvent* event) { |
void Debugger::EnterSingleStepMode() { |
- stepping_fp_ = 0; |
+ ResetSteppingFramePointers(); |
DeoptimizeWorld(); |
isolate_->set_single_step(true); |
} |
+void Debugger::ResetSteppingFramePointers() { |
+ stepping_fp_ = 0; |
+ async_stepping_fp_ = 0; |
+} |
+ |
+ |
+bool Debugger::SteppedForSyntheticAsyncBreakpoint() const { |
+ return synthetic_async_breakpoint_ != NULL; |
+} |
+ |
+ |
+void Debugger::CleanupSyntheticAsyncBreakpoint() { |
+ if (synthetic_async_breakpoint_ != NULL) { |
+ RemoveBreakpoint(synthetic_async_breakpoint_->id()); |
+ synthetic_async_breakpoint_ = NULL; |
+ } |
+} |
+ |
+ |
+void Debugger::RememberTopFrameAwaiter() { |
+ if (!FLAG_async_debugger_stepping) { |
+ return; |
+ } |
+ if (stack_trace_->Length() > 0) { |
+ top_frame_awaiter_ = stack_trace_->FrameAt(0)->GetAsyncAwaiter(); |
+ } else { |
+ top_frame_awaiter_ = Object::null(); |
+ } |
+} |
+ |
+ |
+void Debugger::SetAsyncSteppingFramePointer() { |
+ if (!FLAG_async_debugger_stepping) { |
+ return; |
+ } |
+ if (stack_trace_->FrameAt(0)->function().IsAsyncClosure() || |
+ stack_trace_->FrameAt(0)->function().IsAsyncGenClosure()) { |
+ async_stepping_fp_ = stack_trace_->FrameAt(0)->fp(); |
+ } else { |
+ async_stepping_fp_ = 0; |
+ } |
+} |
+ |
+ |
void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace, |
bool skip_next_step) { |
- stepping_fp_ = 0; |
+ ResetSteppingFramePointers(); |
+ RememberTopFrameAwaiter(); |
if (resume_action_ == kStepInto) { |
// When single stepping, we need to deoptimize because we might be |
// stepping into optimized code. This happens in particular if |
@@ -3218,6 +3281,7 @@ void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace, |
DeoptimizeWorld(); |
isolate_->set_single_step(true); |
skip_next_step_ = skip_next_step; |
+ SetAsyncSteppingFramePointer(); |
if (FLAG_verbose_debug) { |
OS::Print("HandleSteppingRequest- kStepInto\n"); |
} |
@@ -3227,6 +3291,7 @@ void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace, |
skip_next_step_ = skip_next_step; |
ASSERT(stack_trace->Length() > 0); |
stepping_fp_ = stack_trace->FrameAt(0)->fp(); |
+ SetAsyncSteppingFramePointer(); |
if (FLAG_verbose_debug) { |
OS::Print("HandleSteppingRequest- kStepOver %" Px "\n", stepping_fp_); |
} |
@@ -3533,7 +3598,7 @@ bool Debugger::IsDebuggable(const Function& func) { |
void Debugger::SignalPausedEvent(ActivationFrame* top_frame, Breakpoint* bpt) { |
resume_action_ = kContinue; |
- stepping_fp_ = 0; |
+ ResetSteppingFramePointers(); |
isolate_->set_single_step(false); |
ASSERT(!IsPaused()); |
ASSERT(obj_cache_ == NULL); |
@@ -3599,6 +3664,26 @@ RawError* Debugger::PauseStepping() { |
ActivationFrame* frame = TopDartFrame(); |
ASSERT(frame != NULL); |
+ if (FLAG_async_debugger_stepping) { |
+ if ((async_stepping_fp_ != 0) && (top_frame_awaiter_ != Object::null())) { |
+ // Check if the user has single stepped out of an async function with |
+ // an awaiter. The first check handles the case of calling into the |
+ // async machinery as we finish the async function. The second check |
+ // handles the case of returning from an async function. |
+ const bool exited_async_function = |
+ (IsCalleeFrameOf(async_stepping_fp_, frame->fp()) && |
+ frame->IsAsyncMachinery()) || |
+ IsCalleeFrameOf(frame->fp(), async_stepping_fp_); |
+ if (exited_async_function) { |
+ // Step to the top frame awaiter. |
+ const Object& async_op = Object::Handle(top_frame_awaiter_); |
+ top_frame_awaiter_ = Object::null(); |
+ AsyncStepInto(Closure::Cast(async_op)); |
+ return Error::null(); |
+ } |
+ } |
+ } |
+ |
if (stepping_fp_ != 0) { |
// There is an "interesting frame" set. Only pause at appropriate |
// locations in this frame. |
@@ -3610,7 +3695,7 @@ RawError* Debugger::PauseStepping() { |
// We returned from the "interesting frame", there can be no more |
// stepping breaks for it. Pause at the next appropriate location |
// and let the user set the "interesting" frame again. |
- stepping_fp_ = 0; |
+ ResetSteppingFramePointers(); |
} |
} |
@@ -3637,14 +3722,10 @@ RawError* Debugger::PauseStepping() { |
CacheStackTraces(CollectStackTrace(), CollectAsyncCausalStackTrace(), |
CollectAwaiterReturnStackTrace()); |
- // If this step callback is part of stepping over an await statement, |
- // we saved the synthetic async breakpoint in PauseBreakpoint. We report |
- // that we are paused at that breakpoint and then delete it after continuing. |
- SignalPausedEvent(frame, synthetic_async_breakpoint_); |
- if (synthetic_async_breakpoint_ != NULL) { |
- RemoveBreakpoint(synthetic_async_breakpoint_->id()); |
- synthetic_async_breakpoint_ = NULL; |
+ if (SteppedForSyntheticAsyncBreakpoint()) { |
+ CleanupSyntheticAsyncBreakpoint(); |
} |
+ SignalPausedEvent(frame, NULL); |
HandleSteppingRequest(stack_trace_); |
ClearCachedStackTraces(); |
@@ -4237,6 +4318,7 @@ void Debugger::AsyncStepInto(const Closure& async_op) { |
void Debugger::Continue() { |
SetResumeAction(kContinue); |
stepping_fp_ = 0; |
+ async_stepping_fp_ = 0; |
isolate_->set_single_step(false); |
} |