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), |
@@ -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 (int i = 0; i < var_desc_len; i++) { |
srdjan
2013/10/07 21:36:53
s/int/intptr_t/
turnidge
2013/10/08 17:29:12
Done.
There is other cases like this in this file
srdjan
2013/10/08 18:41:25
Either here or a later CL.
|
+ 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) { |
+ if (deopt_frame_.IsNull()) { |
+ uword var_address = fp() + slot_index * kWordSize; |
srdjan
2013/10/07 21:36:53
If you assign the result first to an instance (as
turnidge
2013/10/08 17:29:12
Done.
turnidge
2013/10/08 18:21:11
Turns out this uncovered an oddness -- value isn't
|
+ return reinterpret_cast<RawInstance*>( |
+ *reinterpret_cast<uword*>(var_address)); |
+ } else { |
+ Instance& instance = Instance::Handle(); |
+ 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,154 @@ |
} |
+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()); |
srdjan
2013/10/07 21:36:53
Add parenthesis
turnidge
2013/10/08 17:29:12
Where do you want the parentheses?
turnidge
2013/10/08 18:21:11
Added the parentheses.
|
+ 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. |
srdjan
2013/10/07 21:36:53
Please add bug id.
turnidge
2013/10/08 17:29:12
Done.
|
+ 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; |
+} |
+ |
srdjan
2013/10/07 21:36:53
2 lines between functions
turnidge
2013/10/08 17:29:12
Done.
|
+RawArray* Debugger::DeoptimizeToArray(Isolate* isolate, |
+ StackFrame* frame, |
+ const Code& code) { |
srdjan
2013/10/07 21:36:53
Fix indent
turnidge
2013/10/08 17:29:12
Done.
|
+ ASSERT(code.is_optimized()); |
+ |
+ // Create the DeoptContext for this deoptimization. |
+ DeoptContext* deopt_context = |
+ new DeoptContext(frame, code, |
+ 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 = |
+ 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()); |
+ 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()); |