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