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

Unified Diff: dart/runtime/vm/debugger.cc

Issue 119673004: Version 1.1.0-dev.5.2 (Closed) Base URL: http://dart.googlecode.com/svn/trunk/
Patch Set: Created 6 years, 11 months 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 | « dart/runtime/vm/debugger.h ('k') | dart/runtime/vm/debugger_api_impl_test.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: dart/runtime/vm/debugger.cc
===================================================================
--- dart/runtime/vm/debugger.cc (revision 31530)
+++ dart/runtime/vm/debugger.cc (working copy)
@@ -50,17 +50,19 @@
SourceBreakpoint::SourceBreakpoint(intptr_t id,
- const Function& func,
+ const Script& script,
intptr_t token_pos)
: id_(id),
- function_(func.raw()),
+ script_(script.raw()),
token_pos_(token_pos),
- line_number_(-1),
+ is_resolved_(false),
is_enabled_(false),
- next_(NULL) {
- ASSERT(!func.IsNull());
- ASSERT((func.token_pos() <= token_pos_) &&
- (token_pos_ <= func.end_token_pos()));
+ next_(NULL),
+ function_(Function::null()),
+ line_number_(-1) {
+ ASSERT(id_ > 0);
+ ASSERT(!script.IsNull());
+ ASSERT(token_pos_ >= 0);
}
@@ -76,51 +78,59 @@
}
-RawScript* SourceBreakpoint::SourceCode() {
- const Function& func = Function::Handle(function_);
- return func.script();
+void SourceBreakpoint::SetResolved(const Function& func, intptr_t token_pos) {
+ ASSERT(func.script() == script_);
+ ASSERT((func.token_pos() <= token_pos) &&
+ (token_pos <= func.end_token_pos()));
+ function_ = func.raw();
+ token_pos_ = token_pos;
+ line_number_ = -1; // Recalcualte lazily.
+ is_resolved_ = true;
}
+// TODO(hausner): Get rid of library parameter. A source breakpoint location
+// does not imply a library, since the same source code can be included
+// in more than one library, e.g. the text location of mixin functions.
void SourceBreakpoint::GetCodeLocation(
Library* lib,
Script* script,
- intptr_t* pos) const {
- const Function& func = Function::Handle(function_);
- const Class& cls = Class::Handle(func.origin());
- *lib = cls.library();
- *script = func.script();
- *pos = token_pos();
+ 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();
+ } else {
+ *lib = Library::null();
+ }
}
RawString* SourceBreakpoint::SourceUrl() {
- const Script& script = Script::Handle(SourceCode());
- return script.url();
+ return Script::Handle(script()).url();
}
intptr_t SourceBreakpoint::LineNumber() {
// Compute line number lazily since it causes scanning of the script.
if (line_number_ < 0) {
- const Script& script = Script::Handle(SourceCode());
+ const Script& script = Script::Handle(this->script());
script.GetTokenLocation(token_pos_, &line_number_, NULL);
}
return line_number_;
}
-void SourceBreakpoint::set_function(const Function& func) {
- function_ = func.raw();
-}
-
-
void SourceBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
+ visitor->VisitPointer(reinterpret_cast<RawObject**>(&script_));
visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_));
}
-void SourceBreakpoint::PrintToJSONStream(JSONStream* stream) const {
+void SourceBreakpoint::PrintToJSONStream(JSONStream* stream) {
Isolate* isolate = Isolate::Current();
JSONObject jsobj(stream);
@@ -128,10 +138,8 @@
jsobj.AddProperty("id", id());
jsobj.AddProperty("enabled", IsEnabled());
+ jsobj.AddProperty("resolved", IsResolved());
- const Function& func = Function::Handle(function());
- jsobj.AddProperty("resolved", func.HasCode());
-
Library& library = Library::Handle(isolate);
Script& script = Script::Handle(isolate);
intptr_t token_pos;
@@ -139,7 +147,6 @@
{
JSONObject location(&jsobj, "location");
location.AddProperty("type", "Location");
- location.AddProperty("libId", library.index());
const String& url = String::Handle(script.url());
location.AddProperty("script", url.ToCString());
@@ -189,12 +196,9 @@
DebuggerStackTrace* stack_trace = debugger->CollectStackTrace();
ASSERT(stack_trace->Length() > 0);
ASSERT(debugger->stack_trace_ == NULL);
- ASSERT(debugger->obj_cache_ == NULL);
- debugger->obj_cache_ = new RemoteObjectCache(64);
debugger->stack_trace_ = stack_trace;
- (*event_handler_)(&event);
+ debugger->Pause(&event);
debugger->stack_trace_ = NULL;
- debugger->obj_cache_ = NULL; // Remote object cache is zone allocated.
// TODO(asiva): Need some work here to be able to single step after
// an interrupt.
} else {
@@ -224,13 +228,30 @@
}
+// Returns true if function contains the token position in the given script.
+static bool FunctionContains(const Function& func,
+ const Script& script,
+ intptr_t token_pos) {
+ if ((func.token_pos() <= token_pos) && (token_pos <= func.end_token_pos())) {
+ // Check script equality second because it allocates
+ // handles as a side effect.
+ return func.script() == script.raw();
+ }
+ return false;
+}
+
+
bool Debugger::HasBreakpoint(const Function& func) {
if (!func.HasCode()) {
// If the function is not compiled yet, just check whether there
- // is a user-defined latent breakpoint.
+ // is a user-defined breakpoint that falls into the token
+ // range of the function. This may be a false positive: the breakpoint
+ // might be inside a local closure.
+ Script& script = Script::Handle(isolate_);
SourceBreakpoint* sbpt = src_breakpoints_;
while (sbpt != NULL) {
- if (func.raw() == sbpt->function()) {
+ script = sbpt->script();
+ if (FunctionContains(func, script, sbpt->token_pos())) {
return true;
}
sbpt = sbpt->next_;
@@ -371,11 +392,6 @@
return context_level_;
}
ASSERT(!pc_desc_.IsNull());
- if (pc_desc_.DescriptorKind(pc_desc_idx) == PcDescriptors::kReturn) {
- // Special case: the context chain has already been deallocated.
- // The context level is 0.
- return context_level_;
- }
intptr_t innermost_begin_pos = 0;
intptr_t activation_token_pos = TokenPos();
ASSERT(activation_token_pos >= 0);
@@ -580,9 +596,11 @@
RawContext* ActivationFrame::GetLocalContextVar(intptr_t slot_index) {
- Context& context = Context::Handle();
- context ^= GetLocalVar(slot_index);
- return context.raw();
+ Object& context = Object::Handle(GetLocalVar(slot_index));
+ if (context.IsContext()) {
+ return Context::Cast(context).raw();
+ }
+ return Context::null();
}
@@ -762,7 +780,8 @@
break;
}
case PcDescriptors::kRuntimeCall:
- case PcDescriptors::kClosureCall: {
+ case PcDescriptors::kClosureCall:
+ case PcDescriptors::kReturn: {
const Code& code =
Code::Handle(Function::Handle(function_).unoptimized_code());
saved_bytes_.target_address_ =
@@ -771,9 +790,6 @@
StubCode::BreakpointRuntimeEntryPoint());
break;
}
- case PcDescriptors::kReturn:
- PatchFunctionReturn();
- break;
default:
UNREACHABLE();
}
@@ -793,16 +809,14 @@
}
case PcDescriptors::kUnoptStaticCall:
case PcDescriptors::kClosureCall:
- case PcDescriptors::kRuntimeCall: {
+ case PcDescriptors::kRuntimeCall:
+ case PcDescriptors::kReturn: {
const Code& code =
Code::Handle(Function::Handle(function_).unoptimized_code());
CodePatcher::PatchStaticCallAt(pc_, code,
saved_bytes_.target_address_);
break;
}
- case PcDescriptors::kReturn:
- RestoreFunctionReturn();
- break;
default:
UNREACHABLE();
}
@@ -855,20 +869,20 @@
isolate_id_(ILLEGAL_ISOLATE_ID),
initialized_(false),
next_id_(1),
- stack_trace_(NULL),
- obj_cache_(NULL),
src_breakpoints_(NULL),
code_breakpoints_(NULL),
resume_action_(kContinue),
ignore_breakpoints_(false),
- in_event_notification_(false),
+ pause_event_(NULL),
+ obj_cache_(NULL),
+ stack_trace_(NULL),
exc_pause_info_(kNoPauseOnExceptions) {
}
Debugger::~Debugger() {
isolate_id_ = ILLEGAL_ISOLATE_ID;
- ASSERT(!in_event_notification_);
+ ASSERT(!IsPaused());
ASSERT(src_breakpoints_ == NULL);
ASSERT(code_breakpoints_ == NULL);
ASSERT(stack_trace_ == NULL);
@@ -1270,7 +1284,7 @@
// breakpoint or exception event, or if the debugger is not
// interested in exception events.
if (ignore_breakpoints_ ||
- in_event_notification_ ||
+ IsPaused() ||
(event_handler_ == NULL) ||
(exc_pause_info_ == kNoPauseOnExceptions)) {
return;
@@ -1279,68 +1293,56 @@
if (!ShouldPauseOnException(stack_trace, exc)) {
return;
}
+ DebuggerEvent event(kExceptionThrown);
+ event.exception = &exc;
ASSERT(stack_trace_ == NULL);
stack_trace_ = stack_trace;
- ASSERT(obj_cache_ == NULL);
- in_event_notification_ = true;
- obj_cache_ = new RemoteObjectCache(64);
- DebuggerEvent event(kExceptionThrown);
- event.exception = &exc;
- (*event_handler_)(&event);
- in_event_notification_ = false;
+ Pause(&event);
stack_trace_ = NULL;
- obj_cache_ = NULL; // Remote object cache is zone allocated.
}
-// Given a function and a token position range, return the best fit
-// token position to set a breakpoint.
-// If multiple possible breakpoint positions are within the given range,
-// the one with the lowest machine code address is picked.
-// If no possible breakpoint location exists in the given range, the closest
-// token position after the range is returned.
+// Given a function and a token position, return the best fit
+// token position to set a breakpoint. The best fit is the safe point
+// with the lowest compiled code address that follows the requsted
+// token position.
intptr_t Debugger::ResolveBreakpointPos(const Function& func,
- intptr_t first_token_pos,
- intptr_t last_token_pos) {
+ intptr_t requested_token_pos) {
ASSERT(func.HasCode());
ASSERT(!func.HasOptimizedCode());
Code& code = Code::Handle(func.unoptimized_code());
ASSERT(!code.IsNull());
PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
intptr_t best_fit_index = -1;
- intptr_t best_fit = INT_MAX;
+ intptr_t best_fit_pos = INT_MAX;
uword lowest_pc = kUwordMax;
intptr_t lowest_pc_index = -1;
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) {
- // This descriptor is before the given range.
+ if (desc_token_pos < requested_token_pos) {
+ // This descriptor is before the first acceptable token position.
continue;
}
if (IsSafePoint(desc.DescriptorKind(i))) {
- if ((desc_token_pos - first_token_pos) < best_fit) {
- // So far, this descriptor has the closest token position to the
- // beginning of the range.
- best_fit = desc_token_pos - first_token_pos;
- ASSERT(best_fit >= 0);
+ if (desc_token_pos < best_fit_pos) {
+ // So far, this descriptor has the lowest token position after
+ // the first acceptable token position.
+ best_fit_pos = desc_token_pos;
best_fit_index = i;
}
- if ((first_token_pos <= desc_token_pos) &&
- (desc_token_pos <= last_token_pos) &&
- (desc.PC(i) < lowest_pc)) {
- // This descriptor is within the token position range and so
- // far has the lowest code address.
+ if (desc.PC(i) < lowest_pc) {
+ // This descriptor so far has the lowest code address.
lowest_pc = desc.PC(i);
lowest_pc_index = i;
}
}
}
if (lowest_pc_index >= 0) {
- // We found the the pc descriptor within the given token range that
- // has the lowest execution address. This is the first possible
- // breakpoint on the line. We use this instead of the nearest
- // PC descriptor measured in token index distance.
+ // We found the pc descriptor that has the lowest execution address.
+ // This is the first possible breakpoint after the requested token
+ // position. We use this instead of the nearest PC descriptor
+ // measured in token index distance.
best_fit_index = lowest_pc_index;
}
if (best_fit_index >= 0) {
@@ -1351,15 +1353,16 @@
void Debugger::MakeCodeBreakpointsAt(const Function& func,
- intptr_t token_pos,
SourceBreakpoint* bpt) {
+ ASSERT((bpt != NULL) && bpt->IsResolved());
ASSERT(!func.HasOptimizedCode());
Code& code = Code::Handle(func.unoptimized_code());
ASSERT(!code.IsNull());
PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
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))) {
+ if ((desc_token_pos == bpt->token_pos_) &&
+ IsSafePoint(desc.DescriptorKind(i))) {
CodeBreakpoint* code_bpt = GetCodeBreakpoint(desc.PC(i));
if (code_bpt == NULL) {
// No code breakpoint for this code exists; create one.
@@ -1367,83 +1370,222 @@
RegisterCodeBreakpoint(code_bpt);
}
code_bpt->set_src_bpt(bpt);
+ if (bpt->IsEnabled()) {
+ code_bpt->Enable();
+ }
}
}
}
-SourceBreakpoint* Debugger::SetBreakpoint(const Function& target_function,
- intptr_t first_token_pos,
- intptr_t last_token_pos) {
- if ((last_token_pos < target_function.token_pos()) ||
- (target_function.end_token_pos() < first_token_pos)) {
- // The given token position is not within the target function.
- return NULL;
+void Debugger::FindEquivalentFunctions(const Script& script,
+ intptr_t start_pos,
+ intptr_t end_pos,
+ GrowableObjectArray* function_list) {
+ Class& cls = Class::Handle(isolate_);
+ Array& functions = Array::Handle(isolate_);
+ GrowableObjectArray& closures = GrowableObjectArray::Handle(isolate_);
+ Function& function = Function::Handle(isolate_);
+
+ const ClassTable& class_table = *isolate_->class_table();
+ const intptr_t num_classes = class_table.NumCids();
+ for (intptr_t i = 1; i < num_classes; i++) {
+ if (class_table.HasValidClassAt(i)) {
+ 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.
+ if (!cls.is_finalized()) {
+ continue;
+ }
+ // Note: we need to check the functions of this class even if
+ // the class is defined in a differenct 'script'. There could
+ // be mixin functions from the given script in this class.
+ functions = cls.functions();
+ if (!functions.IsNull()) {
+ const intptr_t num_functions = functions.Length();
+ for (intptr_t pos = 0; pos < num_functions; pos++) {
+ function ^= functions.At(pos);
+ ASSERT(!function.IsNull());
+ // Check token position first to avoid unnecessary calls
+ // to script() which allocates handles.
+ if ((function.token_pos() == start_pos)
+ && (function.end_token_pos() == end_pos)
+ && (function.script() == script.raw())) {
+ function_list->Add(function);
+ if (function.HasImplicitClosureFunction()) {
+ function = function.ImplicitClosureFunction();
+ function_list->Add(function);
+ }
+ }
+ }
+ }
+ closures = cls.closures();
+ if (!closures.IsNull()) {
+ const intptr_t num_closures = closures.Length();
+ for (intptr_t pos = 0; pos < num_closures; pos++) {
+ function ^= closures.At(pos);
+ ASSERT(!function.IsNull());
+ if ((function.token_pos() == start_pos)
+ && (function.end_token_pos() == end_pos)
+ && (function.script() == script.raw())) {
+ function_list->Add(function);
+ if (function.HasImplicitClosureFunction()) {
+ function = function.ImplicitClosureFunction();
+ function_list->Add(function);
+ }
+ }
+ }
+ }
+ }
}
- intptr_t breakpoint_pos = -1;
- Function& closure = Function::Handle(isolate_);
- if (target_function.HasImplicitClosureFunction()) {
- // There is a closurized version of this function.
- closure = target_function.ImplicitClosureFunction();
+}
+
+
+static void SelectBestFit(Function* best_fit, Function* func) {
+ if (best_fit->IsNull()) {
+ *best_fit = func->raw();
}
- // Determine actual breakpoint location if the function or an
- // implicit closure of the function has been compiled already.
- if (target_function.HasCode()) {
- DeoptimizeWorld();
- ASSERT(!target_function.HasOptimizedCode());
- breakpoint_pos =
- ResolveBreakpointPos(target_function, first_token_pos, last_token_pos);
- } else if (!closure.IsNull() && closure.HasCode()) {
- DeoptimizeWorld();
- ASSERT(!closure.HasOptimizedCode());
- breakpoint_pos =
- ResolveBreakpointPos(closure, first_token_pos, last_token_pos);
- } else {
- // This function has not been compiled yet. Set a pending
- // breakpoint to be resolved later.
- SourceBreakpoint* source_bpt =
- GetSourceBreakpoint(target_function, first_token_pos);
- if (source_bpt != NULL) {
- // A pending source breakpoint for this uncompiled location
- // already exists.
- if (FLAG_verbose_debug) {
- OS::Print("Pending breakpoint for uncompiled function"
- " '%s' at line %" Pd " already exists\n",
- target_function.ToFullyQualifiedCString(),
- source_bpt->LineNumber());
+ if (func->token_pos() > best_fit->token_pos()) {
+ if (func->end_token_pos() <= best_fit->end_token_pos()) {
+ // func is contained within best_fit. Select it even if it
+ // has not been compiled yet.
+ *best_fit = func->raw();
+ if (func->HasImplicitClosureFunction()) {
+ *func = func->ImplicitClosureFunction();
+ if (func->HasCode()) {
+ *best_fit = func->raw();
+ }
}
- return source_bpt;
}
- source_bpt =
- new SourceBreakpoint(nextId(), target_function, first_token_pos);
- RegisterSourceBreakpoint(source_bpt);
- if (FLAG_verbose_debug) {
- OS::Print("Registering pending breakpoint for "
- "uncompiled function '%s' at line %" Pd "\n",
- target_function.ToFullyQualifiedCString(),
- source_bpt->LineNumber());
+ } else if ((func->token_pos() == best_fit->token_pos())
+ && (func->end_token_pos() == best_fit->end_token_pos())
+ && func->HasCode()) {
+ // If func covers the same range, it is considered a better fit if
+ // it has been compiled.
+ *best_fit = func->raw();
+ }
+}
+
+
+RawFunction* Debugger::FindBestFit(const Script& script,
+ intptr_t token_pos) {
+ Class& cls = Class::Handle(isolate_);
+ Array& functions = Array::Handle(isolate_);
+ GrowableObjectArray& closures = GrowableObjectArray::Handle(isolate_);
+ Function& function = Function::Handle(isolate_);
+ Function& best_fit = Function::Handle(isolate_);
+
+ const ClassTable& class_table = *isolate_->class_table();
+ const intptr_t num_classes = class_table.NumCids();
+ for (intptr_t i = 1; i < num_classes; i++) {
+ if (class_table.HasValidClassAt(i)) {
+ cls = class_table.At(i);
+ // Note: if this class has been parsed and finalized already,
+ // we need to check the functions of this class even if
+ // it is defined in a differenct 'script'. There could
+ // be mixin functions from the given script in this class.
+ // However, if this class is not parsed yet (not finalized),
+ // we can ignore it and avoid the side effect of parsing it.
+ if ((cls.script() != script.raw()) && !cls.is_finalized()) {
+ continue;
+ }
+ // Parse class definition if not done yet.
+ cls.EnsureIsFinalized(isolate_);
+ functions = cls.functions();
+ if (!functions.IsNull()) {
+ const intptr_t num_functions = functions.Length();
+ for (intptr_t pos = 0; pos < num_functions; pos++) {
+ function ^= functions.At(pos);
+ ASSERT(!function.IsNull());
+ if (FunctionContains(function, script, token_pos)) {
+ SelectBestFit(&best_fit, &function);
+ }
+ }
+ }
+
+ closures = cls.closures();
+ if (!closures.IsNull()) {
+ const intptr_t num_closures = closures.Length();
+ for (intptr_t pos = 0; pos < num_closures; pos++) {
+ function ^= closures.At(pos);
+ ASSERT(!function.IsNull());
+ if (FunctionContains(function, script, token_pos)) {
+ SelectBestFit(&best_fit, &function);
+ }
+ }
+ }
}
- source_bpt->Enable();
- return source_bpt;
}
- ASSERT(breakpoint_pos != -1);
- SourceBreakpoint* source_bpt =
- GetSourceBreakpoint(target_function, breakpoint_pos);
- if (source_bpt != NULL) {
- // A source breakpoint for this location already exists.
- return source_bpt;
+ return best_fit.raw();
+}
+
+
+SourceBreakpoint* Debugger::SetBreakpoint(const Script& script,
+ intptr_t token_pos) {
+ Function& func = Function::Handle(isolate_);
+ func = FindBestFit(script, token_pos);
+ if (func.IsNull()) {
+ return NULL;
}
- source_bpt = new SourceBreakpoint(nextId(), target_function, breakpoint_pos);
- RegisterSourceBreakpoint(source_bpt);
- if (target_function.HasCode()) {
- MakeCodeBreakpointsAt(target_function, breakpoint_pos, source_bpt);
+ if (!func.IsNull() && func.HasCode()) {
+ // A function containing this breakpoint location has already
+ // been compiled. We can resolve the breakpoint now.
+ DeoptimizeWorld();
+ intptr_t breakpoint_pos = ResolveBreakpointPos(func, token_pos);
+ if (breakpoint_pos >= 0) {
+ SourceBreakpoint* bpt = GetSourceBreakpoint(script, breakpoint_pos);
+ if (bpt != NULL) {
+ // A source breakpoint for this location already exists.
+ return bpt;
+ }
+ bpt = new SourceBreakpoint(nextId(), script, token_pos);
+ bpt->SetResolved(func, breakpoint_pos);
+ RegisterSourceBreakpoint(bpt);
+ // There may be more than one function object for a given function
+ // in source code. There may be implicit closure functions, and
+ // there may be copies of mixin functions. Collect all functions whose
+ // source code range matches exactly the best fit function we
+ // found.
+ GrowableObjectArray& functions =
+ GrowableObjectArray::Handle(GrowableObjectArray::New());
+ FindEquivalentFunctions(script,
+ func.token_pos(),
+ func.end_token_pos(),
+ &functions);
+ const intptr_t num_functions = functions.Length();
+ // We must have found at least one function: func.
+ ASSERT(num_functions > 0);
+ // Create code breakpoints for all compiled functions we found.
+ for (intptr_t i = 0; i < num_functions; i++) {
+ func ^= functions.At(i);
+ if (func.HasCode()) {
+ MakeCodeBreakpointsAt(func, bpt);
+ }
+ }
+ bpt->Enable();
+ SignalBpResolved(bpt);
+ return bpt;
+ }
}
- if (!closure.IsNull() && closure.HasCode()) {
- MakeCodeBreakpointsAt(closure, breakpoint_pos, source_bpt);
+ // There is no compiled function at this token position.
+ // Register an unresolved breakpoint.
+ if (FLAG_verbose_debug && !func.IsNull()) {
+ intptr_t line_number;
+ script.GetTokenLocation(token_pos, &line_number, NULL);
+ OS::Print("Registering pending breakpoint for "
+ "uncompiled function '%s' at line %" Pd "\n",
+ func.ToFullyQualifiedCString(),
+ line_number);
}
- source_bpt->Enable();
- SignalBpResolved(source_bpt);
- return source_bpt;
+ SourceBreakpoint* bpt = GetSourceBreakpoint(script, token_pos);
+ if (bpt == NULL) {
+ bpt = new SourceBreakpoint(nextId(), script, token_pos);
+ }
+ RegisterSourceBreakpoint(bpt);
+ bpt->Enable();
+ return bpt;
}
@@ -1477,9 +1619,8 @@
SourceBreakpoint* Debugger::SetBreakpointAtEntry(
const Function& target_function) {
ASSERT(!target_function.IsNull());
- return SetBreakpoint(target_function,
- target_function.token_pos(),
- target_function.end_token_pos());
+ const Script& script = Script::Handle(target_function.script());
+ return SetBreakpoint(script, target_function.token_pos());
}
@@ -1513,33 +1654,25 @@
}
return NULL;
} else if (last_token_idx < 0) {
- // Line does not contain any tokens. first_token_index is the first
- // token after the given line. We check whether that token is
- // part of a function.
- last_token_idx = first_token_idx;
- }
-
- Function& func = Function::Handle(isolate_);
- while (first_token_idx <= last_token_idx) {
- func = lib.LookupFunctionInScript(script, first_token_idx);
- if (!func.IsNull()) {
- break;
- }
- first_token_idx++;
- }
- if (func.IsNull()) {
+ // Line does not contain any tokens.
if (FLAG_verbose_debug) {
OS::Print("No executable code at line %" Pd " in '%s'\n",
line_number, script_url.ToCString());
}
return NULL;
}
- if (last_token_idx < 0) {
- // The token at first_token_index is past the requested source line.
- // Set the breakpoint at the closest position after that line.
- last_token_idx = func.end_token_pos();
+
+ SourceBreakpoint* bpt = NULL;
+ ASSERT(first_token_idx <= last_token_idx);
+ while ((bpt == NULL) && (first_token_idx <= last_token_idx)) {
+ bpt = SetBreakpoint(script, first_token_idx);
+ first_token_idx++;
}
- return SetBreakpoint(func, first_token_idx, last_token_idx);
+ if ((bpt == NULL) && FLAG_verbose_debug) {
+ OS::Print("No executable code at line %" Pd " in '%s'\n",
+ line_number, script_url.ToCString());
+ }
+ return bpt;
}
@@ -1570,11 +1703,10 @@
ASSERT(!getter_func.IsNull());
Object& result = Object::Handle();
- LongJump* base = isolate_->long_jump_base();
- LongJump jump;
- isolate_->set_long_jump_base(&jump);
bool saved_ignore_flag = ignore_breakpoints_;
ignore_breakpoints_ = true;
+
+ LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
const Array& args = Array::Handle(Array::New(1));
args.SetAt(0, object);
@@ -1583,7 +1715,6 @@
result = isolate_->object_store()->sticky_error();
}
ignore_breakpoints_ = saved_ignore_flag;
- isolate_->set_long_jump_base(base);
return result.raw();
}
@@ -1609,18 +1740,15 @@
}
Object& result = Object::Handle();
- LongJump* base = isolate_->long_jump_base();
- LongJump jump;
- isolate_->set_long_jump_base(&jump);
bool saved_ignore_flag = ignore_breakpoints_;
ignore_breakpoints_ = true;
+ LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
result = DartEntry::InvokeFunction(getter_func, Object::empty_array());
} else {
result = isolate_->object_store()->sticky_error();
}
ignore_breakpoints_ = saved_ignore_flag;
- isolate_->set_long_jump_base(base);
return result.raw();
}
@@ -1759,6 +1887,20 @@
}
+void Debugger::Pause(DebuggerEvent* event) {
+ ASSERT(!IsPaused()); // No recursive pausing.
+ ASSERT(obj_cache_ == NULL);
+
+ pause_event_ = event;
+ obj_cache_ = new RemoteObjectCache(64);
+
+ (*event_handler_)(event);
+
+ pause_event_ = NULL;
+ obj_cache_ = NULL; // Zone allocated
+}
+
+
bool Debugger::IsDebuggable(const Function& func) {
RawFunction::Kind fkind = func.kind();
if ((fkind == RawFunction::kImplicitGetter) ||
@@ -1780,16 +1922,12 @@
SourceBreakpoint* bpt) {
resume_action_ = kContinue;
isolate_->set_single_step(false);
- ASSERT(!in_event_notification_);
+ ASSERT(!IsPaused());
ASSERT(obj_cache_ == NULL);
- in_event_notification_ = true;
- obj_cache_ = new RemoteObjectCache(64);
DebuggerEvent event(kBreakpointReached);
event.top_frame = top_frame;
event.breakpoint = bpt;
- (*event_handler_)(&event);
- in_event_notification_ = false;
- obj_cache_ = NULL; // Remote object cache is zone allocated.
+ Pause(&event);
}
@@ -1800,7 +1938,7 @@
// single stepping.
ASSERT(event_handler_ != NULL);
// Don't pause recursively.
- if (in_event_notification_) return;
+ if (IsPaused()) return;
// Check whether we are in a Dart function that the user is
// interested in.
@@ -1822,6 +1960,7 @@
frame->TokenPos());
}
+ ASSERT(stack_trace_ == NULL);
stack_trace_ = CollectStackTrace();
SignalPausedEvent(frame, NULL);
@@ -1842,7 +1981,7 @@
// We ignore this breakpoint when the VM is executing code invoked
// by the debugger to evaluate variables values, or when we see a nested
// breakpoint or exception event.
- if (ignore_breakpoints_ || in_event_notification_) {
+ if (ignore_breakpoints_ || IsPaused()) {
return;
}
DebuggerStackTrace* stack_trace = CollectStackTrace();
@@ -1868,20 +2007,20 @@
}
if (report_bp && (event_handler_ != NULL)) {
+ ASSERT(stack_trace_ == NULL);
stack_trace_ = stack_trace;
SignalPausedEvent(top_frame, bpt->src_bpt_);
stack_trace_ = NULL;
}
Function& func_to_instrument = Function::Handle();
+ if ((resume_action_ == kStepOver) &&
+ (bpt->breakpoint_kind_ == PcDescriptors::kReturn)) {
+ resume_action_ = kStepOut;
+ }
if (resume_action_ == kStepOver) {
- if (bpt->breakpoint_kind_ == PcDescriptors::kReturn) {
- // Step over return is converted into a single step so we break at
- // the caller.
- SetSingleStep();
- } else {
- func_to_instrument = bpt->function();
- }
+ ASSERT(bpt->breakpoint_kind_ != PcDescriptors::kReturn);
+ func_to_instrument = bpt->function();
} else if (resume_action_ == kStepOut) {
if (stack_trace->Length() > 1) {
ActivationFrame* caller_frame = stack_trace->FrameAt(1);
@@ -1919,37 +2058,35 @@
}
-static RawFunction* GetOriginalFunction(const Function& func) {
- if (func.IsClosureFunction()) {
- // Local functions (closures) in mixin functions do not have
- // an original function they were cloned from.
- // Note: this is a problem when a breakpoint is set in a mixed-in
- // closure. The breakpoint is linked to the closure attached to the
- // mixin application class, not the mixin class. When the same
- // closure is compiled for another mixin application class, we
- // don't find the breakpoint since we'll be looking in the
- // mixin class.
- return func.raw();
+// Return innermost closure contained in 'function' that contains
+// the given token position.
+RawFunction* Debugger::FindInnermostClosure(const Function& function,
+ intptr_t token_pos) {
+ const Class& owner = Class::Handle(isolate_, function.Owner());
+ if (owner.closures() == GrowableObjectArray::null()) {
+ return Function::null();
}
- const Class& origin_class = Class::Handle(func.origin());
- if (origin_class.is_patch()) {
- // Patched functions from patch classes are removed from the
- // function array of the patch class, so we will not find an
- // original function object.
- return func.raw();
- }
- const Array& functions = Array::Handle(origin_class.functions());
- Object& orig_func = Object::Handle();
- for (intptr_t i = 0; i < functions.Length(); i++) {
- orig_func = functions.At(i);
- // Function names are symbols, so we can compare the raw pointers.
- if (func.name() == Function::Cast(orig_func).name()) {
- return Function::Cast(orig_func).raw();
+ // Note that we need to check that the closure is in the same
+ // script as the outer function. We could have closures originating
+ // in mixin classes whose source code is contained in a different
+ // script.
+ const Script& outer_origin = Script::Handle(isolate_, function.script());
+ const GrowableObjectArray& closures =
+ GrowableObjectArray::Handle(isolate_, owner.closures());
+ const intptr_t num_closures = closures.Length();
+ Function& closure = Function::Handle(isolate_);
+ Function& best_fit = Function::Handle(isolate_);
+ for (intptr_t i = 0; i < num_closures; i++) {
+ closure ^= closures.At(i);
+ if ((function.token_pos() < closure.token_pos()) &&
+ (closure.end_token_pos() < function.end_token_pos()) &&
+ (closure.token_pos() <= token_pos) &&
+ (token_pos <= closure.end_token_pos()) &&
+ (closure.script() == outer_origin.raw())) {
+ SelectBestFit(&best_fit, &closure);
}
}
- // Uncommon case: not a mixin function.
- ASSERT(!Class::Handle(func.Owner()).IsMixinApplication());
- return func.raw();
+ return best_fit.raw();
}
@@ -1958,56 +2095,67 @@
// Return with minimal overhead if there are no breakpoints.
return;
}
- Function& lookup_function = Function::Handle(func.raw());
- if (func.IsImplicitClosureFunction()) {
- // If the newly compiled function is a an implicit closure (a closure that
- // was formed by assigning a static or instance method to a function
- // object), we need to use the closure's parent function to see whether
- // there are any breakpoints. The parent function is the actual method on
- // which the user sets breakpoints.
- lookup_function = func.parent_function();
- ASSERT(!lookup_function.IsNull());
- }
- if (lookup_function.Owner() != lookup_function.origin()) {
- // This is a cloned function from a mixin class. If a breakpoint
- // was set in this function, it is registered using the function
- // of the origin class.
- lookup_function = GetOriginalFunction(lookup_function);
- }
- SourceBreakpoint* bpt = src_breakpoints_;
- while (bpt != NULL) {
- if (lookup_function.raw() == bpt->function()) {
- // Check if the breakpoint is inside a closure or local function
- // within the newly compiled function. Note that we must look
- // in the owner class of the function that just got compiled
- // (i.e. func), not the owner class of the function we use to
- // record the breakpoint (lookup_function).
- Class& owner = Class::Handle(func.Owner());
- Function& closure =
- Function::Handle(owner.LookupClosureFunction(bpt->token_pos()));
- if (!closure.IsNull() && (closure.raw() != lookup_function.raw())) {
+ // Iterate over all source breakpoints to check whether breakpoints
+ // need to be set in the newly compiled function.
+ Script& script = Script::Handle(isolate_);
+ for (SourceBreakpoint* bpt = src_breakpoints_;
+ bpt != NULL;
+ bpt = bpt->next()) {
+ script = bpt->script();
+ if (FunctionContains(func, script, bpt->token_pos())) {
+ Function& inner_function = Function::Handle(isolate_);
+ inner_function = FindInnermostClosure(func, bpt->token_pos());
+ if (!inner_function.IsNull()) {
+ // The local function of a function we just compiled cannot
+ // be compiled already.
+ ASSERT(!inner_function.HasCode());
if (FLAG_verbose_debug) {
- OS::Print("Resetting pending breakpoint to function %s\n",
- closure.ToFullyQualifiedCString());
+ OS::Print("Pending BP remains unresolved in inner function '%s'\n",
+ inner_function.ToFullyQualifiedCString());
}
- bpt->set_function(closure);
- } else {
+ continue;
+ }
+
+ // TODO(hausner): What should we do if function is optimized?
+ // Can we deoptimize the function?
+ ASSERT(!func.HasOptimizedCode());
+
+ // There is no local function within func that contains the
+ // breakpoint token position. Resolve the breakpoint if necessary
+ // and set the code breakpoints.
+ if (!bpt->IsResolved()) {
+ // Resolve source breakpoint in the newly compiled function.
+ intptr_t bp_pos = ResolveBreakpointPos(func, bpt->token_pos());
+ if (bp_pos < 0) {
+ if (FLAG_verbose_debug) {
+ OS::Print("Failed resolving breakpoint for function '%s'\n",
+ String::Handle(func.name()).ToCString());
+ }
+ continue;
+ }
+ intptr_t requested_pos = bpt->token_pos();
+ bpt->SetResolved(func, bp_pos);
if (FLAG_verbose_debug) {
- OS::Print("Enable pending breakpoint for function '%s'\n",
- String::Handle(lookup_function.name()).ToCString());
+ OS::Print("Resolved BP %" Pd " to pos %" Pd ", line %" Pd ", "
+ "function '%s' (requested pos %" Pd ")\n",
+ bpt->id(),
+ bpt->token_pos(),
+ bpt->LineNumber(),
+ func.ToFullyQualifiedCString(),
+ requested_pos);
}
- const Script& script= Script::Handle(func.script());
- intptr_t first_pos, last_pos;
- script.TokenRangeAtLine(bpt->LineNumber(), &first_pos, &last_pos);
- intptr_t bp_pos =
- ResolveBreakpointPos(func, bpt->token_pos(), last_pos);
- bpt->set_token_pos(bp_pos);
- MakeCodeBreakpointsAt(func, bp_pos, bpt);
SignalBpResolved(bpt);
}
- bpt->Enable(); // Enables the code breakpoint as well.
+ ASSERT(bpt->IsResolved());
+ if (FLAG_verbose_debug) {
+ OS::Print("Setting breakpoint %" Pd " at line %" Pd " for %s '%s'\n",
+ bpt->id(),
+ bpt->LineNumber(),
+ func.IsClosureFunction() ? "closure" : "function",
+ String::Handle(func.name()).ToCString());
+ }
+ MakeCodeBreakpointsAt(func, bpt);
}
- bpt = bpt->next();
}
}
@@ -2101,12 +2249,11 @@
}
-SourceBreakpoint* Debugger::GetSourceBreakpoint(const Function& func,
+SourceBreakpoint* Debugger::GetSourceBreakpoint(const Script& script,
intptr_t token_pos) {
SourceBreakpoint* bpt = src_breakpoints_;
while (bpt != NULL) {
- if ((bpt->function() == func.raw()) &&
- (bpt->token_pos() == token_pos)) {
+ if ((bpt->script_ == script.raw()) && (bpt->token_pos_ == token_pos)) {
return bpt;
}
bpt = bpt->next();
« no previous file with comments | « dart/runtime/vm/debugger.h ('k') | dart/runtime/vm/debugger_api_impl_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698