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

Unified Diff: runtime/vm/debugger.cc

Issue 2603383004: Sane asynchronous debugging and stack traces (Closed)
Patch Set: rebase Created 3 years, 11 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/flag_list.h » ('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 53bcba2632e17b1e2eb42aee4b611cb66b6ad185..8061270f5876c059c1550e3c0ce9f1369bcc698d 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"
@@ -244,32 +245,6 @@ void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
}
-ActivationFrame::ActivationFrame(uword pc,
- uword fp,
- uword sp,
- const Code& code,
- const Array& deopt_frame,
- intptr_t deopt_frame_offset)
- : pc_(pc),
- fp_(fp),
- sp_(sp),
- ctx_(Context::ZoneHandle()),
- code_(Code::ZoneHandle(code.raw())),
- function_(Function::ZoneHandle(code.function())),
- 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.raw())),
- deopt_frame_offset_(deopt_frame_offset),
- 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 +293,12 @@ 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, CollectAsyncStackTrace(),
+ CollectAsyncReturnCallStack());
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());
@@ -503,6 +478,86 @@ void Debugger::PrintSettingsToJSONObject(JSONObject* jsobj) const {
}
+ActivationFrame::ActivationFrame(uword pc,
+ uword fp,
+ uword sp,
+ const Code& code,
+ const Array& deopt_frame,
+ 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())),
+ suspended_frame_(false),
+ 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.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()),
+ suspended_frame_(true),
+ 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()) {}
+
+
+ActivationFrame::ActivationFrame(const Closure& async_activation)
+ : pc_(0),
+ fp_(0),
+ sp_(0),
+ ctx_(Context::ZoneHandle()),
+ code_(Code::ZoneHandle()),
+ function_(Function::ZoneHandle()),
+ suspended_frame_(true),
+ 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_(kAsyncLive),
+ vars_initialized_(false),
+ var_descriptors_(LocalVarDescriptors::ZoneHandle()),
+ desc_indices_(8),
+ pc_desc_(PcDescriptors::ZoneHandle()) {
+ // Extract the function and the code from the asynchronous activation.
+ function_ = async_activation.function();
+ code_ = function_.unoptimized_code();
+ ctx_ = async_activation.context();
+}
+
+
RawString* ActivationFrame::QualifiedFunctionName() {
return String::New(Debugger::QualifiedFunctionName(function()));
}
@@ -622,13 +677,13 @@ intptr_t ActivationFrame::ContextLevel() {
// for the code position of the frame? For now say we are at context
// level 0.
TokenPos();
- if (token_pos_ == TokenPosition::kNoSource) {
+ if (token_pos_.IsClassifying() || token_pos_.IsNoSource()) {
// No PcDescriptor.
return context_level_;
}
ASSERT(!pc_desc_.IsNull());
TokenPosition innermost_begin_pos = TokenPosition::kMinSource;
- TokenPosition activation_token_pos = TokenPos();
+ TokenPosition activation_token_pos = TokenPos().FromSynthetic();
ASSERT(activation_token_pos.IsReal());
GetVarDescriptors();
intptr_t var_desc_len = var_descriptors_.Length();
@@ -676,6 +731,189 @@ const Context& ActivationFrame::GetSavedCurrentContext() {
}
+RawObject* ActivationFrame::GetAsyncCompleter() {
+ if (!function_.IsAsyncClosure()) {
+ return Object::null();
+ }
+ GetVarDescriptors();
+ intptr_t var_desc_len = var_descriptors_.Length();
+ if (fp() == 0) {
+ // Not actually on the stack. Pull it out of the closure's context.
+ intptr_t var_desc_len = var_descriptors_.Length();
+ for (intptr_t i = 0; i < var_desc_len; i++) {
+ RawLocalVarDescriptors::VarInfo var_info;
+ var_descriptors_.GetInfo(i, &var_info);
+ const int8_t kind = var_info.kind();
+ if (var_descriptors_.GetName(i) == Symbols::AsyncCompleter().raw()) {
+ ASSERT(kind == RawLocalVarDescriptors::kContextVar);
+ ASSERT(!ctx_.IsNull());
+ return ctx_.At(var_info.index());
+ }
+ }
+ } else {
+ // On the stack.
+ for (intptr_t i = 0; i < var_desc_len; i++) {
+ RawLocalVarDescriptors::VarInfo var_info;
+ var_descriptors_.GetInfo(i, &var_info);
+ if (var_descriptors_.GetName(i) == Symbols::AsyncCompleter().raw()) {
+ const int8_t kind = var_info.kind();
+ if (kind == RawLocalVarDescriptors::kStackVar) {
+ return GetStackVar(var_info.index());
+ } else {
+ ASSERT(kind == RawLocalVarDescriptors::kContextVar);
+ return GetContextVar(var_info.scope_id, var_info.index());
+ }
+ }
+ }
+ }
+ return Object::null();
+}
+
+
+RawObject* ActivationFrame::GetAsyncCompleterAwaiter(const Object& completer) {
+ const Class& sync_completer_cls = Class::Handle(completer.clazz());
+ ASSERT(!sync_completer_cls.IsNull());
+ const Class& completer_cls = Class::Handle(sync_completer_cls.SuperClass());
+ const Field& future_field =
+ Field::Handle(completer_cls.LookupInstanceFieldAllowPrivate(
+ Symbols::CompleterFuture()));
+ ASSERT(!future_field.IsNull());
+ Instance& future = Instance::Handle();
+ future ^= Instance::Cast(completer).GetField(future_field);
+ ASSERT(!future.IsNull());
+ const Class& future_cls = Class::Handle(future.clazz());
+ ASSERT(!future_cls.IsNull());
+ const Field& awaiter_field = Field::Handle(
+ future_cls.LookupInstanceFieldAllowPrivate(Symbols::_Awaiter()));
+ ASSERT(!awaiter_field.IsNull());
+ return future.GetField(awaiter_field);
+}
+
+
+RawObject* ActivationFrame::GetAsyncStreamControllerStream() {
+ if (!function_.IsAsyncGenClosure()) {
+ return Object::null();
+ }
+ GetVarDescriptors();
+ intptr_t var_desc_len = var_descriptors_.Length();
+ if (fp() == 0) {
+ // Not actually on the stack. Pull it out of the closure's context.
+ intptr_t var_desc_len = var_descriptors_.Length();
+ for (intptr_t i = 0; i < var_desc_len; i++) {
+ RawLocalVarDescriptors::VarInfo var_info;
+ var_descriptors_.GetInfo(i, &var_info);
+ const int8_t kind = var_info.kind();
+ if (var_descriptors_.GetName(i) == Symbols::ControllerStream().raw()) {
+ ASSERT(kind == RawLocalVarDescriptors::kContextVar);
+ ASSERT(!ctx_.IsNull());
+ return ctx_.At(var_info.index());
+ }
+ }
+ } else {
+ // On the stack.
+ for (intptr_t i = 0; i < var_desc_len; i++) {
+ RawLocalVarDescriptors::VarInfo var_info;
+ var_descriptors_.GetInfo(i, &var_info);
+ if (var_descriptors_.GetName(i) == Symbols::ControllerStream().raw()) {
+ const int8_t kind = var_info.kind();
+ if (kind == RawLocalVarDescriptors::kStackVar) {
+ return GetStackVar(var_info.index());
+ } else {
+ ASSERT(kind == RawLocalVarDescriptors::kContextVar);
+ return GetContextVar(var_info.scope_id, var_info.index());
+ }
+ }
+ }
+ }
+ return Object::null();
+}
+
+
+RawObject* ActivationFrame::GetAsyncStreamControllerStreamAwaiter(
+ const Object& stream) {
+ const Class& stream_cls = Class::Handle(stream.clazz());
+ ASSERT(!stream_cls.IsNull());
+ const Class& stream_impl_cls = Class::Handle(stream_cls.SuperClass());
+ const Field& awaiter_field = Field::Handle(
+ stream_impl_cls.LookupInstanceFieldAllowPrivate(Symbols::_Awaiter()));
+ ASSERT(!awaiter_field.IsNull());
+ return Instance::Cast(stream).GetField(awaiter_field);
+}
+
+
+RawObject* ActivationFrame::GetAsyncAwaiter() {
+ const Object& completer = Object::Handle(GetAsyncCompleter());
+ if (!completer.IsNull()) {
+ return GetAsyncCompleterAwaiter(completer);
+ }
+ const Object& async_stream_controller_stream =
+ Object::Handle(GetAsyncStreamControllerStream());
+ if (!async_stream_controller_stream.IsNull()) {
+ return GetAsyncStreamControllerStreamAwaiter(
+ async_stream_controller_stream);
+ }
+ return Object::null();
+}
+
+
+void ActivationFrame::ExtractTokenPositionFromAsyncClosure() {
+ // Attempt to determine the token position from the async closure.
+ ASSERT(function_.IsAsyncGenClosure() || function_.IsAsyncClosure());
+ // This should only be called on frames that aren't active on the stack.
+ ASSERT(fp() == 0);
+ const Array& await_to_token_map =
+ Array::Handle(code_.await_token_positions());
+ if (await_to_token_map.IsNull()) {
+ OS::PrintErr("NO await_token_positions MAPPING\n");
+ // No mapping.
+ return;
+ }
+ GetVarDescriptors();
+ GetPcDescriptors();
+ intptr_t var_desc_len = var_descriptors_.Length();
+ intptr_t await_jump_var = -1;
+ for (intptr_t i = 0; i < var_desc_len; i++) {
+ RawLocalVarDescriptors::VarInfo var_info;
+ var_descriptors_.GetInfo(i, &var_info);
+ const int8_t kind = var_info.kind();
+ if (var_descriptors_.GetName(i) == Symbols::AwaitJumpVar().raw()) {
+ ASSERT(kind == RawLocalVarDescriptors::kContextVar);
+ ASSERT(!ctx_.IsNull());
+ Object& await_jump_index = Object::Handle(ctx_.At(var_info.index()));
+ ASSERT(await_jump_index.IsSmi());
+ await_jump_var = Smi::Cast(await_jump_index).Value();
+ OS::PrintErr("Extracted await var: %" Pd "\n", await_jump_var);
+ }
+ }
+ if (await_jump_var < 0) {
+ OS::PrintErr("await_jump_var < 0\n");
+ return;
+ }
+ ASSERT(await_jump_var < await_to_token_map.Length());
+ const Object& token_pos =
+ Object::Handle(await_to_token_map.At(await_jump_var));
+ if (token_pos.IsNull()) {
+ OS::PrintErr("token_pos was null\n");
+ return;
+ }
+ ASSERT(token_pos.IsSmi());
+ token_pos_ = TokenPosition(Smi::Cast(token_pos).Value());
+ token_pos_initialized_ = true;
+ OS::PrintErr("async token position: %s\n", token_pos_.ToCString());
+// Now that we have the token position, we clear a bunch of frame state that
+// will repopulated based on the token position.
+// Clear the context.
+#if 0
+ ctx_ ^= Object::null();
+ context_level_ = -1;
+ // Clear variable descriptors.
+ vars_initialized_ = false;
+ var_descriptors_ ^= Object::null();
+ desc_indices_.Clear();
+#endif
+}
+
+
RawObject* ActivationFrame::GetAsyncOperation() {
GetVarDescriptors();
intptr_t var_desc_len = var_descriptors_.Length();
@@ -740,7 +978,7 @@ void ActivationFrame::GetDescIndices() {
GetVarDescriptors();
TokenPosition activation_token_pos = TokenPos();
- if (!activation_token_pos.IsDebugPause()) {
+ if (!activation_token_pos.IsDebugPause() || suspended_frame_) {
// We don't have a token position for this frame, so can't determine
// which variables are visible.
vars_initialized_ = true;
@@ -871,6 +1109,35 @@ bool ActivationFrame::IsRewindable() const {
}
+bool ActivationFrame::IsCompleteOnAsyncReturn() {
+ const class Library& async_library = Library::Handle(Library::AsyncLibrary());
+ ASSERT(!async_library.IsNull());
+
+ const Function& complete_on_async_return_function =
+ Function::Handle(async_library.LookupFunctionAllowPrivate(
+ Symbols::CompleterCompleteOnAsyncReturn()));
+ ASSERT(!complete_on_async_return_function.IsNull());
+ return complete_on_async_return_function.raw() == function_.raw();
+}
+
+
+bool ActivationFrame::IsInAsyncStarStreamController() {
+ const class Library& async_library = Library::Handle(Library::AsyncLibrary());
+ ASSERT(!async_library.IsNull());
+
+ const Class& async_stream_controller =
+ Class::Handle(async_library.LookupClassAllowPrivate(
+ Symbols::_AsyncStarStreamController()));
+ ASSERT(!async_stream_controller.IsNull());
+ return function_.Owner() == async_stream_controller.raw();
+}
+
+
+bool ActivationFrame::IsInAsyncMachinery() {
+ return IsCompleteOnAsyncReturn() || IsInAsyncStarStreamController();
+}
+
+
void ActivationFrame::PrintContextMismatchError(intptr_t ctx_slot,
intptr_t frame_ctx_level,
intptr_t var_ctx_level) {
@@ -1086,8 +1353,54 @@ const char* ActivationFrame::ToCString() {
void ActivationFrame::PrintToJSONObject(JSONObject* jsobj, bool full) {
- const Script& script = Script::Handle(SourceScript());
+ if (kind_ == kRegular) {
+ PrintToJSONObjectRegular(jsobj, full);
+ } else {
+ PrintToJSONObjectAsync(jsobj, full);
+ }
+}
+
+
+void ActivationFrame::PrintToJSONObjectAsync(JSONObject* jsobj, bool full) {
+ jsobj->AddProperty("type", "AsyncFrame");
+ jsobj->AddProperty("kind", KindToCString(kind_));
+ if (kind_ == kAsyncSuspensionMarker) {
+ jsobj->AddProperty("marker", "AsynchronousSuspension");
+ } else if (kind_ == kAsyncHistorical) {
+ PrintToJSONObjectHistoricalFrame(jsobj, full);
+ } else {
+ PrintToJSONObjectLiveFrame(jsobj, full);
+ }
+}
+
+
+void ActivationFrame::PrintToJSONObjectRegular(JSONObject* jsobj, bool full) {
jsobj->AddProperty("type", "Frame");
+ PrintToJSONObjectLiveFrame(jsobj, full);
+}
+
+
+void ActivationFrame::PrintToJSONObjectHistoricalFrame(JSONObject* jsobj,
+ bool full) {
+ 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::PrintToJSONObjectLiveFrame(JSONObject* jsobj, bool full) {
+ const Script& script = Script::Handle(SourceScript());
TokenPosition pos = TokenPos();
if (pos.IsSynthetic()) {
pos = pos.FromSynthetic();
@@ -1112,7 +1425,8 @@ void ActivationFrame::PrintToJSONObject(JSONObject* jsobj, bool full) {
TokenPosition visible_end_token_pos;
VariableAt(v, &var_name, &declaration_token_pos, &visible_start_token_pos,
&visible_end_token_pos, &var_value);
- if (var_name.raw() != Symbols::AsyncOperation().raw()) {
+ if ((var_name.raw() != Symbols::AsyncOperation().raw()) &&
+ (var_name.raw() != Symbols::AsyncCompleter().raw())) {
JSONObject jsvar(&jsvars);
jsvar.AddProperty("type", "BoundVariable");
var_name = String::ScrubName(var_name);
@@ -1129,6 +1443,7 @@ void ActivationFrame::PrintToJSONObject(JSONObject* jsobj, bool full) {
}
}
+
static bool IsFunctionVisible(const Function& function) {
return FLAG_show_invisible_frames || function.is_visible();
}
@@ -1141,6 +1456,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::AddAsyncHistoricalFrame(uword pc, const Code& code) {
+ trace_.Add(new ActivationFrame(pc, 0, 0, code, Array::Handle(), 0,
+ ActivationFrame::kAsyncHistorical));
+}
+
+
const uint8_t kSafepointKind = RawPcDescriptors::kIcCall |
RawPcDescriptors::kUnoptStaticCall |
RawPcDescriptors::kRuntimeCall;
@@ -1229,6 +1557,15 @@ void CodeBreakpoint::Disable() {
}
+const char* CodeBreakpoint::ToCString() {
+ Zone* zone = Thread::Current()->zone();
+ ASSERT(zone != NULL);
+ return OS::SCreate(zone, "%s: %" Pd " (token %s)",
+ String::Handle(SourceUrl()).ToCString(), LineNumber(),
+ token_pos().ToCString());
+}
+
+
RemoteObjectCache::RemoteObjectCache(intptr_t initial_size) {
objs_ =
&GrowableObjectArray::ZoneHandle(GrowableObjectArray::New(initial_size));
@@ -1268,7 +1605,11 @@ Debugger::Debugger()
pause_event_(NULL),
obj_cache_(NULL),
stack_trace_(NULL),
+ async_stack_trace_(NULL),
+ async_return_call_stack_(NULL),
stepping_fp_(0),
+ top_frame_awaiter_(Object::null()),
+ async_stepping_fp_(0),
skip_next_step_(false),
synthetic_async_breakpoint_(NULL),
exc_pause_info_(kNoPauseOnExceptions) {}
@@ -1281,6 +1622,7 @@ Debugger::~Debugger() {
ASSERT(breakpoint_locations_ == NULL);
ASSERT(code_breakpoints_ == NULL);
ASSERT(stack_trace_ == NULL);
+ ASSERT(async_stack_trace_ == NULL);
ASSERT(obj_cache_ == NULL);
ASSERT(synthetic_async_breakpoint_ == NULL);
}
@@ -1327,6 +1669,38 @@ static RawFunction* ResolveLibraryFunction(const Library& library,
}
+bool Debugger::SteppedForSyntheticAsyncBreakpoint() const {
+ return synthetic_async_breakpoint_ != NULL;
+}
+
+
+RawError* Debugger::StepForSyntheticAsyncBreakpoint(Breakpoint* bpt) {
+ ASSERT(bpt->is_synthetic_async());
+
+ TD_Print("ASYNC: Auto step-over\n");
+ CacheStackTraces(CollectStackTrace(), CollectAsyncStackTrace(),
+ CollectAsyncReturnCallStack());
+
+ ASSERT(synthetic_async_breakpoint_ == NULL);
+ synthetic_async_breakpoint_ = bpt;
+ // We are at the entry of an async closure.
+ // We issue a step over to resume at the point after the await statement.
+ SetResumeAction(kStepOver);
+ // When we single step from a breakpoint, our next stepping point will be at
+ // the exact same pc. Skip it.
+ HandleSteppingRequest(stack_trace_, true /* skip next step */);
+ ClearCachedStackTraces();
+ return Error::null();
+}
+
+
+void Debugger::CleanupSyntheticAsyncBreakpoint() {
+ if (synthetic_async_breakpoint_ != NULL) {
+ RemoveBreakpoint(synthetic_async_breakpoint_->id());
+ synthetic_async_breakpoint_ = NULL;
+ }
+}
+
bool Debugger::SetupStepOverAsyncSuspension(const char** error) {
ActivationFrame* top_frame = TopDartFrame();
if (!IsAtAsyncJump(top_frame)) {
@@ -1352,6 +1726,30 @@ bool Debugger::SetupStepOverAsyncSuspension(const char** error) {
}
+// Returns the stepping frame pointer when stepping into a caller of
+// the async activation generator functions.
+uword Debugger::GetAsyncActivationGeneratorSteppingFramePointer() {
+ StackFrameIterator iterator(false);
+ // Scan until we hit the top Dart frame.
+ StackFrame* frame = iterator.NextFrame();
+ while ((frame != NULL) && !frame->IsDartFrame()) {
+ frame = iterator.NextFrame();
+ }
+ if (frame != NULL) {
+ frame = iterator.NextFrame();
+ if ((frame != NULL) && frame->IsDartFrame()) {
+ // Check to see if we are in a callee of the async function.
+ const Function& function = Function::Handle(frame->LookupDartFunction());
+ if (function.IsAsyncFunction() || function.IsAsyncGenerator()) {
+ // We are, continue until this function has exited.
+ return frame->fp();
+ }
+ }
+ }
+ return 0;
+}
+
+
bool Debugger::SetResumeAction(ResumeAction action,
intptr_t frame_index,
const char** error) {
@@ -1462,10 +1860,12 @@ ActivationFrame* Debugger::CollectDartFrame(Isolate* isolate,
StackFrame* frame,
const Code& code,
const Array& deopt_frame,
- intptr_t deopt_frame_offset) {
+ intptr_t deopt_frame_offset,
+ ActivationFrame::Kind kind) {
ASSERT(code.ContainsInstructionAt(pc));
- ActivationFrame* activation = new ActivationFrame(
- pc, frame->fp(), frame->sp(), code, deopt_frame, deopt_frame_offset);
+ ActivationFrame* activation =
+ new ActivationFrame(pc, frame->fp(), frame->sp(), code, deopt_frame,
+ deopt_frame_offset, kind);
if (FLAG_trace_debugger_stacktrace) {
const Context& ctx = activation->GetSavedCurrentContext();
OS::PrintErr("\tUsing saved context: %s\n", ctx.ToCString());
@@ -1546,6 +1946,166 @@ DebuggerStackTrace* Debugger::CollectStackTrace() {
}
+DebuggerStackTrace* Debugger::CollectAsyncStackTrace() {
+ if (!FLAG_sane_async_stacks) {
+ return CollectStackTrace();
+ }
+ Thread* thread = Thread::Current();
+ Zone* zone = thread->zone();
+ Isolate* isolate = thread->isolate();
+ DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8);
+ StackFrameIterator iterator(false);
+ Code& code = Code::Handle(zone);
+ Smi& offset = Smi::Handle();
+ Function& function = Function::Handle(zone);
+ Code& inlined_code = Code::Handle(zone);
+ Array& deopt_frame = Array::Handle(zone);
+
+ Function& async_function = Function::Handle(zone);
+ Array& async_code_array = Array::Handle(zone);
+ Array& async_pc_offset_array = Array::Handle(zone);
+ const intptr_t async_stack_trace_length =
+ StackTraceUtils::ExtractAsyncStackTraceInfo(
+ thread, &async_function, &async_code_array, &async_pc_offset_array);
+
+ for (StackFrame* frame = iterator.NextFrame(); frame != NULL;
+ frame = iterator.NextFrame()) {
+ ASSERT(frame->IsValid());
+ if (FLAG_trace_debugger_stacktrace) {
+ OS::PrintErr("CollectStackTrace: visiting frame:\n\t%s\n",
+ frame->ToCString());
+ }
+ 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());
+ }
+ intptr_t deopt_frame_offset = it.GetDeoptFpOffset();
+ ActivationFrame* activation_frame = CollectDartFrame(
+ isolate, it.pc(), frame, inlined_code, deopt_frame,
+ deopt_frame_offset, ActivationFrame::kAsyncLive);
+ stack_trace->AddActivation(activation_frame);
+ }
+ } else {
+ ActivationFrame* activation_frame = CollectDartFrame(
+ isolate, frame->pc(), frame, code, Object::null_array(), 0,
+ ActivationFrame::kAsyncLive);
+ stack_trace->AddActivation(activation_frame);
+ if (async_stack_trace_length > 0) {
+ // Stop once we hit async closure.
+ function = code.function();
+ if (function.parent_function() == async_function.raw()) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ ASSERT((async_stack_trace_length == 0) ||
+ (function.parent_function() == async_function.raw()));
+ // Append the asynchronous stack trace.
+ for (intptr_t i = 0; i < async_stack_trace_length; i++) {
+ if (async_code_array.At(i) == Code::null()) {
+ break;
+ }
+ if (async_code_array.At(i) ==
+ StubCode::AsynchronousGapMarker_entry()->code()) {
+ stack_trace->AddMarker(ActivationFrame::kAsyncSuspensionMarker);
+ } else {
+ code = Code::RawCast(async_code_array.At(i));
+ offset = Smi::RawCast(async_pc_offset_array.At(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->AddAsyncHistoricalFrame(pc, inlined_code);
+ }
+ } else {
+ stack_trace->AddAsyncHistoricalFrame(pc, code);
+ }
+ }
+ }
+ return stack_trace;
+}
+
+
+DebuggerStackTrace* Debugger::CollectAsyncReturnCallStack() {
+ Thread* thread = Thread::Current();
+ Zone* zone = thread->zone();
+ Isolate* isolate = thread->isolate();
+ DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8);
+ StackFrameIterator iterator(false);
+ Code& code = Code::Handle(zone);
+ Code& inlined_code = Code::Handle(zone);
+ Array& deopt_frame = Array::Handle(zone);
+ Function& function = Function::Handle(zone);
+ Closure& async_activation = Closure::Handle(zone);
+
+ for (StackFrame* frame = iterator.NextFrame(); frame != NULL;
+ frame = iterator.NextFrame()) {
+ ASSERT(frame->IsValid());
+ if (FLAG_trace_debugger_stacktrace) {
+ OS::PrintErr("CollectStackTrace: visiting frame:\n\t%s\n",
+ frame->ToCString());
+ }
+ if (frame->IsDartFrame()) {
+ code = frame->LookupDartCode();
+ function = code.function();
+ if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) {
+ ActivationFrame* activation = CollectDartFrame(
+ isolate, frame->pc(), frame, code, Object::null_array(), 0,
+ ActivationFrame::kAsyncLive);
+ ASSERT(activation != NULL);
+ stack_trace->AddActivation(activation);
+ // Grab the awaiter.
+ async_activation ^= activation->GetAsyncAwaiter();
+ break;
+ }
+ 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) {
+ function = 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, ActivationFrame::kAsyncLive));
+ }
+ } else {
+ stack_trace->AddActivation(CollectDartFrame(
+ isolate, frame->pc(), frame, code, Object::null_array(), 0,
+ ActivationFrame::kAsyncLive));
+ }
+ }
+ }
+
+ // Append the asynchronous return call stack.
+ while (!async_activation.IsNull()) {
+ ActivationFrame* activation = new ActivationFrame(async_activation);
+ async_activation ^= activation->GetAsyncAwaiter();
+ activation->ExtractTokenPositionFromAsyncClosure();
+ stack_trace->AddActivation(activation);
+ }
+
+ return stack_trace;
+}
+
+
ActivationFrame* Debugger::TopDartFrame() const {
StackFrameIterator iterator(false);
StackFrame* frame = iterator.NextFrame();
@@ -1563,10 +2123,34 @@ DebuggerStackTrace* Debugger::StackTrace() {
return (stack_trace_ != NULL) ? stack_trace_ : CollectStackTrace();
}
+
DebuggerStackTrace* Debugger::CurrentStackTrace() {
return CollectStackTrace();
}
+
+DebuggerStackTrace* Debugger::AsyncStackTrace() {
+ return (async_stack_trace_ != NULL) ? async_stack_trace_
+ : CollectAsyncStackTrace();
+}
+
+
+DebuggerStackTrace* Debugger::CurrentAsyncStackTrace() {
+ return CollectAsyncStackTrace();
+}
+
+
+DebuggerStackTrace* Debugger::AsyncReturnStack() {
+ return (async_return_call_stack_ != NULL) ? async_return_call_stack_
+ : CollectAsyncReturnCallStack();
+}
+
+
+DebuggerStackTrace* Debugger::CurrentAsyncReturnStack() {
+ return CollectAsyncReturnCallStack();
+}
+
+
DebuggerStackTrace* Debugger::StackTraceFrom(const class StackTrace& ex_trace) {
DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8);
Function& function = Function::Handle();
@@ -1659,6 +2243,8 @@ void Debugger::PauseException(const Instance& exc) {
return;
}
DebuggerStackTrace* stack_trace = CollectStackTrace();
+ DebuggerStackTrace* async_stack_trace = CollectAsyncStackTrace();
+ DebuggerStackTrace* async_return_call_stack = CollectAsyncReturnCallStack();
if (!ShouldPauseOnException(stack_trace, exc)) {
return;
}
@@ -1667,11 +2253,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, async_stack_trace, async_return_call_stack);
Pause(&event);
HandleSteppingRequest(stack_trace_); // we may get a rewind request
- stack_trace_ = NULL;
+ ClearCachedStackTraces();
}
@@ -2544,6 +3129,7 @@ void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) {
cbpt->VisitObjectPointers(visitor);
cbpt = cbpt->next();
}
+ visitor->VisitPointer(reinterpret_cast<RawObject**>(&top_frame_awaiter_));
}
@@ -2603,6 +3189,20 @@ void Debugger::Pause(ServiceEvent* event) {
}
+void Debugger::AsyncContinue() {
+ SetResumeAction(kContinue);
+ stepping_fp_ = 0;
+ async_stepping_fp_ = 0;
+ isolate_->set_single_step(false);
+}
+
+
+void Debugger::AsyncStepInto(const Closure& async_op) {
+ SetBreakpointAtActivation(async_op, true);
+ AsyncContinue();
+}
+
+
void Debugger::EnterSingleStepMode() {
stepping_fp_ = 0;
DeoptimizeWorld();
@@ -2612,7 +3212,9 @@ void Debugger::EnterSingleStepMode() {
void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace,
bool skip_next_step) {
+ top_frame_awaiter_ = stack_trace->FrameAt(0)->GetAsyncAwaiter();
stepping_fp_ = 0;
+ async_stepping_fp_ = 0;
if (resume_action_ == kStepInto) {
// When single stepping, we need to deoptimize because we might be
// stepping into optimized code. This happens in particular if
@@ -2622,6 +3224,10 @@ void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace,
DeoptimizeWorld();
isolate_->set_single_step(true);
skip_next_step_ = skip_next_step;
+ if (stack_trace->FrameAt(0)->function().IsAsyncClosure() ||
+ stack_trace->FrameAt(0)->function().IsAsyncGenClosure()) {
+ async_stepping_fp_ = stack_trace->FrameAt(0)->fp();
+ }
if (FLAG_verbose_debug) {
OS::Print("HandleSteppingRequest- kStepInto\n");
}
@@ -2631,10 +3237,29 @@ void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace,
skip_next_step_ = skip_next_step;
ASSERT(stack_trace->Length() > 0);
stepping_fp_ = stack_trace->FrameAt(0)->fp();
+ if (stack_trace->FrameAt(0)->function().IsAsyncClosure() ||
+ stack_trace->FrameAt(0)->function().IsAsyncGenClosure()) {
+ async_stepping_fp_ = stack_trace->FrameAt(0)->fp();
+ }
if (FLAG_verbose_debug) {
OS::Print("HandleSteppingRequest- kStepOver %" Px "\n", stepping_fp_);
}
} else if (resume_action_ == kStepOut) {
+ // Handle the async case first.
+ if (stack_trace->FrameAt(0)->function().IsAsyncClosure() ||
+ stack_trace->FrameAt(0)->function().IsAsyncGenClosure()) {
+ if (top_frame_awaiter_ != Object::null()) {
+ const Object& async_op = Object::Handle(top_frame_awaiter_);
+ ASSERT(async_op.IsClosure());
+ AsyncStepInto(Closure::Cast(async_op));
+ return;
+ } else {
+ // No awaiter. Just continue execution.
+ AsyncContinue();
+ return;
+ }
+ }
+ // Now the synchronous case.
DeoptimizeWorld();
isolate_->set_single_step(true);
// Find topmost caller that is debuggable.
@@ -2680,6 +3305,25 @@ static intptr_t FindNextRewindFrameIndex(DebuggerStackTrace* stack,
}
+void Debugger::CacheStackTraces(DebuggerStackTrace* stack_trace,
+ DebuggerStackTrace* async_stack_trace,
+ DebuggerStackTrace* async_return_call_stack) {
+ ASSERT(stack_trace_ == NULL);
+ stack_trace_ = stack_trace;
+ ASSERT(async_stack_trace_ == NULL);
+ async_stack_trace_ = async_stack_trace;
+ ASSERT(async_return_call_stack_ == NULL);
+ async_return_call_stack_ = async_return_call_stack;
+}
+
+
+void Debugger::ClearCachedStackTraces() {
+ stack_trace_ = NULL;
+ async_stack_trace_ = NULL;
+ async_return_call_stack_ = NULL;
+}
+
+
// Can the top frame be rewound?
bool Debugger::CanRewindFrame(intptr_t frame_index, const char** error) const {
// check rewind pc is found
@@ -2796,7 +3440,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 +3472,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();
@@ -2904,6 +3548,7 @@ bool Debugger::IsDebuggable(const Function& func) {
void Debugger::SignalPausedEvent(ActivationFrame* top_frame, Breakpoint* bpt) {
resume_action_ = kContinue;
stepping_fp_ = 0;
+ async_stepping_fp_ = 0;
isolate_->set_single_step(false);
ASSERT(!IsPaused());
ASSERT(obj_cache_ == NULL);
@@ -2953,14 +3598,44 @@ RawError* Debugger::PauseStepping() {
return Error::null();
}
+ ActivationFrame* frame = TopDartFrame();
+ ASSERT(frame != NULL);
+
+ OS::PrintErr("top frame: %s\n", frame->ToCString());
+ OS::PrintErr("async_stepping_fp: %" Px "\n", async_stepping_fp_);
+ OS::PrintErr("IsInAsyncMachinery: %d\n", frame->IsInAsyncMachinery());
+
+ // Check if the user has single stepped out of an asynchronous function
+ // with an awaiter. If so, the next point of execution will be in the awaiter.
+ if (async_stepping_fp_ != 0) {
+ // We have either single stepped into async machinery or we have
+ // stepped out of the function.
+ const bool exited_async_function =
+ (IsCalleeFrameOf(async_stepping_fp_, frame->fp()) &&
+ frame->IsInAsyncMachinery()) ||
+ IsCalleeFrameOf(frame->fp(), async_stepping_fp_);
+ OS::PrintErr("exited_async_function = %d\n", exited_async_function);
+ if (exited_async_function) {
+ // We returned from the asynchronous function.
+ if (top_frame_awaiter_ == Object::null()) {
+ AsyncContinue();
+ return Error::null();
+ } else {
+ ASSERT(top_frame_awaiter_ != Object::null());
+ const Object& async_op = Object::Handle(top_frame_awaiter_);
+ ASSERT(async_op.IsClosure());
+ top_frame_awaiter_ = Object::null();
+ AsyncStepInto(Closure::Cast(async_op));
+ return Error::null();
+ }
+ }
+ }
+
// Check whether we are in a Dart function that the user is
// interested in. If we saved the frame pointer of a stack frame
// the user is interested in, we ignore the single step if we are
// in a callee of that frame. Note that we assume that the stack
// grows towards lower addresses.
- ActivationFrame* frame = TopDartFrame();
- ASSERT(frame != NULL);
-
if (stepping_fp_ != 0) {
// There is an "interesting frame" set. Only pause at appropriate
// locations in this frame.
@@ -2974,6 +3649,16 @@ RawError* Debugger::PauseStepping() {
// and let the user set the "interesting" frame again.
stepping_fp_ = 0;
}
+ } else if (FLAG_sane_async_stacks && !SteppedForSyntheticAsyncBreakpoint()) {
+ const uword possible_stepping_fp =
+ GetAsyncActivationGeneratorSteppingFramePointer();
+ if (possible_stepping_fp > 0) {
+ // We are currently executing in a callee of an async activation generator
+ // function. We do not want the user to see any of this code, so we will
+ // continue to step until we have exited the async activation generator.
+ stepping_fp_ = possible_stepping_fp;
+ return Error::null();
+ }
}
if (!frame->IsDebuggable()) {
@@ -2996,18 +3681,14 @@ RawError* Debugger::PauseStepping() {
frame->TokenPos().ToCString());
}
- ASSERT(stack_trace_ == NULL);
- stack_trace_ = CollectStackTrace();
- // 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;
+ CacheStackTraces(CollectStackTrace(), CollectAsyncStackTrace(),
+ CollectAsyncReturnCallStack());
+ if (SteppedForSyntheticAsyncBreakpoint()) {
+ CleanupSyntheticAsyncBreakpoint();
}
+ SignalPausedEvent(frame, 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());
@@ -3024,88 +3705,23 @@ RawError* Debugger::PauseBreakpoint() {
return Error::null();
}
DebuggerStackTrace* stack_trace = CollectStackTrace();
+ DebuggerStackTrace* async_stack_trace = CollectAsyncStackTrace();
+ DebuggerStackTrace* async_return_call_stack = CollectAsyncReturnCallStack();
ASSERT(stack_trace->Length() > 0);
ActivationFrame* top_frame = stack_trace->FrameAt(0);
ASSERT(top_frame != NULL);
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();
}
if (bpt_hit->is_synthetic_async()) {
- DebuggerStackTrace* stack_trace = CollectStackTrace();
- ASSERT(stack_trace->Length() > 0);
- ASSERT(stack_trace_ == NULL);
- stack_trace_ = stack_trace;
-
- // Hit a synthetic async breakpoint.
- if (FLAG_verbose_debug) {
- OS::Print(">>> hit synthetic breakpoint at %s:%" Pd
- " "
- "(token %s) (address %#" Px ")\n",
- String::Handle(cbpt->SourceUrl()).ToCString(),
- cbpt->LineNumber(), cbpt->token_pos().ToCString(),
- top_frame->pc());
- }
-
- ASSERT(synthetic_async_breakpoint_ == NULL);
- synthetic_async_breakpoint_ = bpt_hit;
- bpt_hit = NULL;
-
- // We are at the entry of an async function.
- // We issue a step over to resume at the point after the await statement.
- SetResumeAction(kStepOver);
- // 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;
- return Error::null();
+ TD_Print("ASYNC: hit synthetic breakpoint: %s (address: %#" Px ")\n",
+ cbpt->ToCString(), top_frame->pc());
+ return StepForSyntheticAsyncBreakpoint(bpt_hit);
}
if (FLAG_verbose_debug) {
@@ -3117,13 +3733,12 @@ RawError* Debugger::PauseBreakpoint() {
cbpt->token_pos().ToCString(), top_frame->pc());
}
- ASSERT(stack_trace_ == NULL);
- stack_trace_ = stack_trace;
+ CacheStackTraces(stack_trace, async_stack_trace, async_return_call_stack);
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();
}
@@ -3135,6 +3750,50 @@ 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. ; then ; 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
@@ -3143,10 +3802,8 @@ void Debugger::PauseDeveloper(const String& msg) {
return;
}
- DebuggerStackTrace* stack_trace = CollectStackTrace();
- ASSERT(stack_trace->Length() > 0);
- ASSERT(stack_trace_ == NULL);
- stack_trace_ = stack_trace;
+ CacheStackTraces(CollectStackTrace(), CollectAsyncStackTrace(),
+ CollectAsyncReturnCallStack());
// TODO(johnmccutchan): Send |msg| to Observatory.
@@ -3156,7 +3813,7 @@ void Debugger::PauseDeveloper(const String& msg) {
SetResumeAction(kStepOut);
HandleSteppingRequest(stack_trace_);
- stack_trace_ = NULL;
+ ClearCachedStackTraces();
}
@@ -3174,6 +3831,51 @@ void Debugger::Initialize(Isolate* isolate) {
}
+void Debugger::WhenRunnable() {
+ // Lookup some internal implementation functions and mark them as
+ // not-debuggable (so the user never steps into them) and
+ // not-inlinable (so that we can cheaply detect their presence on the stack).
+ Thread::EnterIsolate(isolate_);
+ Thread* thread = Thread::Current();
+ ASSERT(thread != NULL);
+ {
+ StackZone zone(thread);
+ HandleScope handle_scope(thread);
+
+ const Library& async_library =
+ Library::Handle(isolate_->object_store()->async_library());
+ ASSERT(!async_library.IsNull());
+
+ // This function is called when an async function completes their completer.
+ const Function& complete_on_async_return_function =
+ Function::Handle(async_library.LookupFunctionAllowPrivate(
+ Symbols::CompleterCompleteOnAsyncReturn()));
+ ASSERT(!complete_on_async_return_function.IsNull());
+ complete_on_async_return_function.set_is_debuggable(false);
+ complete_on_async_return_function.set_is_inlinable(false);
+
+ // Class used to implement async* functions.
+ const Class& async_stream_controller =
+ Class::Handle(async_library.LookupClassAllowPrivate(
+ Symbols::_AsyncStarStreamController()));
+ ASSERT(!async_stream_controller.IsNull());
+ const Array& functions = Array::Handle(async_stream_controller.functions());
+ ASSERT(!functions.IsNull());
+ Function& function = Function::Handle();
+ for (intptr_t i = 0; i < functions.Length(); i++) {
+ function ^= functions.At(i);
+ if (function.IsNull()) {
+ // Skip padding.
+ continue;
+ }
+ function.set_is_debuggable(false);
+ function.set_is_inlinable(false);
+ }
+ }
+ Thread::ExitIsolate();
+}
+
+
void Debugger::NotifyIsolateCreated() {
if (NeedsIsolateEvents()) {
ServiceEvent event(isolate_, ServiceEvent::kIsolateStart);
« no previous file with comments | « runtime/vm/debugger.h ('k') | runtime/vm/flag_list.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698