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

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 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « runtime/vm/debugger.h ('k') | runtime/vm/debugger_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 token_pos)
55 : id_(id), 55 : id_(id),
56 function_(func.raw()), 56 script_(script.raw()),
57 token_pos_(token_pos), 57 token_pos_(token_pos),
58 line_number_(-1), 58 is_resolved_(false),
59 is_enabled_(false), 59 is_enabled_(false),
60 next_(NULL) { 60 next_(NULL),
61 ASSERT(!func.IsNull()); 61 function_(Function::null()),
62 ASSERT((func.token_pos() <= token_pos_) && 62 line_number_(-1) {
63 (token_pos_ <= func.end_token_pos())); 63 ASSERT(id_ > 0);
64 ASSERT(!script.IsNull());
65 ASSERT(token_pos_ >= 0);
64 } 66 }
65 67
66 68
67 void SourceBreakpoint::Enable() { 69 void SourceBreakpoint::Enable() {
68 is_enabled_ = true; 70 is_enabled_ = true;
69 Isolate::Current()->debugger()->SyncBreakpoint(this); 71 Isolate::Current()->debugger()->SyncBreakpoint(this);
70 } 72 }
71 73
72 74
73 void SourceBreakpoint::Disable() { 75 void SourceBreakpoint::Disable() {
74 is_enabled_ = false; 76 is_enabled_ = false;
75 Isolate::Current()->debugger()->SyncBreakpoint(this); 77 Isolate::Current()->debugger()->SyncBreakpoint(this);
76 } 78 }
77 79
78 80
79 RawScript* SourceBreakpoint::SourceCode() { 81 void SourceBreakpoint::SetResolved(const Function& func, intptr_t token_pos) {
80 const Function& func = Function::Handle(function_); 82 ASSERT(func.script() == script_);
81 return func.script(); 83 ASSERT((func.token_pos() <= token_pos) &&
84 (token_pos <= func.end_token_pos()));
85 function_ = func.raw();
86 token_pos_ = token_pos;
87 line_number_ = -1; // Recalcualte lazily.
88 is_resolved_ = true;
82 } 89 }
83 90
84 91
92 // TODO(hausner): Get rid of library parameter. A source breakpoint location
93 // does not imply a library, since the same source code can be included
94 // in more than one library, e.g. the text location of mixin functions.
85 void SourceBreakpoint::GetCodeLocation( 95 void SourceBreakpoint::GetCodeLocation(
86 Library* lib, 96 Library* lib,
87 Script* script, 97 Script* script,
88 intptr_t* pos) const { 98 intptr_t* pos) {
89 const Function& func = Function::Handle(function_); 99 *script = this->script();
90 const Class& cls = Class::Handle(func.origin()); 100 *pos = token_pos_;
91 *lib = cls.library(); 101 if (IsResolved()) {
92 *script = func.script(); 102 const Function& func = Function::Handle(function_);
93 *pos = token_pos(); 103 ASSERT(!func.IsNull());
104 const Class& cls = Class::Handle(func.origin());
105 *lib = cls.library();
106 } else {
107 *lib = Library::null();
108 }
94 } 109 }
95 110
96 111
97 RawString* SourceBreakpoint::SourceUrl() { 112 RawString* SourceBreakpoint::SourceUrl() {
98 const Script& script = Script::Handle(SourceCode()); 113 return Script::Handle(script()).url();
99 return script.url();
100 } 114 }
101 115
102 116
103 intptr_t SourceBreakpoint::LineNumber() { 117 intptr_t SourceBreakpoint::LineNumber() {
104 // Compute line number lazily since it causes scanning of the script. 118 // Compute line number lazily since it causes scanning of the script.
105 if (line_number_ < 0) { 119 if (line_number_ < 0) {
106 const Script& script = Script::Handle(SourceCode()); 120 const Script& script = Script::Handle(this->script());
107 script.GetTokenLocation(token_pos_, &line_number_, NULL); 121 script.GetTokenLocation(token_pos_, &line_number_, NULL);
108 } 122 }
109 return line_number_; 123 return line_number_;
110 } 124 }
111 125
112 126
113 void SourceBreakpoint::set_function(const Function& func) {
114 function_ = func.raw();
115 }
116
117
118 void SourceBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { 127 void SourceBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
128 visitor->VisitPointer(reinterpret_cast<RawObject**>(&script_));
119 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); 129 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_));
120 } 130 }
121 131
122 132
123 void SourceBreakpoint::PrintToJSONStream(JSONStream* stream) const { 133 void SourceBreakpoint::PrintToJSONStream(JSONStream* stream) {
124 Isolate* isolate = Isolate::Current(); 134 Isolate* isolate = Isolate::Current();
125 135
126 JSONObject jsobj(stream); 136 JSONObject jsobj(stream);
127 jsobj.AddProperty("type", "Breakpoint"); 137 jsobj.AddProperty("type", "Breakpoint");
128 138
129 jsobj.AddProperty("id", id()); 139 jsobj.AddProperty("id", id());
130 jsobj.AddProperty("enabled", IsEnabled()); 140 jsobj.AddProperty("enabled", IsEnabled());
131 141 jsobj.AddProperty("resolved", IsResolved());
132 const Function& func = Function::Handle(function());
133 jsobj.AddProperty("resolved", func.HasCode());
134 142
135 Library& library = Library::Handle(isolate); 143 Library& library = Library::Handle(isolate);
136 Script& script = Script::Handle(isolate); 144 Script& script = Script::Handle(isolate);
137 intptr_t token_pos; 145 intptr_t token_pos;
138 GetCodeLocation(&library, &script, &token_pos); 146 GetCodeLocation(&library, &script, &token_pos);
139 { 147 {
140 JSONObject location(&jsobj, "location"); 148 JSONObject location(&jsobj, "location");
141 location.AddProperty("type", "Location"); 149 location.AddProperty("type", "Location");
142 location.AddProperty("libId", library.index());
143 150
144 const String& url = String::Handle(script.url()); 151 const String& url = String::Handle(script.url());
145 location.AddProperty("script", url.ToCString()); 152 location.AddProperty("script", url.ToCString());
146 location.AddProperty("tokenPos", token_pos); 153 location.AddProperty("tokenPos", token_pos);
147 } 154 }
148 } 155 }
149 156
150 157
151 void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { 158 void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
152 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); 159 visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_));
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 len++; // String terminator. 221 len++; // String terminator.
215 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); 222 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
216 OS::SNPrint(chars, len, kFormat, 223 OS::SNPrint(chars, len, kFormat,
217 func_class.IsTopLevel() ? "" : class_name.ToCString(), 224 func_class.IsTopLevel() ? "" : class_name.ToCString(),
218 func_class.IsTopLevel() ? "" : ".", 225 func_class.IsTopLevel() ? "" : ".",
219 func_name.ToCString()); 226 func_name.ToCString());
220 return chars; 227 return chars;
221 } 228 }
222 229
223 230
231 // Returns true if function contains the token position in the given script.
232 static bool FunctionContains(const Function& func,
233 const Script& script,
234 intptr_t token_pos) {
235 if ((func.token_pos() <= token_pos) && (token_pos <= func.end_token_pos())) {
236 // Check script equality second because it allocates
237 // handles as a side effect.
238 return func.script() == script.raw();
239 }
240 return false;
241 }
242
243
224 bool Debugger::HasBreakpoint(const Function& func) { 244 bool Debugger::HasBreakpoint(const Function& func) {
225 if (!func.HasCode()) { 245 if (!func.HasCode()) {
226 // If the function is not compiled yet, just check whether there 246 // If the function is not compiled yet, just check whether there
227 // is a user-defined latent breakpoint. 247 // is a user-defined breakpoint that falls into the token
248 // range of the function. This may be a false positive: the breakpoint
249 // might be inside a local closure.
250 Script& script = Script::Handle(isolate_);
228 SourceBreakpoint* sbpt = src_breakpoints_; 251 SourceBreakpoint* sbpt = src_breakpoints_;
229 while (sbpt != NULL) { 252 while (sbpt != NULL) {
230 if (func.raw() == sbpt->function()) { 253 script = sbpt->script();
254 if (FunctionContains(func, script, sbpt->token_pos())) {
231 return true; 255 return true;
232 } 256 }
233 sbpt = sbpt->next_; 257 sbpt = sbpt->next_;
234 } 258 }
235 return false; 259 return false;
236 } 260 }
237 CodeBreakpoint* cbpt = code_breakpoints_; 261 CodeBreakpoint* cbpt = code_breakpoints_;
238 while (cbpt != NULL) { 262 while (cbpt != NULL) {
239 if (func.raw() == cbpt->function()) { 263 if (func.raw() == cbpt->function()) {
240 return true; 264 return true;
(...skipping 1037 matching lines...) Expand 10 before | Expand all | Expand 10 after
1278 } 1302 }
1279 DebuggerEvent event(kExceptionThrown); 1303 DebuggerEvent event(kExceptionThrown);
1280 event.exception = &exc; 1304 event.exception = &exc;
1281 ASSERT(stack_trace_ == NULL); 1305 ASSERT(stack_trace_ == NULL);
1282 stack_trace_ = stack_trace; 1306 stack_trace_ = stack_trace;
1283 Pause(&event); 1307 Pause(&event);
1284 stack_trace_ = NULL; 1308 stack_trace_ = NULL;
1285 } 1309 }
1286 1310
1287 1311
1288 // Given a function and a token position range, return the best fit 1312 // Given a function and a token position, return the best fit
1289 // token position to set a breakpoint. 1313 // token position to set a breakpoint. The best fit is the safe point
1290 // If multiple possible breakpoint positions are within the given range, 1314 // with the lowest compiled code address that follows the requsted
1291 // the one with the lowest machine code address is picked. 1315 // token position.
1292 // If no possible breakpoint location exists in the given range, the closest
1293 // token position after the range is returned.
1294 intptr_t Debugger::ResolveBreakpointPos(const Function& func, 1316 intptr_t Debugger::ResolveBreakpointPos(const Function& func,
1295 intptr_t first_token_pos, 1317 intptr_t requested_token_pos) {
1296 intptr_t last_token_pos) {
1297 ASSERT(func.HasCode()); 1318 ASSERT(func.HasCode());
1298 ASSERT(!func.HasOptimizedCode()); 1319 ASSERT(!func.HasOptimizedCode());
1299 Code& code = Code::Handle(func.unoptimized_code()); 1320 Code& code = Code::Handle(func.unoptimized_code());
1300 ASSERT(!code.IsNull()); 1321 ASSERT(!code.IsNull());
1301 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); 1322 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
1302 intptr_t best_fit_index = -1; 1323 intptr_t best_fit_index = -1;
1303 intptr_t best_fit = INT_MAX; 1324 intptr_t best_fit_pos = INT_MAX;
1304 uword lowest_pc = kUwordMax; 1325 uword lowest_pc = kUwordMax;
1305 intptr_t lowest_pc_index = -1; 1326 intptr_t lowest_pc_index = -1;
1306 for (intptr_t i = 0; i < desc.Length(); i++) { 1327 for (intptr_t i = 0; i < desc.Length(); i++) {
1307 intptr_t desc_token_pos = desc.TokenPos(i); 1328 intptr_t desc_token_pos = desc.TokenPos(i);
1308 ASSERT(desc_token_pos >= 0); 1329 ASSERT(desc_token_pos >= 0);
1309 if (desc_token_pos < first_token_pos) { 1330 if (desc_token_pos < requested_token_pos) {
1310 // This descriptor is before the given range. 1331 // This descriptor is before the first acceptable token position.
1311 continue; 1332 continue;
1312 } 1333 }
1313 if (IsSafePoint(desc.DescriptorKind(i))) { 1334 if (IsSafePoint(desc.DescriptorKind(i))) {
1314 if ((desc_token_pos - first_token_pos) < best_fit) { 1335 if (desc_token_pos < best_fit_pos) {
1315 // So far, this descriptor has the closest token position to the 1336 // So far, this descriptor has the lowest token position after
1316 // beginning of the range. 1337 // the first acceptable token position.
1317 best_fit = desc_token_pos - first_token_pos; 1338 best_fit_pos = desc_token_pos;
1318 ASSERT(best_fit >= 0);
1319 best_fit_index = i; 1339 best_fit_index = i;
1320 } 1340 }
1321 if ((first_token_pos <= desc_token_pos) && 1341 if (desc.PC(i) < lowest_pc) {
1322 (desc_token_pos <= last_token_pos) && 1342 // This descriptor so far has the lowest code address.
1323 (desc.PC(i) < lowest_pc)) {
1324 // This descriptor is within the token position range and so
1325 // far has the lowest code address.
1326 lowest_pc = desc.PC(i); 1343 lowest_pc = desc.PC(i);
1327 lowest_pc_index = i; 1344 lowest_pc_index = i;
1328 } 1345 }
1329 } 1346 }
1330 } 1347 }
1331 if (lowest_pc_index >= 0) { 1348 if (lowest_pc_index >= 0) {
1332 // We found the the pc descriptor within the given token range that 1349 // We found the pc descriptor that has the lowest execution address.
1333 // has the lowest execution address. This is the first possible 1350 // This is the first possible breakpoint after the requested token
1334 // breakpoint on the line. We use this instead of the nearest 1351 // position. We use this instead of the nearest PC descriptor
1335 // PC descriptor measured in token index distance. 1352 // measured in token index distance.
1336 best_fit_index = lowest_pc_index; 1353 best_fit_index = lowest_pc_index;
1337 } 1354 }
1338 if (best_fit_index >= 0) { 1355 if (best_fit_index >= 0) {
1339 return desc.TokenPos(best_fit_index); 1356 return desc.TokenPos(best_fit_index);
1340 } 1357 }
1341 return -1; 1358 return -1;
1342 } 1359 }
1343 1360
1344 1361
1345 void Debugger::MakeCodeBreakpointsAt(const Function& func, 1362 void Debugger::MakeCodeBreakpointsAt(const Function& func,
1346 intptr_t token_pos,
1347 SourceBreakpoint* bpt) { 1363 SourceBreakpoint* bpt) {
1364 ASSERT((bpt != NULL) && bpt->IsResolved());
1348 ASSERT(!func.HasOptimizedCode()); 1365 ASSERT(!func.HasOptimizedCode());
1349 Code& code = Code::Handle(func.unoptimized_code()); 1366 Code& code = Code::Handle(func.unoptimized_code());
1350 ASSERT(!code.IsNull()); 1367 ASSERT(!code.IsNull());
1351 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); 1368 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
1352 for (intptr_t i = 0; i < desc.Length(); i++) { 1369 for (intptr_t i = 0; i < desc.Length(); i++) {
1353 intptr_t desc_token_pos = desc.TokenPos(i); 1370 intptr_t desc_token_pos = desc.TokenPos(i);
1354 if ((desc_token_pos == token_pos) && IsSafePoint(desc.DescriptorKind(i))) { 1371 if ((desc_token_pos == bpt->token_pos_) &&
1372 IsSafePoint(desc.DescriptorKind(i))) {
1355 CodeBreakpoint* code_bpt = GetCodeBreakpoint(desc.PC(i)); 1373 CodeBreakpoint* code_bpt = GetCodeBreakpoint(desc.PC(i));
1356 if (code_bpt == NULL) { 1374 if (code_bpt == NULL) {
1357 // No code breakpoint for this code exists; create one. 1375 // No code breakpoint for this code exists; create one.
1358 code_bpt = new CodeBreakpoint(func, i); 1376 code_bpt = new CodeBreakpoint(func, i);
1359 RegisterCodeBreakpoint(code_bpt); 1377 RegisterCodeBreakpoint(code_bpt);
1360 } 1378 }
1361 code_bpt->set_src_bpt(bpt); 1379 code_bpt->set_src_bpt(bpt);
1362 } 1380 if (bpt->IsEnabled()) {
1363 } 1381 code_bpt->Enable();
1364 } 1382 }
1365 1383 }
1366 1384 }
1367 SourceBreakpoint* Debugger::SetBreakpoint(const Function& target_function, 1385 }
1368 intptr_t first_token_pos, 1386
1369 intptr_t last_token_pos) { 1387
1370 if ((last_token_pos < target_function.token_pos()) || 1388 void Debugger::FindEquivalentFunctions(const Script& script,
1371 (target_function.end_token_pos() < first_token_pos)) { 1389 intptr_t start_pos,
1372 // The given token position is not within the target function. 1390 intptr_t end_pos,
1391 GrowableObjectArray* function_list) {
1392 Class& cls = Class::Handle(isolate_);
1393 Array& functions = Array::Handle(isolate_);
1394 GrowableObjectArray& closures = GrowableObjectArray::Handle(isolate_);
1395 Function& function = Function::Handle(isolate_);
1396
1397 const ClassTable& class_table = *isolate_->class_table();
1398 const intptr_t num_classes = class_table.NumCids();
1399 for (intptr_t i = 1; i < num_classes; i++) {
1400 if (class_table.HasValidClassAt(i)) {
1401 cls = class_table.At(i);
1402 // Note: we need to check the functions of this class even if
1403 // the class is defined in a differenct 'script'. There could
1404 // be mixin functions from the given script in this class.
1405 functions = cls.functions();
1406 if (!functions.IsNull()) {
1407 const intptr_t num_functions = functions.Length();
1408 for (intptr_t pos = 0; pos < num_functions; pos++) {
1409 function ^= functions.At(pos);
1410 ASSERT(!function.IsNull());
1411 // Check token position first to avoid unnecessary calls
1412 // to script() which allocates handles.
1413 if ((function.token_pos() == start_pos)
1414 && (function.end_token_pos() == end_pos)
1415 && (function.script() == script.raw())) {
1416 function_list->Add(function);
1417 if (function.HasImplicitClosureFunction()) {
1418 function = function.ImplicitClosureFunction();
1419 function_list->Add(function);
1420 }
1421 }
1422 }
1423 }
1424 closures = cls.closures();
1425 if (!closures.IsNull()) {
1426 const intptr_t num_closures = closures.Length();
1427 for (intptr_t pos = 0; pos < num_closures; pos++) {
1428 function ^= closures.At(pos);
1429 ASSERT(!function.IsNull());
1430 if ((function.token_pos() == start_pos)
1431 && (function.end_token_pos() == end_pos)
1432 && (function.script() == script.raw())) {
1433 function_list->Add(function);
1434 if (function.HasImplicitClosureFunction()) {
1435 function = function.ImplicitClosureFunction();
1436 function_list->Add(function);
1437 }
1438 }
1439 }
1440 }
1441 }
1442 }
1443 }
1444
1445
1446 static void SelectBestFit(Function* best_fit, Function* func) {
1447 if (best_fit->IsNull()) {
1448 *best_fit = func->raw();
1449 }
1450 if (func->token_pos() > best_fit->token_pos()) {
1451 if (func->end_token_pos() <= best_fit->end_token_pos()) {
1452 // func is contained within best_fit. Select it even if it
1453 // has not been compiled yet.
1454 *best_fit = func->raw();
1455 if (func->HasImplicitClosureFunction()) {
1456 *func = func->ImplicitClosureFunction();
1457 if (func->HasCode()) {
1458 *best_fit = func->raw();
1459 }
1460 }
1461 }
1462 } else if ((func->token_pos() == best_fit->token_pos())
1463 && (func->end_token_pos() == best_fit->end_token_pos())
1464 && func->HasCode()) {
1465 // If func covers the same range, it is considered a better fit if
1466 // it has been compiled.
1467 *best_fit = func->raw();
1468 }
1469 }
1470
1471
1472 RawFunction* Debugger::FindBestFit(const Script& script,
1473 intptr_t token_pos) {
1474 Class& cls = Class::Handle(isolate_);
1475 Array& functions = Array::Handle(isolate_);
1476 GrowableObjectArray& closures = GrowableObjectArray::Handle(isolate_);
1477 Function& function = Function::Handle(isolate_);
1478 Function& best_fit = Function::Handle(isolate_);
1479
1480 const ClassTable& class_table = *isolate_->class_table();
1481 const intptr_t num_classes = class_table.NumCids();
1482 for (intptr_t i = 1; i < num_classes; i++) {
1483 if (class_table.HasValidClassAt(i)) {
1484 cls = class_table.At(i);
1485 // Note: we need to check the functions of this class even if
1486 // the class is defined in a differenct 'script'. There could
1487 // be mixin functions from the given script in this class.
1488 functions = cls.functions();
1489 if (!functions.IsNull()) {
1490 const intptr_t num_functions = functions.Length();
1491 for (intptr_t pos = 0; pos < num_functions; pos++) {
1492 function ^= functions.At(pos);
1493 ASSERT(!function.IsNull());
1494 if (FunctionContains(function, script, token_pos)) {
1495 SelectBestFit(&best_fit, &function);
1496 }
1497 }
1498 }
1499
1500 closures = cls.closures();
1501 if (!closures.IsNull()) {
1502 const intptr_t num_closures = closures.Length();
1503 for (intptr_t pos = 0; pos < num_closures; pos++) {
1504 function ^= closures.At(pos);
1505 ASSERT(!function.IsNull());
1506 if (FunctionContains(function, script, token_pos)) {
1507 SelectBestFit(&best_fit, &function);
1508 }
1509 }
1510 }
1511 }
1512 }
1513 return best_fit.raw();
1514 }
1515
1516
1517 SourceBreakpoint* Debugger::SetBreakpoint(const Script& script,
1518 intptr_t token_pos) {
1519 Function& func = Function::Handle(isolate_);
1520 func = FindBestFit(script, token_pos);
1521 if (func.IsNull()) {
1373 return NULL; 1522 return NULL;
1374 } 1523 }
1375 intptr_t breakpoint_pos = -1; 1524 if (!func.IsNull() && func.HasCode()) {
1376 Function& closure = Function::Handle(isolate_); 1525 // A function containing this breakpoint location has already
1377 if (target_function.HasImplicitClosureFunction()) { 1526 // been compiled. We can resolve the breakpoint now.
1378 // There is a closurized version of this function.
1379 closure = target_function.ImplicitClosureFunction();
1380 }
1381 // Determine actual breakpoint location if the function or an
1382 // implicit closure of the function has been compiled already.
1383 if (target_function.HasCode()) {
1384 DeoptimizeWorld(); 1527 DeoptimizeWorld();
1385 ASSERT(!target_function.HasOptimizedCode()); 1528 intptr_t breakpoint_pos = ResolveBreakpointPos(func, token_pos);
1386 breakpoint_pos = 1529 if (breakpoint_pos >= 0) {
1387 ResolveBreakpointPos(target_function, first_token_pos, last_token_pos); 1530 SourceBreakpoint* bpt = GetSourceBreakpoint(script, breakpoint_pos);
1388 } else if (!closure.IsNull() && closure.HasCode()) { 1531 if (bpt != NULL) {
1389 DeoptimizeWorld(); 1532 // A source breakpoint for this location already exists.
1390 ASSERT(!closure.HasOptimizedCode()); 1533 return bpt;
1391 breakpoint_pos = 1534 }
1392 ResolveBreakpointPos(closure, first_token_pos, last_token_pos); 1535 bpt = new SourceBreakpoint(nextId(), script, token_pos);
1393 } else { 1536 bpt->SetResolved(func, breakpoint_pos);
1394 // This function has not been compiled yet. Set a pending 1537 RegisterSourceBreakpoint(bpt);
1395 // breakpoint to be resolved later. 1538 // There may be more than one function object for a given function
1396 SourceBreakpoint* source_bpt = 1539 // in source code. There may be implicit closure functions, and
1397 GetSourceBreakpoint(target_function, first_token_pos); 1540 // there may be copies of mixin functions. Collect all functions whose
1398 if (source_bpt != NULL) { 1541 // source code range matches exactly the best fit function we
1399 // A pending source breakpoint for this uncompiled location 1542 // found.
1400 // already exists. 1543 GrowableObjectArray& functions =
1401 if (FLAG_verbose_debug) { 1544 GrowableObjectArray::Handle(GrowableObjectArray::New());
1402 OS::Print("Pending breakpoint for uncompiled function" 1545 FindEquivalentFunctions(script,
1403 " '%s' at line %" Pd " already exists\n", 1546 func.token_pos(),
1404 target_function.ToFullyQualifiedCString(), 1547 func.end_token_pos(),
1405 source_bpt->LineNumber()); 1548 &functions);
1406 } 1549 const intptr_t num_functions = functions.Length();
1407 return source_bpt; 1550 // We must have found at least one function: func.
1408 } 1551 ASSERT(num_functions > 0);
1409 source_bpt = 1552 // Create code breakpoints for all compiled functions we found.
1410 new SourceBreakpoint(nextId(), target_function, first_token_pos); 1553 for (intptr_t i = 0; i < num_functions; i++) {
1411 RegisterSourceBreakpoint(source_bpt); 1554 func ^= functions.At(i);
1412 if (FLAG_verbose_debug) { 1555 if (func.HasCode()) {
1413 OS::Print("Registering pending breakpoint for " 1556 MakeCodeBreakpointsAt(func, bpt);
1414 "uncompiled function '%s' at line %" Pd "\n", 1557 }
1415 target_function.ToFullyQualifiedCString(), 1558 }
1416 source_bpt->LineNumber()); 1559 bpt->Enable();
1417 } 1560 SignalBpResolved(bpt);
1418 source_bpt->Enable(); 1561 return bpt;
1419 return source_bpt; 1562 }
1420 } 1563 }
1421 ASSERT(breakpoint_pos != -1); 1564 // There is no compiled function at this token position.
1422 SourceBreakpoint* source_bpt = 1565 // Register an unresolved breakpoint.
1423 GetSourceBreakpoint(target_function, breakpoint_pos); 1566 if (FLAG_verbose_debug && !func.IsNull()) {
1424 if (source_bpt != NULL) { 1567 intptr_t line_number;
1425 // A source breakpoint for this location already exists. 1568 script.GetTokenLocation(token_pos, &line_number, NULL);
1426 return source_bpt; 1569 OS::Print("Registering pending breakpoint for "
1427 } 1570 "uncompiled function '%s' at line %" Pd "\n",
1428 source_bpt = new SourceBreakpoint(nextId(), target_function, breakpoint_pos); 1571 func.ToFullyQualifiedCString(),
1429 RegisterSourceBreakpoint(source_bpt); 1572 line_number);
1430 if (target_function.HasCode()) { 1573 }
1431 MakeCodeBreakpointsAt(target_function, breakpoint_pos, source_bpt); 1574 SourceBreakpoint* bpt = GetSourceBreakpoint(script, token_pos);
1432 } 1575 if (bpt == NULL) {
1433 if (!closure.IsNull() && closure.HasCode()) { 1576 bpt = new SourceBreakpoint(nextId(), script, token_pos);
1434 MakeCodeBreakpointsAt(closure, breakpoint_pos, source_bpt); 1577 }
1435 } 1578 RegisterSourceBreakpoint(bpt);
1436 source_bpt->Enable(); 1579 bpt->Enable();
1437 SignalBpResolved(source_bpt); 1580 return bpt;
1438 return source_bpt; 1581 }
1439 } 1582
1440 1583
1441
1442 // Synchronize the enabled/disabled state of all code breakpoints 1584 // Synchronize the enabled/disabled state of all code breakpoints
1443 // associated with the source breakpoint bpt. 1585 // associated with the source breakpoint bpt.
1444 void Debugger::SyncBreakpoint(SourceBreakpoint* bpt) { 1586 void Debugger::SyncBreakpoint(SourceBreakpoint* bpt) {
1445 CodeBreakpoint* cbpt = code_breakpoints_; 1587 CodeBreakpoint* cbpt = code_breakpoints_;
1446 while (cbpt != NULL) { 1588 while (cbpt != NULL) {
1447 if (bpt == cbpt->src_bpt()) { 1589 if (bpt == cbpt->src_bpt()) {
1448 if (bpt->IsEnabled()) { 1590 if (bpt->IsEnabled()) {
1449 cbpt->Enable(); 1591 cbpt->Enable();
1450 } else { 1592 } else {
1451 cbpt->Disable(); 1593 cbpt->Disable();
(...skipping 10 matching lines...) Expand all
1462 const Function& closure_func = 1604 const Function& closure_func =
1463 Function::Handle(target_function.ImplicitClosureFunction()); 1605 Function::Handle(target_function.ImplicitClosureFunction());
1464 InstrumentForStepping(closure_func); 1606 InstrumentForStepping(closure_func);
1465 } 1607 }
1466 } 1608 }
1467 1609
1468 1610
1469 SourceBreakpoint* Debugger::SetBreakpointAtEntry( 1611 SourceBreakpoint* Debugger::SetBreakpointAtEntry(
1470 const Function& target_function) { 1612 const Function& target_function) {
1471 ASSERT(!target_function.IsNull()); 1613 ASSERT(!target_function.IsNull());
1472 return SetBreakpoint(target_function, 1614 const Script& script = Script::Handle(target_function.script());
1473 target_function.token_pos(), 1615 return SetBreakpoint(script, target_function.token_pos());
1474 target_function.end_token_pos());
1475 } 1616 }
1476 1617
1477 1618
1478 SourceBreakpoint* Debugger::SetBreakpointAtLine(const String& script_url, 1619 SourceBreakpoint* Debugger::SetBreakpointAtLine(const String& script_url,
1479 intptr_t line_number) { 1620 intptr_t line_number) {
1480 Library& lib = Library::Handle(isolate_); 1621 Library& lib = Library::Handle(isolate_);
1481 Script& script = Script::Handle(isolate_); 1622 Script& script = Script::Handle(isolate_);
1482 const GrowableObjectArray& libs = 1623 const GrowableObjectArray& libs =
1483 GrowableObjectArray::Handle(isolate_->object_store()->libraries()); 1624 GrowableObjectArray::Handle(isolate_->object_store()->libraries());
1484 for (intptr_t i = 0; i < libs.Length(); i++) { 1625 for (intptr_t i = 0; i < libs.Length(); i++) {
(...skipping 13 matching lines...) Expand all
1498 intptr_t first_token_idx, last_token_idx; 1639 intptr_t first_token_idx, last_token_idx;
1499 script.TokenRangeAtLine(line_number, &first_token_idx, &last_token_idx); 1640 script.TokenRangeAtLine(line_number, &first_token_idx, &last_token_idx);
1500 if (first_token_idx < 0) { 1641 if (first_token_idx < 0) {
1501 // Script does not contain the given line number. 1642 // Script does not contain the given line number.
1502 if (FLAG_verbose_debug) { 1643 if (FLAG_verbose_debug) {
1503 OS::Print("Script '%s' does not contain line number %" Pd "\n", 1644 OS::Print("Script '%s' does not contain line number %" Pd "\n",
1504 script_url.ToCString(), line_number); 1645 script_url.ToCString(), line_number);
1505 } 1646 }
1506 return NULL; 1647 return NULL;
1507 } else if (last_token_idx < 0) { 1648 } else if (last_token_idx < 0) {
1508 // Line does not contain any tokens. first_token_index is the first 1649 // Line does not contain any tokens.
1509 // token after the given line. We check whether that token is
1510 // part of a function.
1511 last_token_idx = first_token_idx;
1512 }
1513
1514 Function& func = Function::Handle(isolate_);
1515 while (first_token_idx <= last_token_idx) {
1516 func = lib.LookupFunctionInScript(script, first_token_idx);
1517 if (!func.IsNull()) {
1518 break;
1519 }
1520 first_token_idx++;
1521 }
1522 if (func.IsNull()) {
1523 if (FLAG_verbose_debug) { 1650 if (FLAG_verbose_debug) {
1524 OS::Print("No executable code at line %" Pd " in '%s'\n", 1651 OS::Print("No executable code at line %" Pd " in '%s'\n",
1525 line_number, script_url.ToCString()); 1652 line_number, script_url.ToCString());
1526 } 1653 }
1527 return NULL; 1654 return NULL;
1528 } 1655 }
1529 if (last_token_idx < 0) { 1656
1530 // The token at first_token_index is past the requested source line. 1657 SourceBreakpoint* bpt = NULL;
1531 // Set the breakpoint at the closest position after that line. 1658 ASSERT(first_token_idx <= last_token_idx);
1532 last_token_idx = func.end_token_pos(); 1659 while ((bpt == NULL) && (first_token_idx <= last_token_idx)) {
1660 bpt = SetBreakpoint(script, first_token_idx);
1661 first_token_idx++;
1533 } 1662 }
1534 return SetBreakpoint(func, first_token_idx, last_token_idx); 1663 if ((bpt == NULL) && FLAG_verbose_debug) {
1664 OS::Print("No executable code at line %" Pd " in '%s'\n",
1665 line_number, script_url.ToCString());
1666 }
1667 return bpt;
1535 } 1668 }
1536 1669
1537 1670
1538 intptr_t Debugger::CacheObject(const Object& obj) { 1671 intptr_t Debugger::CacheObject(const Object& obj) {
1539 ASSERT(obj_cache_ != NULL); 1672 ASSERT(obj_cache_ != NULL);
1540 return obj_cache_->AddObject(obj); 1673 return obj_cache_->AddObject(obj);
1541 } 1674 }
1542 1675
1543 1676
1544 bool Debugger::IsValidObjectId(intptr_t obj_id) { 1677 bool Debugger::IsValidObjectId(intptr_t obj_id) {
(...skipping 371 matching lines...) Expand 10 before | Expand all | Expand 10 after
1916 // 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
1917 // debugger wire protocol messages. 2050 // debugger wire protocol messages.
1918 isolate_id_ = isolate->main_port(); 2051 isolate_id_ = isolate->main_port();
1919 initialized_ = true; 2052 initialized_ = true;
1920 2053
1921 // Signal isolate creation event. 2054 // Signal isolate creation event.
1922 SignalIsolateEvent(Debugger::kIsolateCreated); 2055 SignalIsolateEvent(Debugger::kIsolateCreated);
1923 } 2056 }
1924 2057
1925 2058
1926 static RawFunction* GetOriginalFunction(const Function& func) { 2059 // Return innermost closure contained in 'function' that contains
1927 if (func.IsClosureFunction()) { 2060 // the given token position.
1928 // Local functions (closures) in mixin functions do not have 2061 RawFunction* Debugger::FindInnermostClosure(const Function& function,
1929 // an original function they were cloned from. 2062 intptr_t token_pos) {
1930 // Note: this is a problem when a breakpoint is set in a mixed-in 2063 const Class& owner = Class::Handle(isolate_, function.Owner());
1931 // closure. The breakpoint is linked to the closure attached to the 2064 if (owner.closures() == GrowableObjectArray::null()) {
1932 // mixin application class, not the mixin class. When the same 2065 return Function::null();
1933 // closure is compiled for another mixin application class, we
1934 // don't find the breakpoint since we'll be looking in the
1935 // mixin class.
1936 return func.raw();
1937 } 2066 }
1938 const Class& origin_class = Class::Handle(func.origin()); 2067 // Note that we need to check that the closure is in the same
1939 if (origin_class.is_patch()) { 2068 // script as the outer function. We could have closures originating
1940 // Patched functions from patch classes are removed from the 2069 // in mixin classes whose source code is contained in a different
1941 // function array of the patch class, so we will not find an 2070 // script.
1942 // original function object. 2071 const Script& outer_origin = Script::Handle(isolate_, function.script());
1943 return func.raw(); 2072 const GrowableObjectArray& closures =
1944 } 2073 GrowableObjectArray::Handle(isolate_, owner.closures());
1945 const Array& functions = Array::Handle(origin_class.functions()); 2074 const intptr_t num_closures = closures.Length();
1946 Object& orig_func = Object::Handle(); 2075 Function& closure = Function::Handle(isolate_);
1947 for (intptr_t i = 0; i < functions.Length(); i++) { 2076 Function& best_fit = Function::Handle(isolate_);
1948 orig_func = functions.At(i); 2077 for (intptr_t i = 0; i < num_closures; i++) {
1949 // Function names are symbols, so we can compare the raw pointers. 2078 closure ^= closures.At(i);
1950 if (func.name() == Function::Cast(orig_func).name()) { 2079 if ((function.token_pos() < closure.token_pos()) &&
1951 return Function::Cast(orig_func).raw(); 2080 (closure.end_token_pos() < function.end_token_pos()) &&
2081 (closure.token_pos() <= token_pos) &&
2082 (token_pos <= closure.end_token_pos()) &&
2083 (closure.script() == outer_origin.raw())) {
2084 SelectBestFit(&best_fit, &closure);
1952 } 2085 }
1953 } 2086 }
1954 // Uncommon case: not a mixin function. 2087 return best_fit.raw();
1955 ASSERT(!Class::Handle(func.Owner()).IsMixinApplication());
1956 return func.raw();
1957 } 2088 }
1958 2089
1959 2090
1960 void Debugger::NotifyCompilation(const Function& func) { 2091 void Debugger::NotifyCompilation(const Function& func) {
1961 if (src_breakpoints_ == NULL) { 2092 if (src_breakpoints_ == NULL) {
1962 // Return with minimal overhead if there are no breakpoints. 2093 // Return with minimal overhead if there are no breakpoints.
1963 return; 2094 return;
1964 } 2095 }
1965 Function& lookup_function = Function::Handle(func.raw()); 2096 // Iterate over all source breakpoints to check whether breakpoints
1966 if (func.IsImplicitClosureFunction()) { 2097 // need to be set in the newly compiled function.
1967 // If the newly compiled function is a an implicit closure (a closure that 2098 Script& script = Script::Handle(isolate_);
1968 // was formed by assigning a static or instance method to a function 2099 for (SourceBreakpoint* bpt = src_breakpoints_;
1969 // object), we need to use the closure's parent function to see whether 2100 bpt != NULL;
1970 // there are any breakpoints. The parent function is the actual method on 2101 bpt = bpt->next()) {
1971 // which the user sets breakpoints. 2102 script = bpt->script();
1972 lookup_function = func.parent_function(); 2103 if (FunctionContains(func, script, bpt->token_pos())) {
1973 ASSERT(!lookup_function.IsNull()); 2104 Function& inner_function = Function::Handle(isolate_);
1974 } 2105 inner_function = FindInnermostClosure(func, bpt->token_pos());
1975 if (lookup_function.Owner() != lookup_function.origin()) { 2106 if (!inner_function.IsNull()) {
1976 // This is a cloned function from a mixin class. If a breakpoint 2107 // The local function of a function we just compiled cannot
1977 // was set in this function, it is registered using the function 2108 // be compiled already.
1978 // of the origin class. 2109 ASSERT(!inner_function.HasCode());
1979 lookup_function = GetOriginalFunction(lookup_function);
1980 }
1981 SourceBreakpoint* bpt = src_breakpoints_;
1982 while (bpt != NULL) {
1983 if (lookup_function.raw() == bpt->function()) {
1984 // Check if the breakpoint is inside a closure or local function
1985 // within the newly compiled function. Note that we must look
1986 // in the owner class of the function that just got compiled
1987 // (i.e. func), not the owner class of the function we use to
1988 // record the breakpoint (lookup_function).
1989 Class& owner = Class::Handle(func.Owner());
1990 Function& closure =
1991 Function::Handle(owner.LookupClosureFunction(bpt->token_pos()));
1992 if (!closure.IsNull() && (closure.raw() != lookup_function.raw())) {
1993 if (FLAG_verbose_debug) { 2110 if (FLAG_verbose_debug) {
1994 OS::Print("Resetting pending breakpoint to function %s\n", 2111 OS::Print("Pending BP remains unresolved in inner function '%s'\n",
1995 closure.ToFullyQualifiedCString()); 2112 inner_function.ToFullyQualifiedCString());
1996 } 2113 }
1997 bpt->set_function(closure); 2114 continue;
1998 } else { 2115 }
2116
2117 // TODO(hausner): What should we do if function is optimized?
2118 // Can we deoptimize the function?
2119 ASSERT(!func.HasOptimizedCode());
2120
2121 // There is no local function within func that contains the
2122 // breakpoint token position. Resolve the breakpoint if necessary
2123 // and set the code breakpoints.
2124 if (!bpt->IsResolved()) {
2125 // Resolve source breakpoint in the newly compiled function.
2126 intptr_t bp_pos = ResolveBreakpointPos(func, bpt->token_pos());
2127 if (bp_pos < 0) {
2128 if (FLAG_verbose_debug) {
2129 OS::Print("Failed resolving breakpoint for function '%s'\n",
2130 String::Handle(func.name()).ToCString());
2131 }
2132 continue;
2133 }
2134 intptr_t requested_pos = bpt->token_pos();
2135 bpt->SetResolved(func, bp_pos);
1999 if (FLAG_verbose_debug) { 2136 if (FLAG_verbose_debug) {
2000 OS::Print("Enable pending breakpoint for function '%s'\n", 2137 OS::Print("Resolved BP %" Pd " to pos %" Pd ", line %" Pd ", "
2001 String::Handle(lookup_function.name()).ToCString()); 2138 "function '%s' (requested pos %" Pd ")\n",
2139 bpt->id(),
2140 bpt->token_pos(),
2141 bpt->LineNumber(),
2142 func.ToFullyQualifiedCString(),
2143 requested_pos);
2002 } 2144 }
2003 const Script& script= Script::Handle(func.script());
2004 intptr_t first_pos, last_pos;
2005 script.TokenRangeAtLine(bpt->LineNumber(), &first_pos, &last_pos);
2006 intptr_t bp_pos =
2007 ResolveBreakpointPos(func, bpt->token_pos(), last_pos);
2008 bpt->set_token_pos(bp_pos);
2009 MakeCodeBreakpointsAt(func, bp_pos, bpt);
2010 SignalBpResolved(bpt); 2145 SignalBpResolved(bpt);
2011 } 2146 }
2012 bpt->Enable(); // Enables the code breakpoint as well. 2147 ASSERT(bpt->IsResolved());
2148 if (FLAG_verbose_debug) {
2149 OS::Print("Setting breakpoint %" Pd " at line %" Pd " for %s '%s'\n",
2150 bpt->id(),
2151 bpt->LineNumber(),
2152 func.IsClosureFunction() ? "closure" : "function",
2153 String::Handle(func.name()).ToCString());
2154 }
2155 MakeCodeBreakpointsAt(func, bpt);
2013 } 2156 }
2014 bpt = bpt->next();
2015 } 2157 }
2016 } 2158 }
2017 2159
2018 2160
2019 CodeBreakpoint* Debugger::GetCodeBreakpoint(uword breakpoint_address) { 2161 CodeBreakpoint* Debugger::GetCodeBreakpoint(uword breakpoint_address) {
2020 CodeBreakpoint* bpt = code_breakpoints_; 2162 CodeBreakpoint* bpt = code_breakpoints_;
2021 while (bpt != NULL) { 2163 while (bpt != NULL) {
2022 if (bpt->pc() == breakpoint_address) { 2164 if (bpt->pc() == breakpoint_address) {
2023 return bpt; 2165 return bpt;
2024 } 2166 }
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
2098 temp_bpt->Disable(); 2240 temp_bpt->Disable();
2099 delete temp_bpt; 2241 delete temp_bpt;
2100 } else { 2242 } else {
2101 prev_bpt = curr_bpt; 2243 prev_bpt = curr_bpt;
2102 curr_bpt = curr_bpt->next(); 2244 curr_bpt = curr_bpt->next();
2103 } 2245 }
2104 } 2246 }
2105 } 2247 }
2106 2248
2107 2249
2108 SourceBreakpoint* Debugger::GetSourceBreakpoint(const Function& func, 2250 SourceBreakpoint* Debugger::GetSourceBreakpoint(const Script& script,
2109 intptr_t token_pos) { 2251 intptr_t token_pos) {
2110 SourceBreakpoint* bpt = src_breakpoints_; 2252 SourceBreakpoint* bpt = src_breakpoints_;
2111 while (bpt != NULL) { 2253 while (bpt != NULL) {
2112 if ((bpt->function() == func.raw()) && 2254 if ((bpt->script_ == script.raw()) && (bpt->token_pos_ == token_pos)) {
2113 (bpt->token_pos() == token_pos)) {
2114 return bpt; 2255 return bpt;
2115 } 2256 }
2116 bpt = bpt->next(); 2257 bpt = bpt->next();
2117 } 2258 }
2118 return NULL; 2259 return NULL;
2119 } 2260 }
2120 2261
2121 2262
2122 SourceBreakpoint* Debugger::GetBreakpointById(intptr_t id) { 2263 SourceBreakpoint* Debugger::GetBreakpointById(intptr_t id) {
2123 SourceBreakpoint* bpt = src_breakpoints_; 2264 SourceBreakpoint* bpt = src_breakpoints_;
(...skipping 14 matching lines...) Expand all
2138 } 2279 }
2139 2280
2140 2281
2141 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) { 2282 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) {
2142 ASSERT(bpt->next() == NULL); 2283 ASSERT(bpt->next() == NULL);
2143 bpt->set_next(code_breakpoints_); 2284 bpt->set_next(code_breakpoints_);
2144 code_breakpoints_ = bpt; 2285 code_breakpoints_ = bpt;
2145 } 2286 }
2146 2287
2147 } // namespace dart 2288 } // namespace dart
OLDNEW
« no previous file with comments | « 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