Chromium Code Reviews| Index: runtime/vm/debugger.cc |
| =================================================================== |
| --- runtime/vm/debugger.cc (revision 28225) |
| +++ runtime/vm/debugger.cc (working copy) |
| @@ -10,6 +10,7 @@ |
| #include "vm/code_patcher.h" |
| #include "vm/compiler.h" |
| #include "vm/dart_entry.h" |
| +#include "vm/deopt_instructions.h" |
| #include "vm/flags.h" |
| #include "vm/globals.h" |
| #include "vm/longjump.h" |
| @@ -26,6 +27,8 @@ |
| namespace dart { |
| DEFINE_FLAG(bool, verbose_debug, false, "Verbose debugger messages"); |
| +DEFINE_FLAG(bool, use_new_stacktrace, true, |
| + "Use new stacktrace creation"); |
| Debugger::EventHandler* Debugger::event_handler_ = NULL; |
| @@ -133,6 +136,8 @@ |
| pc_desc_index_(-1), |
| line_number_(-1), |
| context_level_(-1), |
| + deopt_frame_(Array::ZoneHandle()), |
| + deopt_frame_offset_(0), |
| vars_initialized_(false), |
| var_descriptors_(LocalVarDescriptors::ZoneHandle()), |
| desc_indices_(8), |
| @@ -347,7 +352,7 @@ |
| RawContext* ActivationFrame::GetSavedEntryContext(const Context& ctx) { |
| GetVarDescriptors(); |
| intptr_t var_desc_len = var_descriptors_.Length(); |
| - for (int i = 0; i < var_desc_len; i++) { |
| + for (intptr_t i = 0; i < var_desc_len; i++) { |
| RawLocalVarDescriptors::VarInfo var_info; |
| var_descriptors_.GetInfo(i, &var_info); |
| if (var_info.kind == RawLocalVarDescriptors::kSavedEntryContext) { |
| @@ -358,6 +363,29 @@ |
| } |
| +RawContext* ActivationFrame::GetSavedEntryContextNew() { |
| + if (ctx_.IsNull()) { |
| + // We have bailed on providing a context for this frame. Bail for |
| + // the caller as well. |
| + return Context::null(); |
| + } |
| + |
| + // Attempt to find a saved context. |
| + GetVarDescriptors(); |
| + 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); |
| + if (var_info.kind == RawLocalVarDescriptors::kSavedEntryContext) { |
| + return reinterpret_cast<RawContext*>(GetLocalVarValue(var_info.index)); |
| + } |
| + } |
| + |
| + // No saved context. Return the current context. |
| + return ctx_.raw(); |
| +} |
| + |
| + |
| // Get the saved context if the callee of this activation frame is a |
| // closure function. |
| RawContext* ActivationFrame::GetSavedCurrentContext() { |
| @@ -421,7 +449,7 @@ |
| // Rather than potentially displaying incorrect values, we |
| // pretend that there are no variables in the frame. |
| // We should be more clever about this in the future. |
| - if (code().is_optimized()) { |
| + if (!FLAG_use_new_stacktrace && code().is_optimized()) { |
| vars_initialized_ = true; |
| return; |
| } |
| @@ -501,6 +529,20 @@ |
| } |
| +// TODO(hausner): Handle captured variables. |
| +RawInstance* ActivationFrame::GetLocalVarValue(intptr_t slot_index) { |
| + Instance& instance = Instance::Handle(); |
| + if (deopt_frame_.IsNull()) { |
| + uword var_address = fp() + slot_index * kWordSize; |
| + instance ^= reinterpret_cast<RawObject*>( |
| + *reinterpret_cast<uword*>(var_address)); |
| + } else { |
| + instance ^= deopt_frame_.At(deopt_frame_offset_ + slot_index); |
| + } |
| + return instance.raw(); |
| +} |
| + |
| + |
| void ActivationFrame::VariableAt(intptr_t i, |
| String* name, |
| intptr_t* token_pos, |
| @@ -945,7 +987,155 @@ |
| } |
| +static void PrintStackTraceError(const char* message, |
| + ActivationFrame* current_activation, |
| + ActivationFrame* callee_activation) { |
| + const Function& current = current_activation->function(); |
| + const Function& callee = callee_activation->function(); |
| + const Script& script = |
| + Script::Handle(Class::Handle(current.Owner()).script()); |
| + intptr_t line, col; |
| + script.GetTokenLocation(current_activation->TokenPos(), &line, &col); |
| + OS::PrintErr("Error building stack trace: %s:" |
| + "current function '%s' callee_function '%s' " |
| + " line %" Pd " column %" Pd "\n", |
| + message, |
| + current.ToFullyQualifiedCString(), |
| + callee.ToFullyQualifiedCString(), |
| + line, col); |
| +} |
| + |
| + |
| +ActivationFrame* Debugger::CollectDartFrame(Isolate* isolate, |
| + uword pc, |
| + StackFrame* frame, |
| + const Code& code, |
| + bool optimized, |
| + ActivationFrame* callee_activation, |
| + const Context& entry_ctx) { |
| + // We never provide both a callee activation and an entry context. |
| + ASSERT(callee_activation == NULL || entry_ctx.IsNull()); |
| + ActivationFrame* activation = |
| + new ActivationFrame(pc, frame->fp(), frame->sp(), code); |
| + |
| + // Recover the context for this frame. |
| + if (optimized) { |
| + // Bail out for optimized frames for now. |
| + activation->SetContext(Context::Handle(isolate)); |
| + |
| + } else if (callee_activation == NULL) { |
| + // No callee. Use incoming entry context. Could be from |
| + // isolate's top context or from an entry frame. |
| + activation->SetContext(entry_ctx); |
| + |
| + } else if (callee_activation->function().IsClosureFunction()) { |
| + // If the callee is a closure, we should have stored the context |
| + // in the current frame before making the call. |
| + const Context& closure_call_ctx = |
| + Context::Handle(isolate, activation->GetSavedCurrentContext()); |
| + activation->SetContext(closure_call_ctx); |
| + |
| + // Sometimes there is no saved context. This is a bug. |
| + // https://code.google.com/p/dart/issues/detail?id=12767 |
| + if (FLAG_verbose_debug && closure_call_ctx.IsNull()) { |
| + PrintStackTraceError( |
| + "Expected to find saved context for call to closure function", |
| + activation, callee_activation); |
| + } |
| + |
| + } else { |
| + // Use the context provided by our callee. This is either the |
| + // callee's context or a context that was saved in the callee's |
| + // frame. |
| + const Context& callee_ctx = |
| + Context::Handle(isolate, callee_activation->GetSavedEntryContextNew()); |
| + activation->SetContext(callee_ctx); |
| + } |
| + return activation; |
| +} |
| + |
| + |
| +RawArray* Debugger::DeoptimizeToArray(Isolate* isolate, |
| + StackFrame* frame, |
| + const Code& code) { |
| + ASSERT(code.is_optimized()); |
| + |
| + // Create the DeoptContext for this deoptimization. |
| + DeoptContext* deopt_context =new DeoptContext(frame, code, |
|
Ivan Posva
2013/10/08 18:49:46
= new
turnidge
2013/10/09 17:57:21
Done.
|
| + DeoptContext::kDestIsAllocated, |
| + NULL, NULL); |
| + isolate->set_deopt_context(deopt_context); |
| + |
| + deopt_context->FillDestFrame(); |
| + deopt_context->MaterializeDeferredObjects(); |
| + const Array& dest_frame = Array::Handle(deopt_context->DestFrameAsArray()); |
| + |
| + isolate->set_deopt_context(NULL); |
| + delete deopt_context; |
| + |
| + return dest_frame.raw(); |
| +} |
| + |
| + |
| +DebuggerStackTrace* Debugger::CollectStackTraceNew() { |
| + Isolate* isolate = Isolate::Current(); |
| + DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8); |
| + StackFrameIterator iterator(false); |
| + ActivationFrame* current_activation = NULL; |
| + Context& entry_ctx = Context::Handle(isolate->top_context()); |
| + for (StackFrame* frame = iterator.NextFrame(); |
| + frame != NULL; |
| + frame = iterator.NextFrame()) { |
| + ASSERT(frame->IsValid()); |
| + if (frame->IsEntryFrame()) { |
| + current_activation = NULL; |
| + entry_ctx = reinterpret_cast<EntryFrame*>(frame)->SavedContext(); |
| + |
| + } else if (frame->IsDartFrame()) { |
| + const Code& code = Code::Handle(isolate, frame->LookupDartCode()); |
| + if (code.is_optimized()) { |
| + const Array& deopt_frame = |
|
Ivan Posva
2013/10/08 18:49:46
You could move these handle allocations outside th
turnidge
2013/10/09 17:57:21
Done.
|
| + Array::Handle(DeoptimizeToArray(isolate, frame, code)); |
| + for (InlinedFunctionsIterator it(code, frame->pc()); |
| + !it.Done(); |
| + it.Advance()) { |
| + const Code& inlined_code = Code::Handle(isolate, it.code()); |
|
Ivan Posva
2013/10/08 18:49:46
ditto
turnidge
2013/10/09 17:57:21
Done.
|
| + intptr_t deopt_frame_offset = it.GetDeoptFpOffset(); |
| + current_activation = CollectDartFrame(isolate, |
| + it.pc(), |
| + frame, |
| + inlined_code, |
| + true, |
| + current_activation, |
| + entry_ctx); |
| + current_activation->SetDeoptFrame(deopt_frame, deopt_frame_offset); |
| + stack_trace->AddActivation(current_activation); |
| + entry_ctx = Context::null(); // Only use entry context once. |
| + } |
| + } else { |
| + current_activation = CollectDartFrame(isolate, |
| + frame->pc(), |
| + frame, |
| + code, |
| + false, |
| + current_activation, |
| + entry_ctx); |
| + stack_trace->AddActivation(current_activation); |
| + entry_ctx = Context::null(); // Only use entry context once. |
| + } |
| + } |
| + } |
| + return stack_trace; |
| +} |
| + |
| + |
| + |
| DebuggerStackTrace* Debugger::CollectStackTrace() { |
| + if (FLAG_use_new_stacktrace) { |
| + // Guard new stack trace generation under a flag in case there are |
| + // problems rolling it out. |
| + return CollectStackTraceNew(); |
| + } |
| Isolate* isolate = Isolate::Current(); |
| DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8); |
| Context& ctx = Context::Handle(isolate->top_context()); |