Chromium Code Reviews| Index: runtime/vm/debugger.cc |
| diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc |
| index 7d3eb9fdaa28ab17486a1fd127490c7f52c4712c..ce1756699e669b95b84dfb4daebc90e783764cbc 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()) { |
| @@ -2012,9 +2043,49 @@ DebuggerStackTrace* Debugger::CollectAwaiterReturnStackTrace() { |
| // Append the awaiter return call stack. |
| while (!async_activation.IsNull()) { |
| ActivationFrame* activation = new ActivationFrame(async_activation); |
|
rmacnak
2017/03/29 01:04:26
new(zone)
Cutch
2017/03/29 13:57:43
Done.
|
| - async_activation ^= activation->GetAsyncAwaiter(); |
| 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()) { |
| + break; |
|
rmacnak
2017/03/29 01:04:26
// Incomplete OutOfMemory/StackOverflow trace.
Cutch
2017/03/29 13:57:43
This can also be alignment related padding at the
|
| + } |
| + 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 +2126,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 +2253,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 +3258,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 +3613,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 +3655,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 +3691,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 +3761,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 |