Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(888)

Unified Diff: runtime/vm/debugger.cc

Issue 2785243003: Implement support for single stepping out of an async function. (Closed)
Patch Set: fschneider review Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/debugger.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
}
« no previous file with comments | « runtime/vm/debugger.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698