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

Unified Diff: runtime/vm/debugger.cc

Issue 2646443005: Track async causal stack traces (Closed)
Patch Set: rebase Created 3 years, 10 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') | runtime/vm/exceptions.cc » ('j') | 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 4b145ad272440d11d2a50f55365e7996385eadfe..1c3121a27dc20c8a4f3fcf9e579f04685560da1c 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -26,6 +26,7 @@
#include "vm/service_isolate.h"
#include "vm/service.h"
#include "vm/stack_frame.h"
+#include "vm/stack_trace.h"
#include "vm/stub_code.h"
#include "vm/symbols.h"
#include "vm/thread_interrupter.h"
@@ -249,13 +250,15 @@ ActivationFrame::ActivationFrame(uword pc,
uword sp,
const Code& code,
const Array& deopt_frame,
- intptr_t deopt_frame_offset)
+ intptr_t deopt_frame_offset,
+ ActivationFrame::Kind kind)
: pc_(pc),
fp_(fp),
sp_(sp),
ctx_(Context::ZoneHandle()),
code_(Code::ZoneHandle(code.raw())),
function_(Function::ZoneHandle(code.function())),
+ live_frame_(kind == kRegular),
token_pos_initialized_(false),
token_pos_(TokenPosition::kNoSource),
try_index_(-1),
@@ -264,12 +267,35 @@ ActivationFrame::ActivationFrame(uword pc,
context_level_(-1),
deopt_frame_(Array::ZoneHandle(deopt_frame.raw())),
deopt_frame_offset_(deopt_frame_offset),
+ kind_(kind),
vars_initialized_(false),
var_descriptors_(LocalVarDescriptors::ZoneHandle()),
desc_indices_(8),
pc_desc_(PcDescriptors::ZoneHandle()) {}
+ActivationFrame::ActivationFrame(Kind kind)
+ : pc_(0),
+ fp_(0),
+ sp_(0),
+ ctx_(Context::ZoneHandle()),
+ code_(Code::ZoneHandle()),
+ function_(Function::ZoneHandle()),
+ live_frame_(kind == kRegular),
+ token_pos_initialized_(false),
+ token_pos_(TokenPosition::kNoSource),
+ try_index_(-1),
+ line_number_(-1),
+ column_number_(-1),
+ context_level_(-1),
+ deopt_frame_(Array::ZoneHandle()),
+ deopt_frame_offset_(0),
+ kind_(kind),
+ vars_initialized_(false),
+ var_descriptors_(LocalVarDescriptors::ZoneHandle()),
+ desc_indices_(8),
+ pc_desc_(PcDescriptors::ZoneHandle()) {}
+
bool Debugger::NeedsIsolateEvents() {
return ((isolate_ != Dart::vm_isolate()) &&
!ServiceIsolate::IsServiceIsolateDescendant(isolate_) &&
@@ -318,12 +344,11 @@ RawError* Debugger::PauseRequest(ServiceEvent::EventKind kind) {
if (trace->Length() > 0) {
event.set_top_frame(trace->FrameAt(0));
}
- ASSERT(stack_trace_ == NULL);
- stack_trace_ = trace;
+ CacheStackTraces(trace, CollectAsyncCausalStackTrace());
resume_action_ = kContinue;
Pause(&event);
HandleSteppingRequest(trace);
- stack_trace_ = NULL;
+ ClearCachedStackTraces();
// If any error occurred while in the debug message loop, return it here.
const Error& error = Error::Handle(Thread::Current()->sticky_error());
@@ -740,7 +765,7 @@ void ActivationFrame::GetDescIndices() {
GetVarDescriptors();
TokenPosition activation_token_pos = TokenPos();
- if (!activation_token_pos.IsDebugPause()) {
+ if (!activation_token_pos.IsDebugPause() || !live_frame_) {
// We don't have a token position for this frame, so can't determine
// which variables are visible.
vars_initialized_ = true;
@@ -1086,8 +1111,22 @@ const char* ActivationFrame::ToCString() {
void ActivationFrame::PrintToJSONObject(JSONObject* jsobj, bool full) {
+ if (kind_ == kRegular) {
+ PrintToJSONObjectRegular(jsobj, full);
+ } else if (kind_ == kAsyncCausal) {
+ PrintToJSONObjectAsyncCausal(jsobj, full);
+ } else if (kind_ == kAsyncSuspensionMarker) {
+ PrintToJSONObjectAsyncSuspensionMarker(jsobj, full);
+ } else {
+ UNIMPLEMENTED();
+ }
+}
+
+
+void ActivationFrame::PrintToJSONObjectRegular(JSONObject* jsobj, bool full) {
const Script& script = Script::Handle(SourceScript());
jsobj->AddProperty("type", "Frame");
+ jsobj->AddProperty("kind", KindToCString(kind_));
TokenPosition pos = TokenPos();
if (pos.IsSynthetic()) {
pos = pos.FromSynthetic();
@@ -1129,6 +1168,36 @@ void ActivationFrame::PrintToJSONObject(JSONObject* jsobj, bool full) {
}
}
+
+void ActivationFrame::PrintToJSONObjectAsyncCausal(JSONObject* jsobj,
+ bool full) {
+ jsobj->AddProperty("type", "Frame");
+ jsobj->AddProperty("kind", KindToCString(kind_));
+ const Script& script = Script::Handle(SourceScript());
+ TokenPosition pos = TokenPos();
+ if (pos.IsSynthetic()) {
+ pos = pos.FromSynthetic();
+ }
+ 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);
+ }
+}
+
+
+void ActivationFrame::PrintToJSONObjectAsyncSuspensionMarker(JSONObject* jsobj,
+ bool full) {
+ jsobj->AddProperty("type", "Frame");
+ jsobj->AddProperty("kind", KindToCString(kind_));
+ jsobj->AddProperty("marker", "AsynchronousSuspension");
+}
+
+
static bool IsFunctionVisible(const Function& function) {
return FLAG_show_invisible_frames || function.is_visible();
}
@@ -1141,6 +1210,19 @@ void DebuggerStackTrace::AddActivation(ActivationFrame* frame) {
}
+void DebuggerStackTrace::AddMarker(ActivationFrame::Kind marker) {
+ ASSERT((marker >= ActivationFrame::kAsyncSuspensionMarker) &&
+ (marker <= ActivationFrame::kAsyncSuspensionMarker));
+ trace_.Add(new ActivationFrame(marker));
+}
+
+
+void DebuggerStackTrace::AddAsyncCausalFrame(uword pc, const Code& code) {
+ trace_.Add(new ActivationFrame(pc, 0, 0, code, Array::Handle(), 0,
+ ActivationFrame::kAsyncCausal));
+}
+
+
const uint8_t kSafepointKind = RawPcDescriptors::kIcCall |
RawPcDescriptors::kUnoptStaticCall |
RawPcDescriptors::kRuntimeCall;
@@ -1268,6 +1350,7 @@ Debugger::Debugger()
pause_event_(NULL),
obj_cache_(NULL),
stack_trace_(NULL),
+ async_causal_stack_trace_(NULL),
stepping_fp_(0),
skip_next_step_(false),
synthetic_async_breakpoint_(NULL),
@@ -1281,6 +1364,7 @@ Debugger::~Debugger() {
ASSERT(breakpoint_locations_ == NULL);
ASSERT(code_breakpoints_ == NULL);
ASSERT(stack_trace_ == NULL);
+ ASSERT(async_causal_stack_trace_ == NULL);
ASSERT(obj_cache_ == NULL);
ASSERT(synthetic_async_breakpoint_ == NULL);
}
@@ -1519,33 +1603,125 @@ DebuggerStackTrace* Debugger::CollectStackTrace() {
}
if (frame->IsDartFrame()) {
code = frame->LookupDartCode();
- if (code.is_optimized() && !FLAG_precompiled_runtime) {
- deopt_frame = DeoptimizeToArray(thread, frame, code);
- for (InlinedFunctionsIterator it(code, frame->pc()); !it.Done();
- it.Advance()) {
- inlined_code = it.code();
- if (FLAG_trace_debugger_stacktrace) {
- const Function& function =
- Function::Handle(zone, inlined_code.function());
- ASSERT(!function.IsNull());
- OS::PrintErr("CollectStackTrace: visiting inlined function: %s\n",
- function.ToFullyQualifiedCString());
+ AppendCodeFrames(thread, isolate, zone, stack_trace, frame, &code,
+ &inlined_code, &deopt_frame);
+ }
+ }
+ return stack_trace;
+}
+
+void Debugger::AppendCodeFrames(Thread* thread,
+ Isolate* isolate,
+ Zone* zone,
+ DebuggerStackTrace* stack_trace,
+ StackFrame* frame,
+ Code* code,
+ Code* inlined_code,
+ Array* deopt_frame) {
+ if (code->is_optimized() && !FLAG_precompiled_runtime) {
+ // TODO(rmacnak): Use CodeSourceMap
+ *deopt_frame = DeoptimizeToArray(thread, frame, *code);
+ for (InlinedFunctionsIterator it(*code, frame->pc()); !it.Done();
+ it.Advance()) {
+ *inlined_code = it.code();
+ if (FLAG_trace_debugger_stacktrace) {
+ const Function& function =
+ Function::Handle(zone, inlined_code->function());
+ ASSERT(!function.IsNull());
+ OS::PrintErr("CollectStackTrace: visiting inlined function: %s\n",
+ function.ToFullyQualifiedCString());
+ }
+ intptr_t deopt_frame_offset = it.GetDeoptFpOffset();
+ stack_trace->AddActivation(CollectDartFrame(isolate, it.pc(), frame,
+ *inlined_code, *deopt_frame,
+ deopt_frame_offset));
+ }
+ } else {
+ stack_trace->AddActivation(CollectDartFrame(
+ isolate, frame->pc(), frame, *code, Object::null_array(), 0));
+ }
+}
+
+
+DebuggerStackTrace* Debugger::CollectAsyncCausalStackTrace() {
+ if (!FLAG_causal_async_stacks) {
+ return NULL;
+ }
+ Thread* thread = Thread::Current();
+ Zone* zone = thread->zone();
+ Isolate* isolate = thread->isolate();
+ DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8);
+
+ Code& code = Code::Handle(zone);
+ Smi& offset = Smi::Handle();
+ Code& inlined_code = Code::Handle(zone);
+ Array& deopt_frame = Array::Handle(zone);
+
+ Function& async_function = Function::Handle(zone);
+ class StackTrace& async_stack_trace = StackTrace::Handle(zone);
+ Array& async_code_array = Array::Handle(zone);
+ Array& async_pc_offset_array = Array::Handle(zone);
+ StackTraceUtils::ExtractAsyncStackTraceInfo(
+ thread, &async_function, &async_stack_trace, &async_code_array,
+ &async_pc_offset_array);
+
+ if (async_function.IsNull()) {
+ return NULL;
+ }
+
+ intptr_t synchronous_stack_trace_length =
+ StackTraceUtils::CountFrames(thread, 0, async_function);
+
+ // Append the top frames from the synchronous stack trace, up until the active
+ // asynchronous function. We truncate the remainder of the synchronous
+ // stack trace because it contains activations that are part of the
+ // asynchronous dispatch mechanisms.
+ StackFrameIterator iterator(false);
+ StackFrame* frame = iterator.NextFrame();
+ while (synchronous_stack_trace_length >= 0) {
+ ASSERT(frame != NULL);
+ if (frame->IsDartFrame()) {
+ code = frame->LookupDartCode();
+ AppendCodeFrames(thread, isolate, zone, stack_trace, frame, &code,
+ &inlined_code, &deopt_frame);
+ synchronous_stack_trace_length--;
+ }
+ frame = iterator.NextFrame();
+ }
+
+ // 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;
+ }
+ if (async_stack_trace.CodeAtFrame(i) ==
+ StubCode::AsynchronousGapMarker_entry()->code()) {
+ stack_trace->AddMarker(ActivationFrame::kAsyncSuspensionMarker);
+ } 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(pc, inlined_code);
}
- intptr_t deopt_frame_offset = it.GetDeoptFpOffset();
- stack_trace->AddActivation(CollectDartFrame(isolate, it.pc(), frame,
- inlined_code, deopt_frame,
- deopt_frame_offset));
+ } else {
+ stack_trace->AddAsyncCausalFrame(pc, code);
}
- } else {
- stack_trace->AddActivation(CollectDartFrame(
- isolate, frame->pc(), frame, code, Object::null_array(), 0));
}
}
+ // Follow the link.
+ async_stack_trace = async_stack_trace.async_link();
}
+
return stack_trace;
}
-
ActivationFrame* Debugger::TopDartFrame() const {
StackFrameIterator iterator(false);
StackFrame* frame = iterator.NextFrame();
@@ -1563,10 +1739,23 @@ DebuggerStackTrace* Debugger::StackTrace() {
return (stack_trace_ != NULL) ? stack_trace_ : CollectStackTrace();
}
+
DebuggerStackTrace* Debugger::CurrentStackTrace() {
return CollectStackTrace();
}
+
+DebuggerStackTrace* Debugger::AsyncCausalStackTrace() {
+ return (async_causal_stack_trace_ != NULL) ? async_causal_stack_trace_
+ : CollectAsyncCausalStackTrace();
+}
+
+
+DebuggerStackTrace* Debugger::CurrentAsyncCausalStackTrace() {
+ return CollectAsyncCausalStackTrace();
+}
+
+
DebuggerStackTrace* Debugger::StackTraceFrom(const class StackTrace& ex_trace) {
DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8);
Function& function = Function::Handle();
@@ -1667,11 +1856,10 @@ void Debugger::PauseException(const Instance& exc) {
if (stack_trace->Length() > 0) {
event.set_top_frame(stack_trace->FrameAt(0));
}
- ASSERT(stack_trace_ == NULL);
- stack_trace_ = stack_trace;
+ CacheStackTraces(stack_trace, CollectAsyncCausalStackTrace());
Pause(&event);
HandleSteppingRequest(stack_trace_); // we may get a rewind request
- stack_trace_ = NULL;
+ ClearCachedStackTraces();
}
@@ -2668,6 +2856,21 @@ void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace,
}
+void Debugger::CacheStackTraces(DebuggerStackTrace* stack_trace,
+ DebuggerStackTrace* async_causal_stack_trace) {
+ ASSERT(stack_trace_ == NULL);
+ stack_trace_ = stack_trace;
+ ASSERT(async_causal_stack_trace_ == NULL);
+ async_causal_stack_trace_ = async_causal_stack_trace;
+}
+
+
+void Debugger::ClearCachedStackTraces() {
+ stack_trace_ = NULL;
+ async_causal_stack_trace_ = NULL;
+}
+
+
static intptr_t FindNextRewindFrameIndex(DebuggerStackTrace* stack,
intptr_t frame_index) {
for (intptr_t i = frame_index + 1; i < stack->Length(); i++) {
@@ -2796,7 +2999,7 @@ void Debugger::RewindToFrame(intptr_t frame_index) {
void Debugger::RewindToUnoptimizedFrame(StackFrame* frame, const Code& code) {
// We will be jumping out of the debugger rather than exiting this
// function, so prepare the debugger state.
- stack_trace_ = NULL;
+ ClearCachedStackTraces();
resume_action_ = kContinue;
resume_frame_index_ = -1;
EnterSingleStepMode();
@@ -2828,7 +3031,7 @@ void Debugger::RewindToOptimizedFrame(StackFrame* frame,
// We will be jumping out of the debugger rather than exiting this
// function, so prepare the debugger state.
- stack_trace_ = NULL;
+ ClearCachedStackTraces();
resume_action_ = kContinue;
resume_frame_index_ = -1;
EnterSingleStepMode();
@@ -3004,8 +3207,8 @@ RawError* Debugger::PauseStepping() {
frame->TokenPos().ToCString());
}
- ASSERT(stack_trace_ == NULL);
- stack_trace_ = CollectStackTrace();
+
+ CacheStackTraces(CollectStackTrace(), CollectAsyncCausalStackTrace());
// 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.
@@ -3015,7 +3218,7 @@ RawError* Debugger::PauseStepping() {
synthetic_async_breakpoint_ = NULL;
}
HandleSteppingRequest(stack_trace_);
- stack_trace_ = NULL;
+ ClearCachedStackTraces();
// If any error occurred while in the debug message loop, return it here.
const Error& error = Error::Handle(Thread::Current()->sticky_error());
@@ -3038,50 +3241,7 @@ RawError* Debugger::PauseBreakpoint() {
CodeBreakpoint* cbpt = GetCodeBreakpoint(top_frame->pc());
ASSERT(cbpt != NULL);
- BreakpointLocation* bpt_location = cbpt->bpt_location_;
- Breakpoint* bpt_hit = NULL;
-
- // There may be more than one applicable breakpoint at this location, but we
- // will report only one as reached. If there is a single-shot breakpoint, we
- // favor it; then a closure-specific breakpoint ; then an general breakpoint.
- if (bpt_location != NULL) {
- Breakpoint* bpt = bpt_location->breakpoints();
- while (bpt != NULL) {
- if (bpt->IsSingleShot()) {
- bpt_hit = bpt;
- break;
- }
- bpt = bpt->next();
- }
-
- if (bpt_hit == NULL) {
- bpt = bpt_location->breakpoints();
- while (bpt != NULL) {
- if (bpt->IsPerClosure()) {
- Object& closure = Object::Handle(top_frame->GetClosure());
- ASSERT(closure.IsInstance());
- ASSERT(Instance::Cast(closure).IsClosure());
- if (closure.raw() == bpt->closure()) {
- bpt_hit = bpt;
- break;
- }
- }
- bpt = bpt->next();
- }
- }
-
- if (bpt_hit == NULL) {
- bpt = bpt_location->breakpoints();
- while (bpt != NULL) {
- if (bpt->IsRepeated()) {
- bpt_hit = bpt;
- break;
- }
- bpt = bpt->next();
- }
- }
- }
-
+ Breakpoint* bpt_hit = FindHitBreakpoint(cbpt->bpt_location_, top_frame);
if (bpt_hit == NULL) {
return Error::null();
}
@@ -3089,8 +3249,7 @@ RawError* Debugger::PauseBreakpoint() {
if (bpt_hit->is_synthetic_async()) {
DebuggerStackTrace* stack_trace = CollectStackTrace();
ASSERT(stack_trace->Length() > 0);
- ASSERT(stack_trace_ == NULL);
- stack_trace_ = stack_trace;
+ CacheStackTraces(stack_trace, CollectAsyncCausalStackTrace());
// Hit a synthetic async breakpoint.
if (FLAG_verbose_debug) {
@@ -3112,7 +3271,7 @@ RawError* Debugger::PauseBreakpoint() {
// When we single step from a user breakpoint, our next stepping
// point will be at the exact same pc. Skip it.
HandleSteppingRequest(stack_trace_, true /* skip next step */);
- stack_trace_ = NULL;
+ ClearCachedStackTraces();
return Error::null();
}
@@ -3125,13 +3284,12 @@ RawError* Debugger::PauseBreakpoint() {
cbpt->token_pos().ToCString(), top_frame->pc());
}
- ASSERT(stack_trace_ == NULL);
- stack_trace_ = stack_trace;
+ CacheStackTraces(stack_trace, CollectAsyncCausalStackTrace());
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.
HandleSteppingRequest(stack_trace_, true /* skip next step */);
- stack_trace_ = NULL;
+ ClearCachedStackTraces();
if (cbpt->IsInternal()) {
RemoveInternalBreakpoints();
}
@@ -3143,6 +3301,51 @@ RawError* Debugger::PauseBreakpoint() {
}
+Breakpoint* Debugger::FindHitBreakpoint(BreakpointLocation* location,
+ ActivationFrame* top_frame) {
+ if (location == NULL) {
+ return NULL;
+ }
+ // There may be more than one applicable breakpoint at this location, but we
+ // will report only one as reached. If there is a single-shot breakpoint, we
+ // favor it; then a closure-specific breakpoint ; then an general breakpoint.
+
+ // First check for a single-shot breakpoint.
+ Breakpoint* bpt = location->breakpoints();
+ while (bpt != NULL) {
+ if (bpt->IsSingleShot()) {
+ return bpt;
+ }
+ bpt = bpt->next();
+ }
+
+ // Now check for a closure-specific breakpoint.
+ bpt = location->breakpoints();
+ while (bpt != NULL) {
+ if (bpt->IsPerClosure()) {
+ Object& closure = Object::Handle(top_frame->GetClosure());
+ ASSERT(closure.IsInstance());
+ ASSERT(Instance::Cast(closure).IsClosure());
+ if (closure.raw() == bpt->closure()) {
+ return bpt;
+ }
+ }
+ bpt = bpt->next();
+ }
+
+ // Finally, check for a general breakpoint.
+ bpt = location->breakpoints();
+ while (bpt != NULL) {
+ if (bpt->IsRepeated()) {
+ return bpt;
+ }
+ bpt = bpt->next();
+ }
+
+ return NULL;
+}
+
+
void Debugger::PauseDeveloper(const String& msg) {
// We ignore this breakpoint when the VM is executing code invoked
// by the debugger to evaluate variables values, or when we see a nested
@@ -3153,9 +3356,7 @@ void Debugger::PauseDeveloper(const String& msg) {
DebuggerStackTrace* stack_trace = CollectStackTrace();
ASSERT(stack_trace->Length() > 0);
- ASSERT(stack_trace_ == NULL);
- stack_trace_ = stack_trace;
-
+ CacheStackTraces(stack_trace, CollectAsyncCausalStackTrace());
// TODO(johnmccutchan): Send |msg| to Observatory.
// We are in the native call to Developer_debugger. the developer
@@ -3163,8 +3364,7 @@ void Debugger::PauseDeveloper(const String& msg) {
// this, we continue execution until the call exits (step out).
SetResumeAction(kStepOut);
HandleSteppingRequest(stack_trace_);
-
- stack_trace_ = NULL;
+ ClearCachedStackTraces();
}
« no previous file with comments | « runtime/vm/debugger.h ('k') | runtime/vm/exceptions.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698