| 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();
|
| }
|
|
|
|
|
|
|