Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1074)

Unified Diff: runtime/vm/debugger.cc

Issue 808643004: Support breakpoints in deferred libraries (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/debugger.h ('k') | runtime/vm/debugger_api_impl.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/debugger.cc
===================================================================
--- runtime/vm/debugger.cc (revision 42490)
+++ runtime/vm/debugger.cc (working copy)
@@ -54,12 +54,14 @@
};
+// Create an unresolved breakpoint in given token range and script.
SourceBreakpoint::SourceBreakpoint(intptr_t id,
const Script& script,
intptr_t token_pos,
intptr_t end_token_pos)
: id_(id),
script_(script.raw()),
+ url_(script.url()),
token_pos_(token_pos),
end_token_pos_(end_token_pos),
is_resolved_(false),
@@ -73,6 +75,24 @@
ASSERT(token_pos_ >= 0);
}
+// Create a latent breakpoint at given url and line number.
+SourceBreakpoint::SourceBreakpoint(intptr_t id,
+ const String& url,
+ intptr_t line_number)
+ : id_(id),
+ script_(Script::null()),
+ url_(url.raw()),
+ token_pos_(-1),
+ end_token_pos_(-1),
+ is_resolved_(false),
+ is_enabled_(false),
+ is_one_shot_(false),
+ next_(NULL),
+ function_(Function::null()),
+ line_number_(line_number) {
+ ASSERT(id >= 0);
+ ASSERT(line_number_ >= 0);
+}
void SourceBreakpoint::Enable() {
is_enabled_ = true;
@@ -87,6 +107,7 @@
void SourceBreakpoint::SetResolved(const Function& func, intptr_t token_pos) {
+ ASSERT(!IsLatent());
ASSERT(func.script() == script_);
ASSERT((func.token_pos() <= token_pos) &&
(token_pos <= func.end_token_pos()));
@@ -104,25 +125,28 @@
void SourceBreakpoint::GetCodeLocation(Library* lib,
Script* script,
intptr_t* pos) {
- *script = this->script();
- *pos = token_pos_;
- if (IsResolved()) {
- const Function& func = Function::Handle(function_);
- ASSERT(!func.IsNull());
- const Class& cls = Class::Handle(func.origin());
- *lib = cls.library();
+ if (IsLatent()) {
+ *lib = Library::null();
+ *script = Script::null();
+ *pos = -1;
} else {
- *lib = Library::null();
+ *script = this->script();
+ *pos = token_pos_;
+ if (IsResolved()) {
+ const Function& func = Function::Handle(function_);
+ ASSERT(!func.IsNull());
+ const Class& cls = Class::Handle(func.origin());
+ *lib = cls.library();
+ } else {
+ *lib = Library::null();
+ }
}
}
-RawString* SourceBreakpoint::SourceUrl() {
- return Script::Handle(script()).url();
-}
-
-
intptr_t SourceBreakpoint::LineNumber() {
+ // Latent breakpoints must have a requested line number >= 0.
+ ASSERT(!IsLatent() || line_number_ >= 0);
// Compute line number lazily since it causes scanning of the script.
if (line_number_ < 0) {
const Script& script = Script::Handle(this->script());
@@ -134,6 +158,7 @@
void SourceBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
visitor->VisitPointer(reinterpret_cast<RawObject**>(&script_));
+ visitor->VisitPointer(reinterpret_cast<RawObject**>(&url_));
visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_));
}
@@ -997,6 +1022,7 @@
isolate_id_(ILLEGAL_ISOLATE_ID),
initialized_(false),
next_id_(1),
+ latent_breakpoints_(NULL),
src_breakpoints_(NULL),
code_breakpoints_(NULL),
resume_action_(kContinue),
@@ -1012,6 +1038,7 @@
Debugger::~Debugger() {
isolate_id_ = ILLEGAL_ISOLATE_ID;
ASSERT(!IsPaused());
+ ASSERT(latent_breakpoints_ == NULL);
ASSERT(src_breakpoints_ == NULL);
ASSERT(code_breakpoints_ == NULL);
ASSERT(stack_trace_ == NULL);
@@ -1025,6 +1052,11 @@
src_breakpoints_ = src_breakpoints_->next();
delete bpt;
}
+ while (latent_breakpoints_ != NULL) {
+ SourceBreakpoint* bpt = latent_breakpoints_;
+ latent_breakpoints_ = latent_breakpoints_->next();
+ delete bpt;
+ }
while (code_breakpoints_ != NULL) {
CodeBreakpoint* bpt = code_breakpoints_;
code_breakpoints_ = code_breakpoints_->next();
@@ -1514,8 +1546,8 @@
cls = class_table.At(i);
// If the class is not finalized, e.g. if it hasn't been parsed
// yet entirely, we can ignore it. If it contains a function with
- // a latent breakpoint, we will detect it if and when the function
- // gets compiled.
+ // an unresolved breakpoint, we will detect it if and when the
+ // function gets compiled.
if (!cls.is_finalized()) {
continue;
}
@@ -1783,11 +1815,15 @@
}
}
if (scripts.Length() == 0) {
+ // No script found with given url. Create a latent breakpoint which
+ // will be set if the url is loaded later.
+ SourceBreakpoint* latent_bpt = GetLatentBreakpoint(script_url, line_number);
if (FLAG_verbose_debug) {
- OS::Print("Failed to find script with url '%s'\n",
- script_url.ToCString());
+ OS::Print("Set latent breakpoint in url '%s' at line %" Pd "\n",
+ script_url.ToCString(),
+ line_number);
}
- return NULL;
+ return latent_bpt;
}
if (scripts.Length() > 1) {
if (FLAG_verbose_debug) {
@@ -2033,6 +2069,11 @@
bpt->VisitObjectPointers(visitor);
bpt = bpt->next();
}
+ bpt = latent_breakpoints_;
+ while (bpt != NULL) {
+ bpt->VisitObjectPointers(visitor);
+ bpt = bpt->next();
+ }
CodeBreakpoint* cbpt = code_breakpoints_;
while (cbpt != NULL) {
cbpt->VisitObjectPointers(visitor);
@@ -2314,15 +2355,17 @@
continue;
}
intptr_t requested_pos = bpt->token_pos();
+ intptr_t requested_end_pos = bpt->end_token_pos();
bpt->SetResolved(func, bp_pos);
if (FLAG_verbose_debug) {
OS::Print("Resolved BP %" Pd " to pos %" Pd ", line %" Pd ", "
- "function '%s' (requested pos %" Pd ")\n",
+ "function '%s' (requested range %" Pd "-%" Pd ")\n",
bpt->id(),
bpt->token_pos(),
bpt->LineNumber(),
func.ToFullyQualifiedCString(),
- requested_pos);
+ requested_pos,
+ requested_end_pos);
}
SignalBpResolved(bpt);
}
@@ -2340,6 +2383,94 @@
}
+void Debugger::NotifyDoneLoading() {
+ if (latent_breakpoints_ == NULL) {
+ // Common, fast path.
+ return;
+ }
+ Library& lib = Library::Handle(isolate_);
+ Script& script = Script::Handle(isolate_);
+ String& url = String::Handle(isolate_);
+ SourceBreakpoint* bpt = latent_breakpoints_;
+ SourceBreakpoint* prev_bpt = NULL;
+ const GrowableObjectArray& libs =
+ GrowableObjectArray::Handle(isolate_->object_store()->libraries());
+ while (bpt != NULL) {
+ url = bpt->url();
+ for (intptr_t i = 0; i < libs.Length(); i++) {
+ lib ^= libs.At(i);
+ script = lib.LookupScript(url);
+ if (!script.IsNull()) {
+ // Found a script with matching url for this latent breakpoint.
+ // Unlink the latent breakpoint from the list.
+ SourceBreakpoint* matched_bpt = bpt;
+ bpt = bpt->next();
+ if (prev_bpt == NULL) {
+ latent_breakpoints_ = bpt;
+ } else {
+ prev_bpt->set_next(bpt);
+ }
+ // Now find the token range at the requested line and make a
+ // new unresolved source breakpoint.
+ intptr_t line_number = matched_bpt->LineNumber();
+ ASSERT(line_number >= 0);
+ intptr_t first_token_pos, last_token_pos;
+ script.TokenRangeAtLine(line_number, &first_token_pos, &last_token_pos);
+ if ((first_token_pos < 0) ||
+ (last_token_pos < 0)) {
+ // Script does not contain the given line number or there are no
+ // tokens on the line. Drop the breakpoint silently.
+ if (FLAG_verbose_debug) {
+ OS::Print("No code found at line %" Pd ": "
+ "dropping latent breakpoint %" Pd " in '%s'\n",
+ line_number,
+ matched_bpt->id(),
+ url.ToCString());
+ }
+ delete matched_bpt;
+ } else {
+ // We don't expect to already have a breakpoint for this location.
+ // If there is one, assert in debug build but silently drop
+ // the latent breakpoint in release build.
+ SourceBreakpoint* existing_bpt =
+ GetSourceBreakpoint(script, first_token_pos);
+ ASSERT(existing_bpt == NULL);
+ if (existing_bpt == NULL) {
+ // Create and register a new source breakpoint for the
+ // latent breakpoint.
+ SourceBreakpoint* unresolved_bpt =
+ new SourceBreakpoint(matched_bpt->id(),
+ script,
+ first_token_pos,
+ last_token_pos);
+ RegisterSourceBreakpoint(unresolved_bpt);
+ unresolved_bpt->Enable();
+ if (FLAG_verbose_debug) {
+ OS::Print("Converted latent breakpoint "
+ "%" Pd " in '%s' at line %" Pd "\n",
+ matched_bpt->id(),
+ url.ToCString(),
+ line_number);
+ }
+ }
+ delete matched_bpt;
+ // Break out of the iteration over loaded libraries. If the
+ // same url has been loaded into more than one library, we
+ // only set a breakpoint in the first one.
+ // TODO(hausner): There is one possible pitfall here.
+ // If the user sets a latent breakpoint using a partial url that
+ // ends up matching more than one script, the breakpoint might
+ // get set in the wrong script.
+ // It would be better if we could warn the user if multiple
+ // scripts are matching.
+ break;
+ }
+ }
+ }
+ }
+}
+
+
// TODO(hausner): Could potentially make this faster by checking
// whether the call target at pc is a debugger stub.
bool Debugger::HasActiveBreakpoint(uword pc) {
@@ -2470,6 +2601,25 @@
}
+SourceBreakpoint* Debugger::GetLatentBreakpoint(const String& url,
+ intptr_t line) {
+ SourceBreakpoint* bpt = latent_breakpoints_;
+ String& bpt_url = String::Handle(isolate_);
+ while (bpt != NULL) {
+ bpt_url = bpt->url();
+ if (bpt_url.Equals(url) && (bpt->LineNumber() == line)) {
+ return bpt;
+ }
+ bpt = bpt->next();
+ }
+ // No breakpint for this url and line requested. Allocate new one.
+ bpt = new SourceBreakpoint(nextId(), url, line);
+ bpt->set_next(latent_breakpoints_);
+ latent_breakpoints_ = bpt;
+ return bpt;
+}
+
+
void Debugger::RegisterSourceBreakpoint(SourceBreakpoint* bpt) {
ASSERT(bpt->next() == NULL);
bpt->set_next(src_breakpoints_);
« no previous file with comments | « runtime/vm/debugger.h ('k') | runtime/vm/debugger_api_impl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698