| Index: runtime/vm/debugger.cc
|
| ===================================================================
|
| --- runtime/vm/debugger.cc (revision 2087)
|
| +++ runtime/vm/debugger.cc (working copy)
|
| @@ -4,6 +4,7 @@
|
|
|
| #include "vm/debugger.h"
|
|
|
| +#include "vm/code_index_table.h"
|
| #include "vm/code_patcher.h"
|
| #include "vm/compiler.h"
|
| #include "vm/flags.h"
|
| @@ -11,6 +12,7 @@
|
| #include "vm/object.h"
|
| #include "vm/object_store.h"
|
| #include "vm/os.h"
|
| +#include "vm/stack_frame.h"
|
| #include "vm/stub_code.h"
|
| #include "vm/visitor.h"
|
|
|
| @@ -21,36 +23,162 @@
|
| DEFINE_FLAG(charp, bpt, NULL, "Debug breakpoint at <func>");
|
|
|
|
|
| -class Breakpoint {
|
| - public:
|
| - Breakpoint(const Function& func, intptr_t pc_desc_index, uword pc)
|
| - : function_(func.raw()),
|
| - pc_desc_index_(pc_desc_index),
|
| - pc_(pc),
|
| - next_(NULL) {
|
| +
|
| +Breakpoint::Breakpoint(const Function& func, intptr_t pc_desc_index)
|
| + : function_(func.raw()),
|
| + pc_desc_index_(pc_desc_index),
|
| + pc_(0),
|
| + line_number_(-1),
|
| + next_(NULL) {
|
| + Code& code = Code::Handle(func.code());
|
| + ASSERT(!code.IsNull()); // Function must be compiled.
|
| + PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
|
| + ASSERT(pc_desc_index < desc.Length());
|
| + this->token_index_ = desc.TokenIndex(pc_desc_index);
|
| + ASSERT(this->token_index_ > 0);
|
| + this->pc_ = desc.PC(pc_desc_index);
|
| + ASSERT(this->pc_ != 0);
|
| +}
|
| +
|
| +
|
| +RawScript* Breakpoint::SourceCode() {
|
| + const Function& func = Function::Handle(this->function_);
|
| + const Class& cls = Class::Handle(func.owner());
|
| + return cls.script();
|
| +}
|
| +
|
| +
|
| +RawString* Breakpoint::SourceUrl() {
|
| + const Script& script = Script::Handle(this->SourceCode());
|
| + return script.url();
|
| +}
|
| +
|
| +
|
| +intptr_t Breakpoint::LineNumber() {
|
| + // Compute line number lazily since it causes scanning of the script.
|
| + if (this->line_number_ < 0) {
|
| + const Script& script = Script::Handle(this->SourceCode());
|
| + intptr_t ignore_column;
|
| + script.GetTokenLocation(this->token_index_,
|
| + &this->line_number_, &ignore_column);
|
| }
|
| + return this->line_number_;
|
| +}
|
|
|
| - RawFunction* function() const { return function_; }
|
| - uword pc() const { return pc_; }
|
|
|
| - void set_next(Breakpoint* value) { next_ = value; }
|
| - Breakpoint* next() const { return this->next_; }
|
| +void Breakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
| + visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_));
|
| +}
|
|
|
| - void VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
| - visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_));
|
| +
|
| +ActivationFrame::ActivationFrame(uword pc)
|
| + : pc_(pc),
|
| + function_(Function::null()),
|
| + token_index_(-1),
|
| + line_number_(-1) {
|
| +}
|
| +
|
| +
|
| +RawFunction* ActivationFrame::DartFunction() {
|
| + if (function_ == Function::null()) {
|
| + ASSERT(Isolate::Current() != NULL);
|
| + CodeIndexTable* code_index_table = Isolate::Current()->code_index_table();
|
| + ASSERT(code_index_table != NULL);
|
| + function_ = code_index_table->LookupFunction(pc_);
|
| }
|
| + return function_;
|
| +}
|
|
|
| - private:
|
| - RawFunction* function_;
|
| - intptr_t pc_desc_index_;
|
| - uword pc_;
|
| - Breakpoint* next_;
|
| - DISALLOW_COPY_AND_ASSIGN(Breakpoint);
|
| -};
|
|
|
| +RawString* ActivationFrame::SourceUrl() {
|
| + const Script& script = Script::Handle(SourceScript());
|
| + return script.url();
|
| +}
|
|
|
| +
|
| +RawScript* ActivationFrame::SourceScript() {
|
| + const Function& func = Function::Handle(DartFunction());
|
| + const Class& cls = Class::Handle(func.owner());
|
| + return cls.script();
|
| +}
|
| +
|
| +
|
| +intptr_t ActivationFrame::TokenIndex() {
|
| + if (token_index_ < 0) {
|
| + const Function& func = Function::Handle(DartFunction());
|
| + Code& code = Code::Handle(func.code());
|
| + ASSERT(!code.IsNull());
|
| + PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
|
| + for (int i = 0; i < desc.Length(); i++) {
|
| + if (desc.PC(i) == pc_) {
|
| + token_index_ = desc.TokenIndex(i);
|
| + break;
|
| + }
|
| + }
|
| + ASSERT(token_index_ >= 0);
|
| + }
|
| + return token_index_;
|
| +}
|
| +
|
| +
|
| +intptr_t ActivationFrame::LineNumber() {
|
| + // Compute line number lazily since it causes scanning of the script.
|
| + if (line_number_ < 0) {
|
| + const Script& script = Script::Handle(SourceScript());
|
| + intptr_t ignore_column;
|
| + script.GetTokenLocation(TokenIndex(), &line_number_, &ignore_column);
|
| + }
|
| + return line_number_;
|
| +}
|
| +
|
| +
|
| +RawArray* ActivationFrame::Variables() {
|
| + UNIMPLEMENTED();
|
| +}
|
| +
|
| +
|
| +RawInstance* ActivationFrame::Value(const String& variable_name) {
|
| + UNIMPLEMENTED();
|
| +}
|
| +
|
| +
|
| +const char* ActivationFrame::ToCString() {
|
| + const char* kFormat = "Function: '%s%s%s' url: '%s' line: %d";
|
| +
|
| + Function& func = Function::Handle(DartFunction());
|
| + String& func_name = String::Handle(func.name());
|
| + Class& func_class = Class::Handle(func.owner());
|
| + String& class_name = String::Handle(func_class.Name());
|
| + String& url = String::Handle(SourceUrl());
|
| + intptr_t line = LineNumber();
|
| +
|
| + intptr_t len = OS::SNPrint(NULL, 0, kFormat,
|
| + class_name.ToCString(),
|
| + func_class.IsTopLevel() ? "" : ".",
|
| + func_name.ToCString(),
|
| + url.ToCString(),
|
| + line);
|
| + len++; // String terminator.
|
| + char* chars = reinterpret_cast<char*>(
|
| + Isolate::Current()->current_zone()->Allocate(len));
|
| + OS::SNPrint(chars, len, kFormat,
|
| + class_name.ToCString(),
|
| + func_class.IsTopLevel() ? "" : ".",
|
| + func_name.ToCString(),
|
| + url.ToCString(),
|
| + line);
|
| + return chars;
|
| +}
|
| +
|
| +
|
| +void StackTrace::AddActivation(ActivationFrame* frame) {
|
| + this->trace_.Add(frame);
|
| +}
|
| +
|
| +
|
| Debugger::Debugger()
|
| : initialized_(false),
|
| + bp_handler_(NULL),
|
| breakpoints_(NULL) {
|
| }
|
|
|
| @@ -87,7 +215,6 @@
|
| }
|
|
|
|
|
| -
|
| void Debugger::SetBreakpointAtEntry(const String& class_name,
|
| const String& function_name) {
|
| Function& func = Function::Handle();
|
| @@ -97,8 +224,7 @@
|
| func = ResolveFunction(class_name, function_name);
|
| }
|
| if (func.IsNull()) {
|
| - OS::Print("could not find function '%s' in class '%s'\n",
|
| - function_name.ToCString(), class_name.ToCString());
|
| + OS::Print("could not find function '%s'\n", function_name.ToCString());
|
| return;
|
| }
|
| if (!func.HasCode()) {
|
| @@ -109,21 +235,26 @@
|
| PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
|
| for (int i = 0; i < desc.Length(); i++) {
|
| PcDescriptors::Kind kind = desc.DescriptorKind(i);
|
| + Breakpoint* bpt = NULL;
|
| if (kind == PcDescriptors::kIcCall) {
|
| - OS::Print("patching dynamic call at %p\n", desc.PC(i));
|
| CodePatcher::PatchInstanceCallAt(
|
| desc.PC(i), StubCode::BreakpointDynamicEntryPoint());
|
| - AddBreakpoint(new Breakpoint(func, i, desc.PC(i)));
|
| - return;
|
| + bpt = new Breakpoint(func, i);
|
| } else if (kind == PcDescriptors::kOther) {
|
| if (CodePatcher::IsDartCall(desc.PC(i))) {
|
| - OS::Print("patching static call at %p\n", desc.PC(i));
|
| CodePatcher::PatchStaticCallAt(
|
| desc.PC(i), StubCode::BreakpointStaticEntryPoint());
|
| - AddBreakpoint(new Breakpoint(func, i, desc.PC(i)));
|
| - return;
|
| + bpt = new Breakpoint(func, i);
|
| }
|
| }
|
| + if (bpt != NULL) {
|
| + OS::Print("Setting breakpoint at '%s' line %d (PC %p)\n",
|
| + String::Handle(bpt->SourceUrl()).ToCString(),
|
| + bpt->LineNumber(),
|
| + bpt->pc());
|
| + AddBreakpoint(bpt);
|
| + return;
|
| + }
|
| }
|
| OS::Print("no breakpoint location found in function '%s'\n",
|
| function_name.ToCString());
|
| @@ -140,6 +271,49 @@
|
| }
|
|
|
|
|
| +static void DefaultBreakpointHandler(Breakpoint* bpt, StackTrace* stack) {
|
| + for (intptr_t i = 0; i < stack->Length(); i++) {
|
| + OS::Print(" %d. %s\n",
|
| + i + 1, stack->ActivationFrameAt(i)->ToCString());
|
| + }
|
| +}
|
| +
|
| +
|
| +void Debugger::SetBreakpointHandler(BreakpointHandler* handler) {
|
| + bp_handler_ = handler;
|
| + if (bp_handler_ == NULL) {
|
| + bp_handler_ = &DefaultBreakpointHandler;
|
| + }
|
| +}
|
| +
|
| +
|
| +void Debugger::BreakpointCallback() {
|
| + ASSERT(initialized_);
|
| + DartFrameIterator iterator;
|
| + DartFrame* frame = iterator.NextFrame();
|
| + Function& func = Function::Handle();
|
| + ASSERT(frame != NULL);
|
| + Breakpoint* bpt = GetBreakpoint(frame->pc());
|
| + ASSERT(bpt != NULL);
|
| + OS::Print(">>> Breakpoint at %s:%d (Address %p)\n",
|
| + bpt ? String::Handle(bpt->SourceUrl()).ToCString() : "?",
|
| + bpt ? bpt->LineNumber() : 0,
|
| + frame->pc());
|
| + StackTrace* stack_trace = new StackTrace(8);
|
| + while (frame != NULL) {
|
| + ASSERT(frame->IsValid());
|
| + ASSERT(frame->IsDartFrame());
|
| + ActivationFrame* activation = new ActivationFrame(frame->pc());
|
| + stack_trace->AddActivation(activation);
|
| + frame = iterator.NextFrame();
|
| + }
|
| +
|
| + if (bp_handler_ != NULL) {
|
| + (*bp_handler_)(bpt, stack_trace);
|
| + }
|
| +}
|
| +
|
| +
|
| void Debugger::Initialize(Isolate* isolate) {
|
| if (initialized_) {
|
| return;
|
| @@ -161,6 +335,7 @@
|
| cname = String::New(reinterpret_cast<const uint8_t*>(FLAG_bpt),
|
| dot - FLAG_bpt);
|
| }
|
| + SetBreakpointHandler(DefaultBreakpointHandler);
|
| SetBreakpointAtEntry(cname, fname);
|
| }
|
|
|
|
|