Index: runtime/vm/debugger.cc |
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc |
index 7d3eb9fdaa28ab17486a1fd127490c7f52c4712c..1d947499d4e9d1baeed0a7e5ac20af1b136b1461 100644 |
--- a/runtime/vm/debugger.cc |
+++ b/runtime/vm/debugger.cc |
@@ -377,7 +377,8 @@ RawError* Debugger::PauseRequest(ServiceEvent::EventKind kind) { |
if (trace->Length() > 0) { |
event.set_top_frame(trace->FrameAt(0)); |
} |
- CacheStackTraces(trace, CollectAsyncCausalStackTrace()); |
+ CacheStackTraces(trace, CollectAsyncCausalStackTrace(), |
+ CollectAwaiterReturnStackTrace()); |
resume_action_ = kContinue; |
Pause(&event); |
HandleSteppingRequest(trace); |
@@ -713,7 +714,7 @@ intptr_t ActivationFrame::ContextLevel() { |
RawObject* ActivationFrame::GetAsyncContextVariable(const String& name) { |
- if (!function_.IsAsyncClosure()) { |
+ if (!function_.IsAsyncClosure() && !function_.IsAsyncGenClosure()) { |
return Object::null(); |
} |
GetVarDescriptors(); |
@@ -799,6 +800,11 @@ RawObject* ActivationFrame::GetAsyncAwaiter() { |
} |
+RawObject* ActivationFrame::GetCausalStack() { |
+ return GetAsyncContextVariable(Symbols::AsyncStackTraceVar()); |
+} |
+ |
+ |
bool ActivationFrame::HandlesException(const Instance& exc_obj) { |
intptr_t try_index = TryIndex(); |
if (try_index < 0) { |
@@ -1309,6 +1315,8 @@ void ActivationFrame::PrintToJSONObject(JSONObject* jsobj, bool full) { |
PrintToJSONObjectAsyncCausal(jsobj, full); |
} else if (kind_ == kAsyncSuspensionMarker) { |
PrintToJSONObjectAsyncSuspensionMarker(jsobj, full); |
+ } else if (kind_ == kAsyncActivation) { |
+ PrintToJSONObjectAsyncActivation(jsobj, full); |
} else { |
UNIMPLEMENTED(); |
} |
@@ -1343,7 +1351,8 @@ void ActivationFrame::PrintToJSONObjectRegular(JSONObject* jsobj, bool full) { |
if ((var_name.raw() != Symbols::AsyncOperation().raw()) && |
(var_name.raw() != Symbols::AsyncCompleter().raw()) && |
(var_name.raw() != Symbols::ControllerStream().raw()) && |
- (var_name.raw() != Symbols::AwaitJumpVar().raw())) { |
+ (var_name.raw() != Symbols::AwaitJumpVar().raw()) && |
+ (var_name.raw() != Symbols::AsyncStackTraceVar().raw())) { |
JSONObject jsvar(&jsvars); |
jsvar.AddProperty("type", "BoundVariable"); |
var_name = String::ScrubName(var_name); |
@@ -1387,6 +1396,24 @@ void ActivationFrame::PrintToJSONObjectAsyncSuspensionMarker(JSONObject* jsobj, |
} |
+void ActivationFrame::PrintToJSONObjectAsyncActivation(JSONObject* jsobj, |
+ bool full) { |
+ jsobj->AddProperty("type", "Frame"); |
+ jsobj->AddProperty("kind", KindToCString(kind_)); |
+ const Script& script = Script::Handle(SourceScript()); |
+ const TokenPosition pos = TokenPos().SourcePosition(); |
+ jsobj->AddLocation(script, pos); |
+ jsobj->AddProperty("function", function(), !full); |
+ jsobj->AddProperty("code", code()); |
+ if (full) { |
+ // TODO(cutch): The old "full" script usage no longer fits |
+ // in the world where we pass the script as part of the |
+ // location. |
+ jsobj->AddProperty("script", script, !full); |
+ } |
+} |
+ |
+ |
static bool IsFunctionVisible(const Function& function) { |
return FLAG_show_invisible_frames || function.is_visible(); |
} |
@@ -1540,6 +1567,7 @@ Debugger::Debugger() |
obj_cache_(NULL), |
stack_trace_(NULL), |
async_causal_stack_trace_(NULL), |
+ awaiter_stack_trace_(NULL), |
stepping_fp_(0), |
skip_next_step_(false), |
needs_breakpoint_cleanup_(false), |
@@ -1937,10 +1965,13 @@ DebuggerStackTrace* Debugger::CollectAwaiterReturnStackTrace() { |
StackFrameIterator iterator(StackFrameIterator::kDontValidateFrames); |
Code& code = Code::Handle(zone); |
+ Smi& offset = Smi::Handle(zone); |
Function& function = Function::Handle(zone); |
Code& inlined_code = Code::Handle(zone); |
Closure& async_activation = Closure::Handle(zone); |
+ Object& next_async_activation = Object::Handle(zone); |
Array& deopt_frame = Array::Handle(zone); |
+ class StackTrace& async_stack_trace = StackTrace::Handle(zone); |
for (StackFrame* frame = iterator.NextFrame(); frame != NULL; |
frame = iterator.NextFrame()) { |
@@ -2011,10 +2042,51 @@ DebuggerStackTrace* Debugger::CollectAwaiterReturnStackTrace() { |
// Append the awaiter return call stack. |
while (!async_activation.IsNull()) { |
- ActivationFrame* activation = new ActivationFrame(async_activation); |
- async_activation ^= activation->GetAsyncAwaiter(); |
+ ActivationFrame* activation = new (zone) ActivationFrame(async_activation); |
activation->ExtractTokenPositionFromAsyncClosure(); |
stack_trace->AddActivation(activation); |
+ next_async_activation = activation->GetAsyncAwaiter(); |
+ if (next_async_activation.IsNull()) { |
+ // No more awaiters. Extract the causal stack trace (if it exists). |
+ async_stack_trace ^= activation->GetCausalStack(); |
+ break; |
+ } |
+ async_activation = Closure::RawCast(next_async_activation.raw()); |
+ } |
+ |
+ // Now we append the asynchronous causal stack trace. These are not active |
+ // frames but a historical record of how this asynchronous function was |
+ // activated. |
+ while (!async_stack_trace.IsNull()) { |
+ for (intptr_t i = 0; i < async_stack_trace.Length(); i++) { |
+ if (async_stack_trace.CodeAtFrame(i) == Code::null()) { |
+ // Incomplete OutOfMemory/StackOverflow trace OR array padding. |
+ break; |
+ } |
+ if (async_stack_trace.CodeAtFrame(i) == |
+ StubCode::AsynchronousGapMarker_entry()->code()) { |
+ stack_trace->AddMarker(ActivationFrame::kAsyncSuspensionMarker); |
+ // The frame immediately below the asynchronous gap marker is the |
+ // identical to the frame above the marker. Skip the frame to enhance |
+ // the readability of the trace. |
+ i++; |
+ } else { |
+ code = Code::RawCast(async_stack_trace.CodeAtFrame(i)); |
+ offset = Smi::RawCast(async_stack_trace.PcOffsetAtFrame(i)); |
+ uword pc = code.PayloadStart() + offset.Value(); |
+ if (code.is_optimized()) { |
+ for (InlinedFunctionsIterator it(code, pc); !it.Done(); |
+ it.Advance()) { |
+ inlined_code = it.code(); |
+ stack_trace->AddAsyncCausalFrame(it.pc(), inlined_code); |
+ } |
+ } else { |
+ stack_trace->AddAsyncCausalFrame(pc, code); |
+ } |
+ } |
+ } |
+ // Follow the link. |
+ async_stack_trace = async_stack_trace.async_link(); |
} |
return stack_trace; |
@@ -2055,6 +2127,17 @@ DebuggerStackTrace* Debugger::CurrentAsyncCausalStackTrace() { |
} |
+DebuggerStackTrace* Debugger::AwaiterStackTrace() { |
+ return (awaiter_stack_trace_ != NULL) ? awaiter_stack_trace_ |
+ : CollectAwaiterReturnStackTrace(); |
+} |
+ |
+ |
+DebuggerStackTrace* Debugger::CurrentAwaiterStackTrace() { |
+ return CollectAwaiterReturnStackTrace(); |
+} |
+ |
+ |
DebuggerStackTrace* Debugger::StackTraceFrom(const class StackTrace& ex_trace) { |
DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8); |
Function& function = Function::Handle(); |
@@ -2171,7 +2254,8 @@ void Debugger::PauseException(const Instance& exc) { |
if (stack_trace->Length() > 0) { |
event.set_top_frame(stack_trace->FrameAt(0)); |
} |
- CacheStackTraces(stack_trace, CollectAsyncCausalStackTrace()); |
+ CacheStackTraces(stack_trace, CollectAsyncCausalStackTrace(), |
+ CollectAwaiterReturnStackTrace()); |
Pause(&event); |
HandleSteppingRequest(stack_trace_); // we may get a rewind request |
ClearCachedStackTraces(); |
@@ -3175,17 +3259,21 @@ void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace, |
void Debugger::CacheStackTraces(DebuggerStackTrace* stack_trace, |
- DebuggerStackTrace* async_causal_stack_trace) { |
+ DebuggerStackTrace* async_causal_stack_trace, |
+ DebuggerStackTrace* awaiter_stack_trace) { |
ASSERT(stack_trace_ == NULL); |
stack_trace_ = stack_trace; |
ASSERT(async_causal_stack_trace_ == NULL); |
async_causal_stack_trace_ = async_causal_stack_trace; |
+ ASSERT(awaiter_stack_trace_ == NULL); |
+ awaiter_stack_trace_ = awaiter_stack_trace; |
} |
void Debugger::ClearCachedStackTraces() { |
stack_trace_ = NULL; |
async_causal_stack_trace_ = NULL; |
+ awaiter_stack_trace_ = NULL; |
} |
@@ -3526,7 +3614,8 @@ RawError* Debugger::PauseStepping() { |
} |
- CacheStackTraces(CollectStackTrace(), CollectAsyncCausalStackTrace()); |
+ 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. |
@@ -3567,7 +3656,8 @@ RawError* Debugger::PauseBreakpoint() { |
if (bpt_hit->is_synthetic_async()) { |
DebuggerStackTrace* stack_trace = CollectStackTrace(); |
ASSERT(stack_trace->Length() > 0); |
- CacheStackTraces(stack_trace, CollectAsyncCausalStackTrace()); |
+ CacheStackTraces(stack_trace, CollectAsyncCausalStackTrace(), |
+ CollectAwaiterReturnStackTrace()); |
// Hit a synthetic async breakpoint. |
if (FLAG_verbose_debug) { |
@@ -3602,7 +3692,8 @@ RawError* Debugger::PauseBreakpoint() { |
top_frame->pc()); |
} |
- CacheStackTraces(stack_trace, CollectAsyncCausalStackTrace()); |
+ CacheStackTraces(stack_trace, CollectAsyncCausalStackTrace(), |
+ CollectAwaiterReturnStackTrace()); |
SignalPausedEvent(top_frame, bpt_hit); |
// When we single step from a user breakpoint, our next stepping |
// point will be at the exact same pc. Skip it. |
@@ -3671,7 +3762,8 @@ void Debugger::PauseDeveloper(const String& msg) { |
DebuggerStackTrace* stack_trace = CollectStackTrace(); |
ASSERT(stack_trace->Length() > 0); |
- CacheStackTraces(stack_trace, CollectAsyncCausalStackTrace()); |
+ CacheStackTraces(stack_trace, CollectAsyncCausalStackTrace(), |
+ CollectAwaiterReturnStackTrace()); |
// TODO(johnmccutchan): Send |msg| to Observatory. |
// We are in the native call to Developer_debugger. the developer |