Chromium Code Reviews| Index: runtime/vm/debugger.cc |
| diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc |
| index 0cd16d852896199b9843d5d2687776c0c9e4dfb1..b3db87e350fee9a1d2f4f352eeb09229fb148364 100644 |
| --- a/runtime/vm/debugger.cc |
| +++ b/runtime/vm/debugger.cc |
| @@ -65,7 +65,8 @@ class RemoteObjectCache : public ZoneAllocated { |
| // Create an unresolved breakpoint in given token range and script. |
| BreakpointLocation::BreakpointLocation(const Script& script, |
| intptr_t token_pos, |
| - intptr_t end_token_pos) |
| + intptr_t end_token_pos, |
| + intptr_t requested_column_number) |
| : script_(script.raw()), |
| url_(script.url()), |
| token_pos_(token_pos), |
| @@ -73,15 +74,19 @@ BreakpointLocation::BreakpointLocation(const Script& script, |
| is_resolved_(false), |
| next_(NULL), |
| conditions_(NULL), |
| + requested_line_number_(-1), |
| + requested_column_number_(requested_column_number), |
| function_(Function::null()), |
| - line_number_(-1) { |
| + line_number_(-1), |
| + column_number_(-1) { |
| ASSERT(!script.IsNull()); |
| ASSERT(token_pos_ >= 0); |
| } |
| // Create a latent breakpoint at given url and line number. |
| BreakpointLocation::BreakpointLocation(const String& url, |
| - intptr_t line_number) |
| + intptr_t requested_line_number, |
| + intptr_t requested_column_number) |
| : script_(Script::null()), |
| url_(url.raw()), |
| token_pos_(-1), |
| @@ -89,8 +94,11 @@ BreakpointLocation::BreakpointLocation(const String& url, |
| is_resolved_(false), |
| next_(NULL), |
| conditions_(NULL), |
| + requested_line_number_(requested_line_number), |
| + requested_column_number_(requested_column_number), |
| function_(Function::null()), |
| - line_number_(line_number) { |
| + line_number_(-1), |
| + column_number_(-1) { |
| ASSERT(line_number_ >= 0); |
|
hausner
2015/09/03 00:37:50
This assert would fire right away, since the field
turnidge
2015/09/04 18:05:29
Thanks. I have added a set of tests for latent br
|
| } |
| @@ -119,7 +127,6 @@ void BreakpointLocation::SetResolved(const Function& func, intptr_t token_pos) { |
| function_ = func.raw(); |
| token_pos_ = token_pos; |
| end_token_pos_ = token_pos; |
| - line_number_ = -1; // Recalculate lazily. |
|
hausner
2015/09/03 00:37:50
Why is it not necessary to recalculate the line nu
turnidge
2015/09/04 18:05:29
After my change, the line number before resolution
|
| is_resolved_ = true; |
| } |
| @@ -150,8 +157,7 @@ void BreakpointLocation::GetCodeLocation(Library* lib, |
| intptr_t BreakpointLocation::LineNumber() { |
| - // Latent breakpoints must have a requested line number >= 0. |
| - ASSERT(!IsLatent() || line_number_ >= 0); |
| + ASSERT(IsResolved()); |
| // Compute line number lazily since it causes scanning of the script. |
| if (line_number_ < 0) { |
| const Script& script = Script::Handle(this->script()); |
| @@ -161,6 +167,17 @@ intptr_t BreakpointLocation::LineNumber() { |
| } |
| +intptr_t BreakpointLocation::ColumnNumber() { |
| + ASSERT(IsResolved()); |
| + // Compute column number lazily since it causes scanning of the script. |
| + if (column_number_ < 0) { |
| + const Script& script = Script::Handle(this->script()); |
| + script.GetTokenLocation(token_pos_, &line_number_, &column_number_); |
| + } |
| + return column_number_; |
| +} |
| + |
| + |
| void Breakpoint::set_bpt_location(BreakpointLocation* new_bpt_location) { |
| ASSERT(bpt_location_->IsLatent()); // Only reason to move. |
| bpt_location_ = new_bpt_location; |
| @@ -1608,7 +1625,8 @@ static intptr_t LastTokenOnLine(const TokenStream& tokens, intptr_t pos) { |
| // that line, the safe point with the lowest compiled code address. |
|
hausner
2015/09/03 00:37:50
Update the comment to describe how the requested c
turnidge
2015/09/04 18:05:29
I updated this comment. It turns out that, given
|
| intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
| intptr_t requested_token_pos, |
| - intptr_t last_token_pos) { |
| + intptr_t last_token_pos, |
| + intptr_t requested_column) { |
| ASSERT(func.HasCode()); |
| ASSERT(!func.HasOptimizedCode()); |
| @@ -1619,6 +1637,7 @@ intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
| last_token_pos = func.end_token_pos(); |
| } |
| + Script& script = Script::Handle(func.script()); |
| Code& code = Code::Handle(func.unoptimized_code()); |
| ASSERT(!code.IsNull()); |
| PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
| @@ -1626,16 +1645,38 @@ intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
| // First pass: find the safe point which is closest to the beginning |
| // of the given token range. |
| intptr_t best_fit_pos = INT_MAX; |
| + intptr_t best_column = INT_MAX; |
| PcDescriptors::Iterator iter(desc, kSafepointKind); |
| while (iter.MoveNext()) { |
| - const intptr_t desc_token_pos = iter.TokenPos(); |
| - if ((desc_token_pos != Scanner::kNoSourcePos) && |
| - (desc_token_pos < best_fit_pos) && |
| - (desc_token_pos >= requested_token_pos) && |
| - (desc_token_pos <= last_token_pos)) { |
| - best_fit_pos = desc_token_pos; |
| + const intptr_t pos = iter.TokenPos(); |
| + if ((pos == Scanner::kNoSourcePos) || |
| + (pos < requested_token_pos) || |
| + (pos > last_token_pos)) { |
| + // Token is not in the target range. |
| + continue; |
| + } |
| + |
| + intptr_t token_start_column = -1; |
| + if (requested_column >= 0) { |
|
hausner
2015/09/03 00:37:50
Do I understand this code right:
If the caller re
turnidge
2015/09/04 18:05:29
There is a kind of logic to the current implementa
|
| + intptr_t ignored = -1; |
| + intptr_t token_len = -1; |
| + script.GetTokenLocation(pos, &ignored, &token_start_column, &token_len); |
|
hausner
2015/09/03 00:37:50
GetTokenLocation is a really expensive operation,
turnidge
2015/09/04 18:05:29
I agree. I have added a TODO/comment about this.
|
| + intptr_t token_end_column = token_start_column + token_len - 1; |
| + if ((token_end_column < requested_column) || |
| + (token_start_column > best_column)) { |
| + // Prefer the token with the lowest column number over the |
| + // requested column. |
| + continue; |
| + } |
| + } |
| + |
| + // Prefer the lowest (first) token pos. |
| + if (pos < best_fit_pos) { |
| + best_fit_pos = pos; |
| + best_column = token_start_column; |
| } |
| } |
| + |
| // Second pass (if we found a safe point in the first pass): |
| // For all token positions on the same line, select the one |
| // with the lowest compiled code address. E.g., in a line with |
| @@ -1651,9 +1692,24 @@ intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
| PcDescriptors::Iterator iter(desc, kSafepointKind); |
| while (iter.MoveNext()) { |
| const intptr_t pos = iter.TokenPos(); |
| - if ((pos != Scanner::kNoSourcePos) && |
| - (begin_pos <= pos) && (pos <= end_of_line_pos) && |
| - (iter.PcOffset() < lowest_pc_offset)) { |
| + if ((pos == Scanner::kNoSourcePos) || |
| + (pos < begin_pos) || |
| + (pos > end_of_line_pos)) { |
| + // Token is not on same line as best fit. |
| + continue; |
| + } |
| + |
| + if (requested_column >= 0) { |
| + intptr_t ignored = -1; |
| + intptr_t token_start_column = -1; |
| + script.GetTokenLocation(pos, &ignored, &token_start_column); |
| + if (token_start_column != best_column) { |
|
hausner
2015/09/03 00:37:50
Maybe comment that there may be more than one toke
turnidge
2015/09/04 18:05:29
Done.
|
| + continue; |
| + } |
| + } |
| + |
| + // Prefer the lowest pc offset. |
| + if (iter.PcOffset() < lowest_pc_offset) { |
| lowest_pc_offset = iter.PcOffset(); |
| best_fit_pos = pos; |
| } |
| @@ -1661,10 +1717,13 @@ intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
| return best_fit_pos; |
| } |
| - // We didn't find a safe point in the given token range. Try and find |
| - // a safe point in the remaining source code of the function. |
| + // We didn't find a safe point in the given token range. Try and |
| + // find a safe point in the remaining source code of the function. |
| + // Since we have moved to the next line of the function, we no |
| + // longer are requesting a specific column number. |
| if (last_token_pos < func.end_token_pos()) { |
| - return ResolveBreakpointPos(func, last_token_pos, func.end_token_pos()); |
| + return ResolveBreakpointPos(func, last_token_pos, func.end_token_pos(), |
| + -1 /* no column */); |
| } |
| return -1; |
| } |
| @@ -1857,7 +1916,8 @@ RawFunction* Debugger::FindBestFit(const Script& script, |
| BreakpointLocation* Debugger::SetBreakpoint(const Script& script, |
| intptr_t token_pos, |
| - intptr_t last_token_pos) { |
| + intptr_t last_token_pos, |
| + intptr_t requested_column) { |
| Function& func = Function::Handle(isolate_); |
| func = FindBestFit(script, token_pos); |
| if (func.IsNull()) { |
| @@ -1881,14 +1941,16 @@ BreakpointLocation* Debugger::SetBreakpoint(const Script& script, |
| DeoptimizeWorld(); |
| func ^= functions.At(0); |
| intptr_t breakpoint_pos = |
| - ResolveBreakpointPos(func, token_pos, last_token_pos); |
| + ResolveBreakpointPos(func, token_pos, last_token_pos, requested_column); |
| if (breakpoint_pos >= 0) { |
| - BreakpointLocation* bpt = GetBreakpointLocation(script, breakpoint_pos); |
| + BreakpointLocation* bpt = |
| + GetBreakpointLocation(script, breakpoint_pos, requested_column); |
| if (bpt != NULL) { |
| // A source breakpoint for this location already exists. |
| return bpt; |
| } |
| - bpt = new BreakpointLocation(script, token_pos, last_token_pos); |
| + bpt = new BreakpointLocation(script, token_pos, last_token_pos, |
| + requested_column); |
| bpt->SetResolved(func, breakpoint_pos); |
| RegisterBreakpointLocation(bpt); |
| @@ -1901,11 +1963,12 @@ BreakpointLocation* Debugger::SetBreakpoint(const Script& script, |
| } |
| if (FLAG_verbose_debug) { |
| intptr_t line_number; |
| - script.GetTokenLocation(breakpoint_pos, &line_number, NULL); |
| + intptr_t column_number; |
| + script.GetTokenLocation(breakpoint_pos, &line_number, &column_number); |
| OS::Print("Resolved BP for " |
| - "function '%s' at line %" Pd "\n", |
| + "function '%s' at line %" Pd " col %" Pd "\n", |
| func.ToFullyQualifiedCString(), |
| - line_number); |
| + line_number, column_number); |
| } |
| return bpt; |
| } |
| @@ -1914,15 +1977,18 @@ BreakpointLocation* Debugger::SetBreakpoint(const Script& script, |
| // Register an unresolved breakpoint. |
| if (FLAG_verbose_debug && !func.IsNull()) { |
| intptr_t line_number; |
| - script.GetTokenLocation(token_pos, &line_number, NULL); |
| + intptr_t column_number; |
| + script.GetTokenLocation(token_pos, &line_number, &column_number); |
| OS::Print("Registering pending breakpoint for " |
| - "uncompiled function '%s' at line %" Pd "\n", |
| + "uncompiled function '%s' at line %" Pd " col %" Pd "\n", |
| func.ToFullyQualifiedCString(), |
| - line_number); |
| + line_number, column_number); |
| } |
| - BreakpointLocation* bpt = GetBreakpointLocation(script, token_pos); |
| + BreakpointLocation* bpt = |
| + GetBreakpointLocation(script, token_pos, requested_column); |
| if (bpt == NULL) { |
| - bpt = new BreakpointLocation(script, token_pos, last_token_pos); |
| + bpt = new BreakpointLocation(script, token_pos, last_token_pos, |
| + requested_column); |
| RegisterBreakpointLocation(bpt); |
| } |
| return bpt; |
| @@ -1969,7 +2035,8 @@ Breakpoint* Debugger::SetBreakpointAtEntry(const Function& target_function, |
| BreakpointLocation* bpt_location = |
| SetBreakpoint(script, |
| target_function.token_pos(), |
| - target_function.end_token_pos()); |
| + target_function.end_token_pos(), |
| + -1 /* no column */); |
| if (single_shot) { |
| return bpt_location->AddSingleShot(this); |
| } else { |
| @@ -1986,7 +2053,8 @@ Breakpoint* Debugger::SetBreakpointAtActivation(const Instance& closure) { |
| const Script& script = Script::Handle(func.script()); |
| BreakpointLocation* bpt_location = SetBreakpoint(script, |
| func.token_pos(), |
| - func.end_token_pos()); |
| + func.end_token_pos(), |
| + -1 /* no column */); |
| return bpt_location->AddPerClosure(this, closure); |
| } |
| @@ -2016,7 +2084,21 @@ Breakpoint* Debugger::BreakpointAtActivation(const Instance& closure) { |
| Breakpoint* Debugger::SetBreakpointAtLine(const String& script_url, |
| intptr_t line_number) { |
| - BreakpointLocation* loc = BreakpointLocationAtLine(script_url, line_number); |
| + BreakpointLocation* loc = |
| + BreakpointLocationAtLineCol(script_url, line_number, -1 /* no column */); |
| + if (loc != NULL) { |
| + return loc->AddRepeated(this); |
| + } |
| + return NULL; |
| +} |
| + |
| + |
| +Breakpoint* Debugger::SetBreakpointAtLineCol(const String& script_url, |
| + intptr_t line_number, |
| + intptr_t column_number) { |
| + BreakpointLocation* loc = BreakpointLocationAtLineCol(script_url, |
| + line_number, |
| + column_number); |
| if (loc != NULL) { |
| return loc->AddRepeated(this); |
| } |
| @@ -2024,8 +2106,10 @@ Breakpoint* Debugger::SetBreakpointAtLine(const String& script_url, |
| } |
| -BreakpointLocation* Debugger::BreakpointLocationAtLine(const String& script_url, |
| - intptr_t line_number) { |
| +BreakpointLocation* Debugger::BreakpointLocationAtLineCol( |
| + const String& script_url, |
| + intptr_t line_number, |
| + intptr_t column_number) { |
| Library& lib = Library::Handle(isolate_); |
| Script& script = Script::Handle(isolate_); |
| const GrowableObjectArray& libs = |
| @@ -2043,11 +2127,13 @@ BreakpointLocation* Debugger::BreakpointLocationAtLine(const String& script_url, |
| // No script found with given url. Create a latent breakpoint which |
| // will be set if the url is loaded later. |
| BreakpointLocation* latent_bpt = GetLatentBreakpoint(script_url, |
| - line_number); |
| + line_number, |
| + column_number); |
| if (FLAG_verbose_debug) { |
| - OS::Print("Set latent breakpoint in url '%s' at line %" Pd "\n", |
| + OS::Print("Set latent breakpoint in url '%s' at " |
| + "line %" Pd " col %" Pd "\n", |
| script_url.ToCString(), |
| - line_number); |
| + line_number, column_number); |
| } |
| return latent_bpt; |
| } |
| @@ -2079,7 +2165,7 @@ BreakpointLocation* Debugger::BreakpointLocationAtLine(const String& script_url, |
| BreakpointLocation* bpt = NULL; |
| ASSERT(first_token_idx <= last_token_idx); |
| while ((bpt == NULL) && (first_token_idx <= last_token_idx)) { |
| - bpt = SetBreakpoint(script, first_token_idx, last_token_idx); |
| + bpt = SetBreakpoint(script, first_token_idx, last_token_idx, column_number); |
| first_token_idx++; |
| } |
| if ((bpt == NULL) && FLAG_verbose_debug) { |
| @@ -2680,7 +2766,8 @@ void Debugger::NotifyCompilation(const Function& func) { |
| if (!loc->IsResolved()) { |
| // Resolve source breakpoint in the newly compiled function. |
| intptr_t bp_pos = |
| - ResolveBreakpointPos(func, loc->token_pos(), loc->end_token_pos()); |
| + ResolveBreakpointPos(func, loc->token_pos(), loc->end_token_pos(), |
| + loc->requested_column_number()); |
| if (bp_pos < 0) { |
| if (FLAG_verbose_debug) { |
| OS::Print("Failed resolving breakpoint for function '%s'\n", |
| @@ -2694,14 +2781,18 @@ void Debugger::NotifyCompilation(const Function& func) { |
| Breakpoint* bpt = loc->breakpoints(); |
| while (bpt != NULL) { |
| if (FLAG_verbose_debug) { |
| - OS::Print("Resolved BP %" Pd " to pos %" Pd ", line %" Pd ", " |
| - "function '%s' (requested range %" Pd "-%" Pd ")\n", |
| + OS::Print("Resolved BP %" Pd " to pos %" Pd ", " |
| + "line %" Pd " col %" Pd ", " |
| + "function '%s' (requested range %" Pd "-%" Pd ", " |
| + "requested col %" Pd ")\n", |
| bpt->id(), |
| loc->token_pos(), |
| loc->LineNumber(), |
| + loc->ColumnNumber(), |
| func.ToFullyQualifiedCString(), |
| requested_pos, |
| - requested_end_pos); |
| + requested_end_pos, |
| + loc->requested_column_number()); |
| } |
| SignalBpResolved(bpt); |
| SendServiceBreakpointEvent(ServiceEvent::kBreakpointResolved, bpt); |
| @@ -2712,9 +2803,11 @@ void Debugger::NotifyCompilation(const Function& func) { |
| if (FLAG_verbose_debug) { |
| Breakpoint* bpt = loc->breakpoints(); |
| while (bpt != NULL) { |
| - OS::Print("Setting breakpoint %" Pd " at line %" Pd " for %s '%s'\n", |
| + OS::Print("Setting breakpoint %" Pd " at line %" Pd " col %" Pd "" |
| + " for %s '%s'\n", |
| bpt->id(), |
| loc->LineNumber(), |
| + loc->ColumnNumber(), |
| func.IsClosureFunction() ? "closure" : "function", |
| String::Handle(func.name()).ToCString()); |
| bpt = bpt->next(); |
| @@ -2757,7 +2850,8 @@ void Debugger::NotifyDoneLoading() { |
| } |
| // Now find the token range at the requested line and make a |
| // new unresolved source breakpoint. |
| - intptr_t line_number = matched_loc->LineNumber(); |
| + intptr_t line_number = matched_loc->requested_line_number(); |
| + intptr_t column_number = matched_loc->requested_column_number(); |
| ASSERT(line_number >= 0); |
| intptr_t first_token_pos, last_token_pos; |
| script.TokenRangeAtLine(line_number, &first_token_pos, &last_token_pos); |
| @@ -2784,7 +2878,7 @@ void Debugger::NotifyDoneLoading() { |
| // If there is one, assert in debug build but silently drop |
| // the latent breakpoint in release build. |
| BreakpointLocation* existing_loc = |
| - GetBreakpointLocation(script, first_token_pos); |
| + GetBreakpointLocation(script, first_token_pos, column_number); |
| ASSERT(existing_loc == NULL); |
| if (existing_loc == NULL) { |
| // Create and register a new source breakpoint for the |
| @@ -2792,7 +2886,8 @@ void Debugger::NotifyDoneLoading() { |
| BreakpointLocation* unresolved_loc = |
| new BreakpointLocation(script, |
| first_token_pos, |
| - last_token_pos); |
| + last_token_pos, |
| + column_number); |
| RegisterBreakpointLocation(unresolved_loc); |
| // Move breakpoints over. |
| @@ -2803,10 +2898,10 @@ void Debugger::NotifyDoneLoading() { |
| bpt->set_bpt_location(unresolved_loc); |
| if (FLAG_verbose_debug) { |
| OS::Print("Converted latent breakpoint " |
| - "%" Pd " in '%s' at line %" Pd "\n", |
| + "%" Pd " in '%s' at line %" Pd " col %" Pd "\n", |
| bpt->id(), |
| url.ToCString(), |
| - line_number); |
| + line_number, column_number); |
| } |
| bpt = bpt->next(); |
| } |
| @@ -2970,10 +3065,13 @@ void Debugger::RemoveInternalBreakpoints() { |
| BreakpointLocation* Debugger::GetBreakpointLocation(const Script& script, |
| - intptr_t token_pos) { |
| + intptr_t token_pos, |
| + intptr_t requested_column) { |
| BreakpointLocation* bpt = breakpoint_locations_; |
| while (bpt != NULL) { |
| - if ((bpt->script_ == script.raw()) && (bpt->token_pos_ == token_pos)) { |
| + if ((bpt->script_ == script.raw()) && |
| + (bpt->token_pos_ == token_pos) && |
| + (bpt->requested_column_number_ == requested_column)) { |
| return bpt; |
| } |
| bpt = bpt->next(); |
| @@ -2999,18 +3097,21 @@ Breakpoint* Debugger::GetBreakpointById(intptr_t id) { |
| BreakpointLocation* Debugger::GetLatentBreakpoint(const String& url, |
| - intptr_t line) { |
| + intptr_t line, |
| + intptr_t column) { |
| BreakpointLocation* bpt = latent_locations_; |
| String& bpt_url = String::Handle(isolate_); |
| while (bpt != NULL) { |
| bpt_url = bpt->url(); |
| - if (bpt_url.Equals(url) && (bpt->LineNumber() == line)) { |
| + if (bpt_url.Equals(url) && |
| + (bpt->requested_line_number() == line) && |
| + (bpt->requested_column_number() == column)) { |
| return bpt; |
| } |
| bpt = bpt->next(); |
| } |
| - // No breakpint for this url and line requested. Allocate new one. |
| - bpt = new BreakpointLocation(url, line); |
| + // No breakpoint for this location requested. Allocate new one. |
| + bpt = new BreakpointLocation(url, line, column); |
| bpt->set_next(latent_locations_); |
| latent_locations_ = bpt; |
| return bpt; |