Index: runtime/vm/debugger.cc |
=================================================================== |
--- runtime/vm/debugger.cc (revision 28466) |
+++ 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), |
@@ -245,7 +250,7 @@ |
intptr_t ActivationFrame::TokenPos() { |
if (token_pos_ < 0) { |
GetPcDescriptors(); |
- for (int i = 0; i < pc_desc_.Length(); i++) { |
+ for (intptr_t i = 0; i < pc_desc_.Length(); i++) { |
if (pc_desc_.PC(i) == pc_) { |
pc_desc_index_ = i; |
token_pos_ = pc_desc_.TokenPos(i); |
@@ -321,7 +326,7 @@ |
ASSERT(activation_token_pos >= 0); |
GetVarDescriptors(); |
intptr_t var_desc_len = var_descriptors_.Length(); |
- for (int cur_idx = 0; cur_idx < var_desc_len; cur_idx++) { |
+ for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) { |
RawLocalVarDescriptors::VarInfo var_info; |
var_descriptors_.GetInfo(cur_idx, &var_info); |
if ((var_info.kind == RawLocalVarDescriptors::kContextLevel) && |
@@ -347,27 +352,50 @@ |
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) { |
- return reinterpret_cast<RawContext*>(GetLocalVarValue(var_info.index)); |
+ return GetLocalContextVar(var_info.index); |
} |
} |
return ctx.raw(); |
} |
+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 GetLocalContextVar(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() { |
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::kSavedCurrentContext) { |
- return reinterpret_cast<RawContext*>(GetLocalVarValue(var_info.index)); |
+ return GetLocalContextVar(var_info.index); |
} |
} |
return Context::null(); |
@@ -380,7 +408,9 @@ |
Array& handled_types = Array::Handle(); |
AbstractType& type = Type::Handle(); |
const TypeArguments& no_instantiator = TypeArguments::Handle(); |
- for (int frame_index = 0; frame_index < UnfilteredLength(); frame_index++) { |
+ for (intptr_t frame_index = 0; |
+ frame_index < UnfilteredLength(); |
+ frame_index++) { |
ActivationFrame* frame = UnfilteredFrameAt(frame_index); |
intptr_t try_index = frame->TryIndex(); |
if (try_index < 0) continue; |
@@ -393,7 +423,7 @@ |
ASSERT(num_handlers_checked <= handlers.Length()); |
handled_types = handlers.GetHandledTypes(try_index); |
const intptr_t num_types = handled_types.Length(); |
- for (int k = 0; k < num_types; k++) { |
+ for (intptr_t k = 0; k < num_types; k++) { |
type ^= handled_types.At(k); |
ASSERT(!type.IsNull()); |
// Uninstantiated types are not added to ExceptionHandlers data. |
@@ -421,7 +451,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; |
} |
@@ -436,7 +466,7 @@ |
GrowableArray<String*> var_names(8); |
intptr_t var_desc_len = var_descriptors_.Length(); |
- for (int cur_idx = 0; cur_idx < var_desc_len; cur_idx++) { |
+ for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) { |
ASSERT(var_names.length() == desc_indices_.length()); |
RawLocalVarDescriptors::VarInfo var_info; |
var_descriptors_.GetInfo(cur_idx, &var_info); |
@@ -462,7 +492,7 @@ |
String& var_name = String::Handle(var_descriptors_.GetName(cur_idx)); |
intptr_t indices_len = desc_indices_.length(); |
bool name_match_found = false; |
- for (int i = 0; i < indices_len; i++) { |
+ for (intptr_t i = 0; i < indices_len; i++) { |
if (var_name.Equals(*var_names[i])) { |
// Found two local variables with the same name. Now determine |
// which one is shadowed. |
@@ -500,7 +530,32 @@ |
return desc_indices_.length(); |
} |
+// TODO(hausner): Handle captured variables. |
+RawObject* ActivationFrame::GetLocalVar(intptr_t slot_index) { |
+ if (deopt_frame_.IsNull()) { |
+ uword var_address = fp() + slot_index * kWordSize; |
+ return reinterpret_cast<RawObject*>( |
+ *reinterpret_cast<uword*>(var_address)); |
+ } else { |
+ return deopt_frame_.At(deopt_frame_offset_ + slot_index); |
+ } |
+} |
+ |
+RawInstance* ActivationFrame::GetLocalInstanceVar(intptr_t slot_index) { |
+ Instance& instance = Instance::Handle(); |
+ instance ^= GetLocalVar(slot_index); |
+ return instance.raw(); |
+} |
+ |
+ |
+RawContext* ActivationFrame::GetLocalContextVar(intptr_t slot_index) { |
+ Context& context = Context::Handle(); |
+ context ^= GetLocalVar(slot_index); |
+ return context.raw(); |
+} |
+ |
+ |
void ActivationFrame::VariableAt(intptr_t i, |
String* name, |
intptr_t* token_pos, |
@@ -519,7 +574,7 @@ |
*end_pos = var_info.end_pos; |
ASSERT(value != NULL); |
if (var_info.kind == RawLocalVarDescriptors::kStackVar) { |
- *value = GetLocalVarValue(var_info.index); |
+ *value = GetLocalInstanceVar(var_info.index); |
} else { |
ASSERT(var_info.kind == RawLocalVarDescriptors::kContextVar); |
// The context level at the PC/token index of this activation frame. |
@@ -566,7 +621,7 @@ |
String& var_name = String::Handle(); |
Instance& value = Instance::Handle(); |
const Array& list = Array::Handle(Array::New(2 * num_variables)); |
- for (int i = 0; i < num_variables; i++) { |
+ for (intptr_t i = 0; i < num_variables; i++) { |
intptr_t ignore; |
VariableAt(i, &var_name, &ignore, &ignore, &value); |
list.SetAt(2 * i, var_name); |
@@ -762,7 +817,7 @@ |
intptr_t RemoteObjectCache::AddObject(const Object& obj) { |
intptr_t len = objs_->Length(); |
- for (int i = 0; i < len; i++) { |
+ for (intptr_t i = 0; i < len; i++) { |
if (objs_->At(i) == obj.raw()) { |
return i; |
} |
@@ -918,7 +973,7 @@ |
Code& code = Code::Handle(target_function.unoptimized_code()); |
ASSERT(!code.IsNull()); |
PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
- for (int i = 0; i < desc.Length(); i++) { |
+ for (intptr_t i = 0; i < desc.Length(); i++) { |
CodeBreakpoint* bpt = GetCodeBreakpoint(desc.PC(i)); |
if (bpt != NULL) { |
// There is already a breakpoint for this address. Make sure |
@@ -945,7 +1000,159 @@ |
} |
+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, |
+ 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()); |
+ Code& code = Code::Handle(isolate); |
+ Code& inlined_code = Code::Handle(isolate); |
+ Array& deopt_frame = Array::Handle(isolate); |
+ |
+ 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()) { |
+ code = frame->LookupDartCode(); |
+ if (code.is_optimized()) { |
+ deopt_frame = DeoptimizeToArray(isolate, frame, code); |
+ for (InlinedFunctionsIterator it(code, frame->pc()); |
+ !it.Done(); |
+ it.Advance()) { |
+ inlined_code = 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()); |
@@ -1103,7 +1310,7 @@ |
intptr_t best_fit = INT_MAX; |
uword lowest_pc = kUwordMax; |
intptr_t lowest_pc_index = -1; |
- for (int i = 0; i < desc.Length(); i++) { |
+ for (intptr_t i = 0; i < desc.Length(); i++) { |
intptr_t desc_token_pos = desc.TokenPos(i); |
ASSERT(desc_token_pos >= 0); |
if (desc_token_pos < first_token_pos) { |
@@ -1149,7 +1356,7 @@ |
Code& code = Code::Handle(func.unoptimized_code()); |
ASSERT(!code.IsNull()); |
PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
- for (int i = 0; i < desc.Length(); i++) { |
+ for (intptr_t i = 0; i < desc.Length(); i++) { |
intptr_t desc_token_pos = desc.TokenPos(i); |
if ((desc_token_pos == token_pos) && IsSafePoint(desc.DescriptorKind(i))) { |
CodeBreakpoint* code_bpt = GetCodeBreakpoint(desc.PC(i)); |
@@ -1276,7 +1483,7 @@ |
Script& script = Script::Handle(isolate_); |
const GrowableObjectArray& libs = |
GrowableObjectArray::Handle(isolate_->object_store()->libraries()); |
- for (int i = 0; i < libs.Length(); i++) { |
+ for (intptr_t i = 0; i < libs.Length(); i++) { |
lib ^= libs.At(i); |
script = lib.LookupScript(script_url); |
if (!script.IsNull()) { |
@@ -1423,7 +1630,7 @@ |
// Iterate over fields in class hierarchy to count all instance fields. |
while (!cls.IsNull()) { |
fields = cls.fields(); |
- for (int i = 0; i < fields.Length(); i++) { |
+ for (intptr_t i = 0; i < fields.Length(); i++) { |
field ^= fields.At(i); |
if (!field.is_static()) { |
field_name = field.name(); |
@@ -1445,7 +1652,7 @@ |
Field& field = Field::Handle(); |
String& field_name = String::Handle(); |
Object& field_value = Object::Handle(); |
- for (int i = 0; i < fields.Length(); i++) { |
+ for (intptr_t i = 0; i < fields.Length(); i++) { |
field ^= fields.At(i); |
if (field.is_static()) { |
field_name = field.name(); |
@@ -1505,7 +1712,7 @@ |
CollectLibraryFields(field_list, lib, prefix_name, true); |
Library& imported = Library::Handle(isolate_); |
intptr_t num_imports = lib.num_imports(); |
- for (int i = 0; i < num_imports; i++) { |
+ for (intptr_t i = 0; i < num_imports; i++) { |
imported = lib.ImportLibraryAt(i); |
ASSERT(!imported.IsNull()); |
CollectLibraryFields(field_list, imported, prefix_name, false); |
@@ -1517,7 +1724,7 @@ |
prefix_name = prefix.name(); |
ASSERT(!prefix_name.IsNull()); |
prefix_name = String::Concat(prefix_name, Symbols::Dot()); |
- for (int i = 0; i < prefix.num_imports(); i++) { |
+ for (intptr_t i = 0; i < prefix.num_imports(); i++) { |
imported = prefix.GetLibrary(i); |
CollectLibraryFields(field_list, imported, prefix_name, false); |
} |