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

Side by Side Diff: runtime/vm/debugger.cc

Issue 117133005: Change how debugger handles source breakpoints (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "vm/debugger.h" 5 #include "vm/debugger.h"
6 6
7 #include "include/dart_api.h" 7 #include "include/dart_api.h"
8 8
9 #include "vm/code_generator.h" 9 #include "vm/code_generator.h"
10 #include "vm/code_patcher.h" 10 #include "vm/code_patcher.h"
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
43 } 43 }
44 44
45 private: 45 private:
46 GrowableObjectArray* objs_; 46 GrowableObjectArray* objs_;
47 47
48 DISALLOW_COPY_AND_ASSIGN(RemoteObjectCache); 48 DISALLOW_COPY_AND_ASSIGN(RemoteObjectCache);
49 }; 49 };
50 50
51 51
52 SourceBreakpoint::SourceBreakpoint(intptr_t id, 52 SourceBreakpoint::SourceBreakpoint(intptr_t id,
53 const Function& func, 53 const Script& script,
54 intptr_t token_pos) 54 intptr_t range_start,
55 intptr_t range_end)
55 : id_(id), 56 : id_(id),
56 function_(func.raw()), 57 script_(script.raw()),
57 token_pos_(token_pos), 58 range_start_(range_start),
58 line_number_(-1), 59 range_end_(range_end),
59 is_enabled_(false), 60 is_enabled_(false),
60 next_(NULL) { 61 next_(NULL),
61 ASSERT(!func.IsNull()); 62 token_pos_(-1),
62 ASSERT((func.token_pos() <= token_pos_) && 63 line_number_(-1) {
63 (token_pos_ <= func.end_token_pos())); 64 ASSERT(id_ > 0);
65 ASSERT(!script.IsNull());
66 ASSERT((range_start_ >= 0) && (range_start_ <= range_end_));
turnidge 2013/12/19 22:31:59 Should this <= be a <? It depends whether the ran
hausner 2013/12/21 00:08:32 obsolete
64 } 67 }
65 68
66 69
67 void SourceBreakpoint::Enable() { 70 void SourceBreakpoint::Enable() {
68 is_enabled_ = true; 71 is_enabled_ = true;
69 Isolate::Current()->debugger()->SyncBreakpoint(this); 72 Isolate::Current()->debugger()->SyncBreakpoint(this);
70 } 73 }
71 74
72 75
73 void SourceBreakpoint::Disable() { 76 void SourceBreakpoint::Disable() {
74 is_enabled_ = false; 77 is_enabled_ = false;
75 Isolate::Current()->debugger()->SyncBreakpoint(this); 78 Isolate::Current()->debugger()->SyncBreakpoint(this);
76 } 79 }
77 80
78 81
79 RawScript* SourceBreakpoint::SourceCode() { 82 void SourceBreakpoint::SetResolved(const Function& func, intptr_t token_pos) {
turnidge 2013/12/19 22:31:59 What do you think of renaming "SetResolved" to "Re
hausner 2013/12/21 00:08:32 I like SetResolved better. The actual resolving (c
80 const Function& func = Function::Handle(function_); 83 ASSERT(func.script() == script_);
81 return func.script(); 84 function_ = func.raw();
85 token_pos_ = token_pos;
82 } 86 }
83 87
84 88
89 // TODO(hausner): Get rid of library parameter. A source breakpoint location
90 // does not imply a library, since the same source code can be included
91 // in more than one library, e.g. the text location of mixin functions.
85 void SourceBreakpoint::GetCodeLocation( 92 void SourceBreakpoint::GetCodeLocation(
86 Library* lib, 93 Library* lib,
87 Script* script, 94 Script* script,
88 intptr_t* pos) const { 95 intptr_t* pos) {
89 const Function& func = Function::Handle(function_); 96 *script = this->script();
90 const Class& cls = Class::Handle(func.origin()); 97 if (IsResolved()) {
91 *lib = cls.library(); 98 const Function& func = Function::Handle(function_);
92 *script = func.script(); 99 ASSERT(!func.IsNull());
93 *pos = token_pos(); 100 const Class& cls = Class::Handle(func.origin());
101 *lib = cls.library();
102 *pos = token_pos_;
103 } else {
104 *lib = Library::null();
105 *pos = range_start_;
106 }
94 } 107 }
95 108
96 109
97 RawString* SourceBreakpoint::SourceUrl() { 110 RawString* SourceBreakpoint::SourceUrl() {
98 const Script& script = Script::Handle(SourceCode()); 111 return Script::Handle(script()).url();
99 return script.url();
100 } 112 }
101 113
102 114
103 intptr_t SourceBreakpoint::LineNumber() { 115 intptr_t SourceBreakpoint::LineNumber() {
104 // Compute line number lazily since it causes scanning of the script. 116 // Compute line number lazily since it causes scanning of the script.
117 ASSERT(IsResolved());
105 if (line_number_ < 0) { 118 if (line_number_ < 0) {
106 const Script& script = Script::Handle(SourceCode()); 119 const Script& script = Script::Handle(this->script());
107 script.GetTokenLocation(token_pos_, &line_number_, NULL); 120 script.GetTokenLocation(token_pos_, &line_number_, NULL);
108 } 121 }
109 return line_number_; 122 return line_number_;
110 } 123 }
111 124
112 125
113 void SourceBreakpoint::set_function(const Function& func) {
114 function_ = func.raw();
115 }
116
117
118 void SourceBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { 126 void SourceBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
127 visitor->VisitPointer(reinterpret_cast<RawObject**>(&script_));
119 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); 128 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_));
120 } 129 }
121 130
122 131
123 void SourceBreakpoint::PrintToJSONStream(JSONStream* stream) const { 132 void SourceBreakpoint::PrintToJSONStream(JSONStream* stream) {
124 Isolate* isolate = Isolate::Current(); 133 Isolate* isolate = Isolate::Current();
125 134
126 JSONObject jsobj(stream); 135 JSONObject jsobj(stream);
127 jsobj.AddProperty("type", "Breakpoint"); 136 jsobj.AddProperty("type", "Breakpoint");
128 137
129 jsobj.AddProperty("id", id()); 138 jsobj.AddProperty("id", id());
130 jsobj.AddProperty("enabled", IsEnabled()); 139 jsobj.AddProperty("enabled", IsEnabled());
131 140 jsobj.AddProperty("resolved", IsResolved());
132 const Function& func = Function::Handle(function());
133 jsobj.AddProperty("resolved", func.HasCode());
134 141
135 Library& library = Library::Handle(isolate); 142 Library& library = Library::Handle(isolate);
136 Script& script = Script::Handle(isolate); 143 Script& script = Script::Handle(isolate);
137 intptr_t token_pos; 144 intptr_t token_pos;
138 GetCodeLocation(&library, &script, &token_pos); 145 GetCodeLocation(&library, &script, &token_pos);
139 { 146 {
140 JSONObject location(&jsobj, "location"); 147 JSONObject location(&jsobj, "location");
141 location.AddProperty("type", "Location"); 148 location.AddProperty("type", "Location");
142 location.AddProperty("libId", library.index());
143 149
144 const String& url = String::Handle(script.url()); 150 const String& url = String::Handle(script.url());
145 location.AddProperty("script", url.ToCString()); 151 location.AddProperty("script", url.ToCString());
146 location.AddProperty("tokenPos", token_pos); 152 location.AddProperty("tokenPos", token_pos);
147 } 153 }
148 } 154 }
149 155
150 156
151 void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { 157 void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
152 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); 158 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_));
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
223 return chars; 229 return chars;
224 } 230 }
225 231
226 232
227 bool Debugger::HasBreakpoint(const Function& func) { 233 bool Debugger::HasBreakpoint(const Function& func) {
228 if (!func.HasCode()) { 234 if (!func.HasCode()) {
229 // If the function is not compiled yet, just check whether there 235 // If the function is not compiled yet, just check whether there
230 // is a user-defined latent breakpoint. 236 // is a user-defined latent breakpoint.
231 SourceBreakpoint* sbpt = src_breakpoints_; 237 SourceBreakpoint* sbpt = src_breakpoints_;
232 while (sbpt != NULL) { 238 while (sbpt != NULL) {
233 if (func.raw() == sbpt->function()) { 239 if (sbpt->IsResolved() && (func.raw() == sbpt->function())) {
turnidge 2013/12/19 22:31:59 So this function has no code, has not been compile
hausner 2013/12/21 00:08:32 Yes that seems fishy. Changed so it returns true i
234 return true; 240 return true;
235 } 241 }
236 sbpt = sbpt->next_; 242 sbpt = sbpt->next_;
237 } 243 }
238 return false; 244 return false;
239 } 245 }
240 CodeBreakpoint* cbpt = code_breakpoints_; 246 CodeBreakpoint* cbpt = code_breakpoints_;
241 while (cbpt != NULL) { 247 while (cbpt != NULL) {
242 if (func.raw() == cbpt->function()) { 248 if (func.raw() == cbpt->function()) {
243 return true; 249 return true;
(...skipping 1086 matching lines...) Expand 10 before | Expand all | Expand 10 after
1330 (desc_token_pos <= last_token_pos) && 1336 (desc_token_pos <= last_token_pos) &&
1331 (desc.PC(i) < lowest_pc)) { 1337 (desc.PC(i) < lowest_pc)) {
1332 // This descriptor is within the token position range and so 1338 // This descriptor is within the token position range and so
1333 // far has the lowest code address. 1339 // far has the lowest code address.
1334 lowest_pc = desc.PC(i); 1340 lowest_pc = desc.PC(i);
1335 lowest_pc_index = i; 1341 lowest_pc_index = i;
1336 } 1342 }
1337 } 1343 }
1338 } 1344 }
1339 if (lowest_pc_index >= 0) { 1345 if (lowest_pc_index >= 0) {
1340 // We found the the pc descriptor within the given token range that 1346 // We found the pc descriptor within the given token range that
1341 // has the lowest execution address. This is the first possible 1347 // has the lowest execution address. This is the first possible
1342 // breakpoint on the line. We use this instead of the nearest 1348 // breakpoint on the line. We use this instead of the nearest
1343 // PC descriptor measured in token index distance. 1349 // PC descriptor measured in token index distance.
1344 best_fit_index = lowest_pc_index; 1350 best_fit_index = lowest_pc_index;
1345 } 1351 }
1346 if (best_fit_index >= 0) { 1352 if (best_fit_index >= 0) {
1347 return desc.TokenPos(best_fit_index); 1353 return desc.TokenPos(best_fit_index);
1348 } 1354 }
1349 return -1; 1355 return -1;
1350 } 1356 }
1351 1357
1352 1358
1359 void Debugger::FindAllFunctions(const Script& script,
turnidge 2013/12/19 22:31:59 I find the name confusing -- it seems to be trying
hausner 2013/12/21 00:08:32 Name changed.
1360 intptr_t start_pos,
1361 intptr_t end_pos,
1362 GrowableObjectArray* function_list) {
turnidge 2013/12/19 22:31:59 This function belongs lower in the file, nearer to
hausner 2013/12/21 00:08:32 Done.
1363 Class& cls = Class::Handle(isolate_);
1364 Array& functions = Array::Handle(isolate_);
1365 GrowableObjectArray& closures = GrowableObjectArray::Handle(isolate_);
1366 Function& function = Function::Handle(isolate_);
1367
1368 const ClassTable& class_table = *isolate_->class_table();
1369 const intptr_t num_classes = class_table.NumCids();
1370 for (intptr_t i = 1; i < num_classes; i++) {
1371 if (class_table.HasValidClassAt(i)) {
1372 cls = class_table.At(i);
1373 // Note: we need to check the functions of this class even if
1374 // the class is defined in a differenct 'script'. There could
1375 // be mixin functions from the given script in this class.
1376 functions = cls.functions();
1377 if (!functions.IsNull()) {
1378 const intptr_t num_functions = functions.Length();
1379 for (intptr_t pos = 0; pos < num_functions; pos++) {
1380 function ^= functions.At(pos);
1381 ASSERT(!function.IsNull());
1382 // Check token position first to avoid unnecessary calls
1383 // to script() which allocates handles.
1384 if ((function.token_pos() == start_pos)
1385 && (function.end_token_pos() == end_pos)
1386 && (function.script() == script.raw())) {
1387 function_list->Add(function);
1388 if (function.HasImplicitClosureFunction()) {
1389 function = function.ImplicitClosureFunction();
1390 function_list->Add(function);
1391 }
1392 }
1393 }
1394 }
1395
1396 closures = cls.closures();
1397 if (!closures.IsNull()) {
1398 const intptr_t num_closures = closures.Length();
1399 for (intptr_t pos = 0; pos < num_closures; pos++) {
1400 function ^= closures.At(pos);
1401 ASSERT(!function.IsNull());
1402 if ((function.token_pos() == start_pos)
1403 && (function.end_token_pos() == end_pos)
1404 && (function.script() == script.raw())) {
1405 function_list->Add(function);
1406 if (function.HasImplicitClosureFunction()) {
1407 function = function.ImplicitClosureFunction();
1408 function_list->Add(function);
1409 }
1410 }
1411 }
1412 }
1413 }
1414 }
1415 }
1416
1417
1353 void Debugger::MakeCodeBreakpointsAt(const Function& func, 1418 void Debugger::MakeCodeBreakpointsAt(const Function& func,
1354 intptr_t token_pos,
1355 SourceBreakpoint* bpt) { 1419 SourceBreakpoint* bpt) {
1420 ASSERT((bpt != NULL) && bpt->IsResolved());
1356 ASSERT(!func.HasOptimizedCode()); 1421 ASSERT(!func.HasOptimizedCode());
1357 Code& code = Code::Handle(func.unoptimized_code()); 1422 Code& code = Code::Handle(func.unoptimized_code());
1358 ASSERT(!code.IsNull()); 1423 ASSERT(!code.IsNull());
1359 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); 1424 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
1360 for (intptr_t i = 0; i < desc.Length(); i++) { 1425 for (intptr_t i = 0; i < desc.Length(); i++) {
1361 intptr_t desc_token_pos = desc.TokenPos(i); 1426 intptr_t desc_token_pos = desc.TokenPos(i);
1362 if ((desc_token_pos == token_pos) && IsSafePoint(desc.DescriptorKind(i))) { 1427 if ((desc_token_pos == bpt->token_pos_) &&
1428 IsSafePoint(desc.DescriptorKind(i))) {
1363 CodeBreakpoint* code_bpt = GetCodeBreakpoint(desc.PC(i)); 1429 CodeBreakpoint* code_bpt = GetCodeBreakpoint(desc.PC(i));
1364 if (code_bpt == NULL) { 1430 if (code_bpt == NULL) {
1365 // No code breakpoint for this code exists; create one. 1431 // No code breakpoint for this code exists; create one.
1366 code_bpt = new CodeBreakpoint(func, i); 1432 code_bpt = new CodeBreakpoint(func, i);
1367 RegisterCodeBreakpoint(code_bpt); 1433 RegisterCodeBreakpoint(code_bpt);
1368 } 1434 }
1369 code_bpt->set_src_bpt(bpt); 1435 code_bpt->set_src_bpt(bpt);
1436 if (bpt->IsEnabled()) {
1437 code_bpt->Enable();
1438 }
1370 } 1439 }
1371 } 1440 }
1372 } 1441 }
1373 1442
1374 1443
1375 SourceBreakpoint* Debugger::SetBreakpoint(const Function& target_function, 1444 static void SelectBestFit(Function* best_fit, Function* func) {
1376 intptr_t first_token_pos, 1445 if (best_fit->IsNull()) {
1377 intptr_t last_token_pos) { 1446 *best_fit = func->raw();
1378 if ((last_token_pos < target_function.token_pos()) ||
1379 (target_function.end_token_pos() < first_token_pos)) {
1380 // The given token position is not within the target function.
1381 return NULL;
1382 } 1447 }
1383 intptr_t breakpoint_pos = -1; 1448 if (func->token_pos() > best_fit->token_pos()) {
1384 Function& closure = Function::Handle(isolate_); 1449 if (func->end_token_pos() <= best_fit->end_token_pos()) {
1385 if (target_function.HasImplicitClosureFunction()) { 1450 // func is contained within best_fit. Select it even if it
1386 // There is a closurized version of this function. 1451 // has not been compiled yet.
1387 closure = target_function.ImplicitClosureFunction(); 1452 *best_fit = func->raw();
1453 if (func->HasImplicitClosureFunction()) {
1454 *func = func->ImplicitClosureFunction();
1455 if (func->HasCode()) {
1456 *best_fit = func->raw();
1457 }
1458 }
1459 }
1460 } else if ((func->token_pos() == best_fit->token_pos())
1461 && (func->end_token_pos() == best_fit->end_token_pos())
1462 && func->HasCode()) {
1463 // If func covers the same range, it is considered a better fit if
1464 // it has been compiled.
1465 *best_fit = func->raw();
1388 } 1466 }
1389 // Determine actual breakpoint location if the function or an
1390 // implicit closure of the function has been compiled already.
1391 if (target_function.HasCode()) {
1392 DeoptimizeWorld();
1393 ASSERT(!target_function.HasOptimizedCode());
1394 breakpoint_pos =
1395 ResolveBreakpointPos(target_function, first_token_pos, last_token_pos);
1396 } else if (!closure.IsNull() && closure.HasCode()) {
1397 DeoptimizeWorld();
1398 ASSERT(!closure.HasOptimizedCode());
1399 breakpoint_pos =
1400 ResolveBreakpointPos(closure, first_token_pos, last_token_pos);
1401 } else {
1402 // This function has not been compiled yet. Set a pending
1403 // breakpoint to be resolved later.
1404 SourceBreakpoint* source_bpt =
1405 GetSourceBreakpoint(target_function, first_token_pos);
1406 if (source_bpt != NULL) {
1407 // A pending source breakpoint for this uncompiled location
1408 // already exists.
1409 if (FLAG_verbose_debug) {
1410 OS::Print("Pending breakpoint for uncompiled function"
1411 " '%s' at line %" Pd " already exists\n",
1412 target_function.ToFullyQualifiedCString(),
1413 source_bpt->LineNumber());
1414 }
1415 return source_bpt;
1416 }
1417 source_bpt =
1418 new SourceBreakpoint(nextId(), target_function, first_token_pos);
1419 RegisterSourceBreakpoint(source_bpt);
1420 if (FLAG_verbose_debug) {
1421 OS::Print("Registering pending breakpoint for "
1422 "uncompiled function '%s' at line %" Pd "\n",
1423 target_function.ToFullyQualifiedCString(),
1424 source_bpt->LineNumber());
1425 }
1426 source_bpt->Enable();
1427 return source_bpt;
1428 }
1429 ASSERT(breakpoint_pos != -1);
1430 SourceBreakpoint* source_bpt =
1431 GetSourceBreakpoint(target_function, breakpoint_pos);
1432 if (source_bpt != NULL) {
1433 // A source breakpoint for this location already exists.
1434 return source_bpt;
1435 }
1436 source_bpt = new SourceBreakpoint(nextId(), target_function, breakpoint_pos);
1437 RegisterSourceBreakpoint(source_bpt);
1438 if (target_function.HasCode()) {
1439 MakeCodeBreakpointsAt(target_function, breakpoint_pos, source_bpt);
1440 }
1441 if (!closure.IsNull() && closure.HasCode()) {
1442 MakeCodeBreakpointsAt(closure, breakpoint_pos, source_bpt);
1443 }
1444 source_bpt->Enable();
1445 SignalBpResolved(source_bpt);
1446 return source_bpt;
1447 } 1467 }
1448 1468
1449 1469
1470 // Returns true if function is at least partially overlapping
1471 // the given token range in a script.
1472 static bool RangeOverlaps(const Function& func,
1473 const Script& script,
1474 intptr_t range_start,
1475 intptr_t range_end) {
1476 if ((func.end_token_pos() > range_start) && (func.token_pos() < range_end)) {
1477 // Check script equality second because it allocates
1478 // handles as a side effect.
1479 return func.script() == script.raw();
1480 }
1481 return false;
turnidge 2013/12/19 22:31:59 This cl contains a lot of range math that could ha
hausner 2013/12/21 00:08:32 Range math simplified and test added (not unit tes
1482 }
1483
1484
turnidge 2013/12/19 22:31:59 Could use a comment saying what it means for one f
hausner 2013/12/21 00:08:32 I think the comments you suggest are inside the fu
1485 RawFunction* Debugger::FindBestFit(const Script& script,
1486 intptr_t range_start,
1487 intptr_t range_end) {
turnidge 2013/12/19 22:31:59 Out of curiousity, could this function be static?
hausner 2013/12/21 00:08:32 Yes, but then I could not use the debugger object'
1488 Class& cls = Class::Handle(isolate_);
1489 Array& functions = Array::Handle(isolate_);
1490 GrowableObjectArray& closures = GrowableObjectArray::Handle(isolate_);
1491 Function& function = Function::Handle(isolate_);
1492 Function& best_fit = Function::Handle(isolate_);
1493
1494 const ClassTable& class_table = *isolate_->class_table();
1495 const intptr_t num_classes = class_table.NumCids();
1496 for (intptr_t i = 1; i < num_classes; i++) {
1497 if (class_table.HasValidClassAt(i)) {
1498 cls = class_table.At(i);
1499 // Note: we need to check the functions of this class even if
1500 // the class is defined in a differenct 'script'. There could
1501 // be mixin functions from the given script in this class.
1502 functions = cls.functions();
1503 if (!functions.IsNull()) {
1504 const intptr_t num_functions = functions.Length();
1505 for (intptr_t pos = 0; pos < num_functions; pos++) {
1506 function ^= functions.At(pos);
1507 ASSERT(!function.IsNull());
1508 if (RangeOverlaps(function, script, range_start, range_end)) {
1509 SelectBestFit(&best_fit, &function);
1510 }
1511 }
1512 }
1513
1514 closures = cls.closures();
1515 if (!closures.IsNull()) {
1516 const intptr_t num_closures = closures.Length();
1517 for (intptr_t pos = 0; pos < num_closures; pos++) {
1518 function ^= closures.At(pos);
1519 ASSERT(!function.IsNull());
1520 if (RangeOverlaps(function, script, range_start, range_end)) {
1521 SelectBestFit(&best_fit, &function);
turnidge 2013/12/19 22:31:59 If we were to change SelectBestFit to return the R
hausner 2013/12/21 00:08:32 I wanted to reuse the handles so that SelectBestFi
1522 }
1523 }
1524 }
1525 }
1526 }
1527 return best_fit.raw();
1528 }
1529
1530
1531 SourceBreakpoint* Debugger::SetBreakpoint(const Script& script,
1532 intptr_t range_start,
1533 intptr_t range_end) {
1534 Function& func = Function::Handle(isolate_);
1535 func = FindBestFit(script, range_start, range_end);
1536 if (func.IsNull()) {
1537 return NULL;
1538 }
1539 if (!func.IsNull() && func.HasCode()) {
1540 // A function containing this breakpoint location has already
1541 // been compiled. We can resolve the breakpoint now.
1542 DeoptimizeWorld();
1543 intptr_t breakpoint_pos =
1544 ResolveBreakpointPos(func, range_start, range_end);
1545 if (breakpoint_pos >= 0) {
1546 SourceBreakpoint* bpt = GetResolvedBreakpoint(script, breakpoint_pos);
1547 if (bpt != NULL) {
1548 // A source breakpoint for this location already exists.
1549 return bpt;
1550 }
1551 bpt = new SourceBreakpoint(nextId(), script, range_start, range_end);
1552 bpt->SetResolved(func, breakpoint_pos);
1553 RegisterSourceBreakpoint(bpt);
1554 // There may be more than one function object for a given function
1555 // in source code. There may be implicit closure functions, and
1556 // there may be copies of mixin functions. Collect all functions whose
1557 // source code range matches exactly the best fit function we
1558 // found.
1559 GrowableObjectArray& functions =
1560 GrowableObjectArray::Handle(GrowableObjectArray::New());
1561 FindAllFunctions(script,
1562 func.token_pos(),
1563 func.end_token_pos(),
1564 &functions);
1565 const intptr_t num_functions = functions.Length();
1566 // We must have found at least one function: func.
1567 ASSERT(num_functions > 0);
1568 // Create code breakpoints for all compiled functions we found.
1569 for (intptr_t i = 0; i < num_functions; i++) {
1570 func ^= functions.At(i);
1571 if (func.HasCode()) {
1572 MakeCodeBreakpointsAt(func, bpt);
1573 }
1574 }
1575 bpt->Enable();
turnidge 2013/12/19 22:31:59 If we move the call to Enable before we make the c
hausner 2013/12/21 00:08:32 Yes but the call to Enable() has the side effect o
1576 SignalBpResolved(bpt);
1577 return bpt;
1578 }
1579 }
1580 // There is no compiled function at this range. Register an unresolved
1581 // breakpoint.
1582 if (FLAG_verbose_debug && !func.IsNull()) {
1583 intptr_t line_number;
1584 script.GetTokenLocation(range_start, &line_number, NULL);
1585 OS::Print("Registering pending breakpoint for "
1586 "uncompiled function '%s' at line %" Pd "\n",
1587 func.ToFullyQualifiedCString(),
1588 line_number);
1589 }
1590 SourceBreakpoint* bpt = GetUnresolvedBreakpoint(script, range_start);
1591 if (bpt == NULL) {
1592 bpt = new SourceBreakpoint(nextId(), script, range_start, range_end);
1593 }
1594 RegisterSourceBreakpoint(bpt);
1595 bpt->Enable();
1596 return bpt;
1597 }
1598
1599
1450 // Synchronize the enabled/disabled state of all code breakpoints 1600 // Synchronize the enabled/disabled state of all code breakpoints
1451 // associated with the source breakpoint bpt. 1601 // associated with the source breakpoint bpt.
1452 void Debugger::SyncBreakpoint(SourceBreakpoint* bpt) { 1602 void Debugger::SyncBreakpoint(SourceBreakpoint* bpt) {
1453 CodeBreakpoint* cbpt = code_breakpoints_; 1603 CodeBreakpoint* cbpt = code_breakpoints_;
1454 while (cbpt != NULL) { 1604 while (cbpt != NULL) {
1455 if (bpt == cbpt->src_bpt()) { 1605 if (bpt == cbpt->src_bpt()) {
1456 if (bpt->IsEnabled()) { 1606 if (bpt->IsEnabled()) {
1457 cbpt->Enable(); 1607 cbpt->Enable();
1458 } else { 1608 } else {
1459 cbpt->Disable(); 1609 cbpt->Disable();
(...skipping 10 matching lines...) Expand all
1470 const Function& closure_func = 1620 const Function& closure_func =
1471 Function::Handle(target_function.ImplicitClosureFunction()); 1621 Function::Handle(target_function.ImplicitClosureFunction());
1472 InstrumentForStepping(closure_func); 1622 InstrumentForStepping(closure_func);
1473 } 1623 }
1474 } 1624 }
1475 1625
1476 1626
1477 SourceBreakpoint* Debugger::SetBreakpointAtEntry( 1627 SourceBreakpoint* Debugger::SetBreakpointAtEntry(
1478 const Function& target_function) { 1628 const Function& target_function) {
1479 ASSERT(!target_function.IsNull()); 1629 ASSERT(!target_function.IsNull());
1480 return SetBreakpoint(target_function, 1630 const Script& script = Script::Handle(target_function.script());
1631 return SetBreakpoint(script,
1481 target_function.token_pos(), 1632 target_function.token_pos(),
1482 target_function.end_token_pos()); 1633 target_function.token_pos() + 1);
turnidge 2013/12/19 22:31:59 If the range end is included in the range, do you
hausner 2013/12/21 00:08:32 obsolete.
1483 } 1634 }
1484 1635
1485 1636
1486 SourceBreakpoint* Debugger::SetBreakpointAtLine(const String& script_url, 1637 SourceBreakpoint* Debugger::SetBreakpointAtLine(const String& script_url,
1487 intptr_t line_number) { 1638 intptr_t line_number) {
1488 Library& lib = Library::Handle(isolate_); 1639 Library& lib = Library::Handle(isolate_);
1489 Script& script = Script::Handle(isolate_); 1640 Script& script = Script::Handle(isolate_);
1490 const GrowableObjectArray& libs = 1641 const GrowableObjectArray& libs =
1491 GrowableObjectArray::Handle(isolate_->object_store()->libraries()); 1642 GrowableObjectArray::Handle(isolate_->object_store()->libraries());
1492 for (intptr_t i = 0; i < libs.Length(); i++) { 1643 for (intptr_t i = 0; i < libs.Length(); i++) {
(...skipping 19 matching lines...) Expand all
1512 script_url.ToCString(), line_number); 1663 script_url.ToCString(), line_number);
1513 } 1664 }
1514 return NULL; 1665 return NULL;
1515 } else if (last_token_idx < 0) { 1666 } else if (last_token_idx < 0) {
1516 // Line does not contain any tokens. first_token_index is the first 1667 // Line does not contain any tokens. first_token_index is the first
1517 // token after the given line. We check whether that token is 1668 // token after the given line. We check whether that token is
1518 // part of a function. 1669 // part of a function.
1519 last_token_idx = first_token_idx; 1670 last_token_idx = first_token_idx;
1520 } 1671 }
1521 1672
1522 Function& func = Function::Handle(isolate_); 1673 SourceBreakpoint* bpt =
1523 while (first_token_idx <= last_token_idx) { 1674 SetBreakpoint(script, first_token_idx, last_token_idx);
1524 func = lib.LookupFunctionInScript(script, first_token_idx); 1675 if ((bpt == NULL) && FLAG_verbose_debug) {
1525 if (!func.IsNull()) { 1676 OS::Print("No executable code at line %" Pd " in '%s'\n",
1526 break; 1677 line_number, script_url.ToCString());
1527 }
1528 first_token_idx++;
1529 } 1678 }
1530 if (func.IsNull()) { 1679 return bpt;
1531 if (FLAG_verbose_debug) {
1532 OS::Print("No executable code at line %" Pd " in '%s'\n",
1533 line_number, script_url.ToCString());
1534 }
1535 return NULL;
1536 }
1537 if (last_token_idx < 0) {
1538 // The token at first_token_index is past the requested source line.
1539 // Set the breakpoint at the closest position after that line.
1540 last_token_idx = func.end_token_pos();
1541 }
1542 return SetBreakpoint(func, first_token_idx, last_token_idx);
1543 } 1680 }
1544 1681
1545 1682
1546 intptr_t Debugger::CacheObject(const Object& obj) { 1683 intptr_t Debugger::CacheObject(const Object& obj) {
1547 ASSERT(obj_cache_ != NULL); 1684 ASSERT(obj_cache_ != NULL);
1548 return obj_cache_->AddObject(obj); 1685 return obj_cache_->AddObject(obj);
1549 } 1686 }
1550 1687
1551 1688
1552 bool Debugger::IsValidObjectId(intptr_t obj_id) { 1689 bool Debugger::IsValidObjectId(intptr_t obj_id) {
(...skipping 359 matching lines...) Expand 10 before | Expand all | Expand 10 after
1912 // This port will be used as a unique ID to represet the isolate in the 2049 // This port will be used as a unique ID to represet the isolate in the
1913 // debugger wire protocol messages. 2050 // debugger wire protocol messages.
1914 isolate_id_ = isolate->main_port(); 2051 isolate_id_ = isolate->main_port();
1915 initialized_ = true; 2052 initialized_ = true;
1916 2053
1917 // Signal isolate creation event. 2054 // Signal isolate creation event.
1918 SignalIsolateEvent(Debugger::kIsolateCreated); 2055 SignalIsolateEvent(Debugger::kIsolateCreated);
1919 } 2056 }
1920 2057
1921 2058
1922 static RawFunction* GetOriginalFunction(const Function& func) { 2059 // Return innermost closure contained in 'function' that entirely contains
1923 if (func.IsClosureFunction()) { 2060 // the given token range.
1924 // Local functions (closures) in mixin functions do not have 2061 RawFunction* Debugger::FindInnermostClosure(const Function& function,
1925 // an original function they were cloned from. 2062 intptr_t range_start,
1926 // Note: this is a problem when a breakpoint is set in a mixed-in 2063 intptr_t range_end) {
turnidge 2013/12/19 22:31:59 Could this be static?
hausner 2013/12/21 00:08:32 Yes but I wanted to used debugger->isolate_ to spe
1927 // closure. The breakpoint is linked to the closure attached to the 2064 const Class& owner = Class::Handle(isolate_, function.Owner());
1928 // mixin application class, not the mixin class. When the same 2065 if (owner.closures() == GrowableObjectArray::null()) {
1929 // closure is compiled for another mixin application class, we 2066 return Function::null();
1930 // don't find the breakpoint since we'll be looking in the
1931 // mixin class.
1932 return func.raw();
1933 } 2067 }
1934 const Class& origin_class = Class::Handle(func.origin()); 2068 // Note that we need to check that the closure is in the same
1935 if (origin_class.is_patch()) { 2069 // script as the outer function. We could have closures originating
1936 // Patched functions from patch classes are removed from the 2070 // in mixin classes whose source code is contained in a differenct
turnidge 2013/12/19 22:31:59 typo "differenct"
hausner 2013/12/21 00:08:32 Done.
1937 // function array of the patch class, so we will not find an 2071 // script.
1938 // original function object. 2072 const Script& outer_origin = Script::Handle(isolate_, function.script());
1939 return func.raw(); 2073 const GrowableObjectArray& closures =
1940 } 2074 GrowableObjectArray::Handle(isolate_, owner.closures());
1941 const Array& functions = Array::Handle(origin_class.functions()); 2075 const intptr_t num_closures = closures.Length();
1942 Object& orig_func = Object::Handle(); 2076 Function& closure = Function::Handle(isolate_);
1943 for (intptr_t i = 0; i < functions.Length(); i++) { 2077 Function& best_fit = Function::Handle(isolate_);
1944 orig_func = functions.At(i); 2078 for (intptr_t i = 0; i < num_closures; i++) {
1945 // Function names are symbols, so we can compare the raw pointers. 2079 closure ^= closures.At(i);
1946 if (func.name() == Function::Cast(orig_func).name()) { 2080 if ((function.token_pos() < closure.token_pos()) &&
1947 return Function::Cast(orig_func).raw(); 2081 (closure.end_token_pos() < function.end_token_pos()) &&
2082 (closure.token_pos() <= range_start) &&
2083 (range_end <= closure.end_token_pos()) &&
2084 (closure.script() == outer_origin.raw())) {
2085 SelectBestFit(&best_fit, &closure);
1948 } 2086 }
1949 } 2087 }
1950 // Uncommon case: not a mixin function. 2088 return best_fit.raw();
1951 ASSERT(!Class::Handle(func.Owner()).IsMixinApplication());
1952 return func.raw();
1953 } 2089 }
1954 2090
1955 2091
1956 void Debugger::NotifyCompilation(const Function& func) { 2092 void Debugger::NotifyCompilation(const Function& func) {
1957 if (src_breakpoints_ == NULL) { 2093 if (src_breakpoints_ == NULL) {
1958 // Return with minimal overhead if there are no breakpoints. 2094 // Return with minimal overhead if there are no breakpoints.
1959 return; 2095 return;
1960 } 2096 }
1961 Function& lookup_function = Function::Handle(func.raw()); 2097 // Iterate over all source breakpoints to check whether breakpoints
1962 if (func.IsImplicitClosureFunction()) { 2098 // need to be set in the newly compiled function.
1963 // If the newly compiled function is a an implicit closure (a closure that 2099 Script& script = Script::Handle(isolate_);
1964 // was formed by assigning a static or instance method to a function 2100 for (SourceBreakpoint* bpt = src_breakpoints_;
1965 // object), we need to use the closure's parent function to see whether 2101 bpt != NULL;
1966 // there are any breakpoints. The parent function is the actual method on 2102 bpt = bpt->next()) {
1967 // which the user sets breakpoints. 2103 script = bpt->script();
1968 lookup_function = func.parent_function(); 2104 if (RangeOverlaps(func, script, bpt->range_start_, bpt->range_end_)) {
1969 ASSERT(!lookup_function.IsNull()); 2105 Function& inner_function = Function::Handle(isolate_);
turnidge 2013/12/19 22:31:59 Since the function name doesn't say "containing th
hausner 2013/12/21 00:08:32 obsolete
1970 } 2106 inner_function =
1971 if (lookup_function.Owner() != lookup_function.origin()) { 2107 FindInnermostClosure(func, bpt->range_start_, bpt->range_end_);
1972 // This is a cloned function from a mixin class. If a breakpoint 2108 if (!inner_function.IsNull()) {
1973 // was set in this function, it is registered using the function 2109 // The local function of a function we just compiled cannot
1974 // of the origin class. 2110 // be compiled already.
1975 lookup_function = GetOriginalFunction(lookup_function); 2111 ASSERT(!inner_function.HasCode());
1976 }
1977 SourceBreakpoint* bpt = src_breakpoints_;
1978 while (bpt != NULL) {
1979 if (lookup_function.raw() == bpt->function()) {
1980 // Check if the breakpoint is inside a closure or local function
1981 // within the newly compiled function. Note that we must look
1982 // in the owner class of the function that just got compiled
1983 // (i.e. func), not the owner class of the function we use to
1984 // record the breakpoint (lookup_function).
1985 Class& owner = Class::Handle(func.Owner());
1986 Function& closure =
1987 Function::Handle(owner.LookupClosureFunction(bpt->token_pos()));
1988 if (!closure.IsNull() && (closure.raw() != lookup_function.raw())) {
1989 if (FLAG_verbose_debug) { 2112 if (FLAG_verbose_debug) {
1990 OS::Print("Resetting pending breakpoint to function %s\n", 2113 OS::Print("Pending BP remains unresolved in inner function '%s'\n",
1991 closure.ToFullyQualifiedCString()); 2114 inner_function.ToFullyQualifiedCString());
1992 } 2115 }
1993 bpt->set_function(closure); 2116 continue;
1994 } else { 2117 }
2118
2119 // TODO(hausner): What should we do here? Can we deoptimize the function?
2120 ASSERT(!func.HasOptimizedCode());
2121
2122 // There is no local function within func that contains the
2123 // breakpoint token range. Resolve the breakpoint if necessary
2124 // and set the code breakpoints.
2125 if (!bpt->IsResolved()) {
2126 // Resolve source breakpoint in the newly compiled function.
2127 intptr_t bp_pos =
2128 ResolveBreakpointPos(func, bpt->range_start_, bpt->range_end_);
2129 if (bp_pos < 0) {
2130 if (FLAG_verbose_debug) {
2131 OS::Print("Failed resolving breakpoint for function '%s'\n",
2132 String::Handle(func.name()).ToCString());
2133 }
2134 continue;
2135 }
2136 bpt->SetResolved(func, bp_pos);
1995 if (FLAG_verbose_debug) { 2137 if (FLAG_verbose_debug) {
1996 OS::Print("Enable pending breakpoint for function '%s'\n", 2138 OS::Print("Resolved BP %" Pd " to pos %" Pd ", line %" Pd ", "
1997 String::Handle(lookup_function.name()).ToCString()); 2139 "function '%s' (requested range %" Pd "-%" Pd ")\n",
2140 bpt->id(),
2141 bpt->token_pos(),
2142 bpt->LineNumber(),
2143 func.ToFullyQualifiedCString(),
2144 bpt->range_start_, bpt->range_end_);
1998 } 2145 }
1999 const Script& script= Script::Handle(func.script());
2000 intptr_t first_pos, last_pos;
2001 script.TokenRangeAtLine(bpt->LineNumber(), &first_pos, &last_pos);
2002 intptr_t bp_pos =
2003 ResolveBreakpointPos(func, bpt->token_pos(), last_pos);
2004 bpt->set_token_pos(bp_pos);
2005 MakeCodeBreakpointsAt(func, bp_pos, bpt);
2006 SignalBpResolved(bpt); 2146 SignalBpResolved(bpt);
2007 } 2147 }
2008 bpt->Enable(); // Enables the code breakpoint as well. 2148 ASSERT(bpt->IsResolved());
2149 if (FLAG_verbose_debug) {
2150 OS::Print("Setting breakpoint %" Pd " at line %" Pd " for %s '%s'\n",
2151 bpt->id(),
2152 bpt->LineNumber(),
2153 func.IsClosureFunction() ? "closure" : "function",
2154 String::Handle(func.name()).ToCString());
2155 }
2156 MakeCodeBreakpointsAt(func, bpt);
turnidge 2013/12/19 22:31:59 In SetBreakpoint, you call SignalBpResolved *after
hausner 2013/12/21 00:08:32 I don't think it matters. The isolate is not pause
2009 } 2157 }
2010 bpt = bpt->next();
2011 } 2158 }
2012 } 2159 }
2013 2160
2014 2161
2015 CodeBreakpoint* Debugger::GetCodeBreakpoint(uword breakpoint_address) { 2162 CodeBreakpoint* Debugger::GetCodeBreakpoint(uword breakpoint_address) {
2016 CodeBreakpoint* bpt = code_breakpoints_; 2163 CodeBreakpoint* bpt = code_breakpoints_;
2017 while (bpt != NULL) { 2164 while (bpt != NULL) {
2018 if (bpt->pc() == breakpoint_address) { 2165 if (bpt->pc() == breakpoint_address) {
2019 return bpt; 2166 return bpt;
2020 } 2167 }
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
2094 temp_bpt->Disable(); 2241 temp_bpt->Disable();
2095 delete temp_bpt; 2242 delete temp_bpt;
2096 } else { 2243 } else {
2097 prev_bpt = curr_bpt; 2244 prev_bpt = curr_bpt;
2098 curr_bpt = curr_bpt->next(); 2245 curr_bpt = curr_bpt->next();
2099 } 2246 }
2100 } 2247 }
2101 } 2248 }
2102 2249
2103 2250
2104 SourceBreakpoint* Debugger::GetSourceBreakpoint(const Function& func, 2251 SourceBreakpoint* Debugger::GetUnresolvedBreakpoint(const Script& script,
2105 intptr_t token_pos) { 2252 intptr_t range_start) {
2106 SourceBreakpoint* bpt = src_breakpoints_; 2253 SourceBreakpoint* bpt = src_breakpoints_;
2107 while (bpt != NULL) { 2254 while (bpt != NULL) {
2108 if ((bpt->function() == func.raw()) && 2255 if ((bpt->script_ == script.raw()) && (bpt->range_start_ == range_start)) {
turnidge 2013/12/19 22:31:59 SetResolved does not clear the range_start_, does
hausner 2013/12/21 00:08:32 obsolete
2109 (bpt->token_pos() == token_pos)) {
2110 return bpt; 2256 return bpt;
2111 } 2257 }
2112 bpt = bpt->next(); 2258 bpt = bpt->next();
2113 } 2259 }
2114 return NULL; 2260 return NULL;
2115 } 2261 }
2116 2262
2117 2263
2264 SourceBreakpoint* Debugger::GetResolvedBreakpoint(const Script& script,
2265 intptr_t token_pos) {
2266 SourceBreakpoint* bpt = src_breakpoints_;
2267 while (bpt != NULL) {
2268 if ((bpt->script_ == script.raw()) && (bpt->token_pos_ == token_pos)) {
2269 return bpt;
2270 }
2271 bpt = bpt->next();
2272 }
2273 return NULL;
2274 }
2275
2276
2118 SourceBreakpoint* Debugger::GetBreakpointById(intptr_t id) { 2277 SourceBreakpoint* Debugger::GetBreakpointById(intptr_t id) {
2119 SourceBreakpoint* bpt = src_breakpoints_; 2278 SourceBreakpoint* bpt = src_breakpoints_;
2120 while (bpt != NULL) { 2279 while (bpt != NULL) {
2121 if (bpt->id() == id) { 2280 if (bpt->id() == id) {
2122 return bpt; 2281 return bpt;
2123 } 2282 }
2124 bpt = bpt->next(); 2283 bpt = bpt->next();
2125 } 2284 }
2126 return NULL; 2285 return NULL;
2127 } 2286 }
2128 2287
2129 2288
2130 void Debugger::RegisterSourceBreakpoint(SourceBreakpoint* bpt) { 2289 void Debugger::RegisterSourceBreakpoint(SourceBreakpoint* bpt) {
2131 ASSERT(bpt->next() == NULL); 2290 ASSERT(bpt->next() == NULL);
2132 bpt->set_next(src_breakpoints_); 2291 bpt->set_next(src_breakpoints_);
2133 src_breakpoints_ = bpt; 2292 src_breakpoints_ = bpt;
2134 } 2293 }
2135 2294
2136 2295
2137 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) { 2296 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) {
2138 ASSERT(bpt->next() == NULL); 2297 ASSERT(bpt->next() == NULL);
2139 bpt->set_next(code_breakpoints_); 2298 bpt->set_next(code_breakpoints_);
2140 code_breakpoints_ = bpt; 2299 code_breakpoints_ = bpt;
2141 } 2300 }
2142 2301
2143 } // namespace dart 2302 } // namespace dart
OLDNEW
« runtime/vm/debugger.h ('K') | « runtime/vm/debugger.h ('k') | runtime/vm/debugger_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698