OLD | NEW |
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 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
57 SourceBreakpoint::SourceBreakpoint(intptr_t id, | 57 SourceBreakpoint::SourceBreakpoint(intptr_t id, |
58 const Script& script, | 58 const Script& script, |
59 intptr_t token_pos, | 59 intptr_t token_pos, |
60 intptr_t end_token_pos) | 60 intptr_t end_token_pos) |
61 : id_(id), | 61 : id_(id), |
62 script_(script.raw()), | 62 script_(script.raw()), |
63 token_pos_(token_pos), | 63 token_pos_(token_pos), |
64 end_token_pos_(end_token_pos), | 64 end_token_pos_(end_token_pos), |
65 is_resolved_(false), | 65 is_resolved_(false), |
66 is_enabled_(false), | 66 is_enabled_(false), |
| 67 is_one_shot_(false), |
67 next_(NULL), | 68 next_(NULL), |
68 function_(Function::null()), | 69 function_(Function::null()), |
69 line_number_(-1) { | 70 line_number_(-1) { |
70 ASSERT(id_ > 0); | 71 ASSERT(id_ > 0); |
71 ASSERT(!script.IsNull()); | 72 ASSERT(!script.IsNull()); |
72 ASSERT(token_pos_ >= 0); | 73 ASSERT(token_pos_ >= 0); |
73 } | 74 } |
74 | 75 |
75 | 76 |
76 void SourceBreakpoint::Enable() { | 77 void SourceBreakpoint::Enable() { |
(...skipping 1054 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1131 if (function.HasOptimizedCode()) { | 1132 if (function.HasOptimizedCode()) { |
1132 function.SwitchToUnoptimizedCode(); | 1133 function.SwitchToUnoptimizedCode(); |
1133 } | 1134 } |
1134 } | 1135 } |
1135 } | 1136 } |
1136 } | 1137 } |
1137 } | 1138 } |
1138 } | 1139 } |
1139 | 1140 |
1140 | 1141 |
1141 RawError* Debugger::SetInternalBreakpoints(const Function& target_function) { | |
1142 if (target_function.is_native()) { | |
1143 // Can't instrument native functions. Fail silently. | |
1144 return Error::null(); | |
1145 } | |
1146 Isolate* isolate = Isolate::Current(); | |
1147 if (!target_function.HasCode()) { | |
1148 const Error& error = Error::Handle( | |
1149 Compiler::CompileFunction(isolate, target_function)); | |
1150 if (!error.IsNull()) { | |
1151 return error.raw(); | |
1152 } | |
1153 } | |
1154 // Hang on to the code object before deoptimizing, in case deoptimization | |
1155 // might cause the GC to run. | |
1156 Code& code = Code::Handle(isolate, target_function.unoptimized_code()); | |
1157 ASSERT(!code.IsNull()); | |
1158 DeoptimizeWorld(); | |
1159 ASSERT(!target_function.HasOptimizedCode()); | |
1160 PcDescriptors& desc = PcDescriptors::Handle(isolate, code.pc_descriptors()); | |
1161 PcDescriptors::Iterator iter(desc, kSafepointKind); | |
1162 while (iter.MoveNext()) { | |
1163 if (iter.TokenPos() != Scanner::kNoSourcePos) { | |
1164 CodeBreakpoint* bpt = GetCodeBreakpoint(iter.Pc()); | |
1165 if (bpt != NULL) { | |
1166 // There is already a breakpoint for this address. Make sure | |
1167 // it is enabled. | |
1168 bpt->Enable(); | |
1169 continue; | |
1170 } | |
1171 bpt = new CodeBreakpoint(code, iter.TokenPos(), | |
1172 iter.Pc(), iter.Kind()); | |
1173 RegisterCodeBreakpoint(bpt); | |
1174 bpt->Enable(); | |
1175 } | |
1176 } | |
1177 return Error::null(); | |
1178 } | |
1179 | |
1180 | |
1181 void Debugger::SignalBpResolved(SourceBreakpoint* bpt) { | 1142 void Debugger::SignalBpResolved(SourceBreakpoint* bpt) { |
1182 if (HasEventHandler()) { | 1143 if (HasEventHandler() && !bpt->IsOneShot()) { |
1183 DebuggerEvent event(isolate_, DebuggerEvent::kBreakpointResolved); | 1144 DebuggerEvent event(isolate_, DebuggerEvent::kBreakpointResolved); |
1184 event.set_breakpoint(bpt); | 1145 event.set_breakpoint(bpt); |
1185 InvokeEventHandler(&event); | 1146 InvokeEventHandler(&event); |
1186 } | 1147 } |
1187 } | 1148 } |
1188 | 1149 |
1189 | 1150 |
1190 ActivationFrame* Debugger::CollectDartFrame(Isolate* isolate, | 1151 ActivationFrame* Debugger::CollectDartFrame(Isolate* isolate, |
1191 uword pc, | 1152 uword pc, |
1192 StackFrame* frame, | 1153 StackFrame* frame, |
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1411 } | 1372 } |
1412 DebuggerEvent event(isolate_, DebuggerEvent::kExceptionThrown); | 1373 DebuggerEvent event(isolate_, DebuggerEvent::kExceptionThrown); |
1413 event.set_exception(&exc); | 1374 event.set_exception(&exc); |
1414 ASSERT(stack_trace_ == NULL); | 1375 ASSERT(stack_trace_ == NULL); |
1415 stack_trace_ = stack_trace; | 1376 stack_trace_ = stack_trace; |
1416 Pause(&event); | 1377 Pause(&event); |
1417 stack_trace_ = NULL; | 1378 stack_trace_ = NULL; |
1418 } | 1379 } |
1419 | 1380 |
1420 | 1381 |
| 1382 static intptr_t LastTokenOnLine(const TokenStream& tokens, intptr_t pos) { |
| 1383 TokenStream::Iterator iter(tokens, pos, TokenStream::Iterator::kAllTokens); |
| 1384 ASSERT(iter.IsValid()); |
| 1385 intptr_t last_pos = pos; |
| 1386 while ((iter.CurrentTokenKind() != Token::kNEWLINE) && |
| 1387 (iter.CurrentTokenKind() != Token::kEOS)) { |
| 1388 last_pos = iter.CurrentPosition(); |
| 1389 iter.Advance(); |
| 1390 } |
| 1391 return last_pos; |
| 1392 } |
| 1393 |
| 1394 |
1421 // Given a function and a token range, return the best fit | 1395 // Given a function and a token range, return the best fit |
1422 // token position to set a breakpoint. The best fit is the safe point | 1396 // token position to set a breakpoint. The best fit is the safe point |
1423 // with the lowest compiled code address within the token range. | 1397 // in the line closest to the beginning of the token range, and within |
| 1398 // that line, the safe point with the lowest compiled code address. |
1424 intptr_t Debugger::ResolveBreakpointPos(const Function& func, | 1399 intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
1425 intptr_t requested_token_pos, | 1400 intptr_t requested_token_pos, |
1426 intptr_t last_token_pos) { | 1401 intptr_t last_token_pos) { |
1427 ASSERT(func.HasCode()); | 1402 ASSERT(func.HasCode()); |
1428 ASSERT(!func.HasOptimizedCode()); | 1403 ASSERT(!func.HasOptimizedCode()); |
1429 | 1404 |
1430 if (requested_token_pos < func.token_pos()) { | 1405 if (requested_token_pos < func.token_pos()) { |
1431 requested_token_pos = func.token_pos(); | 1406 requested_token_pos = func.token_pos(); |
1432 } | 1407 } |
1433 if (last_token_pos > func.end_token_pos()) { | 1408 if (last_token_pos > func.end_token_pos()) { |
1434 last_token_pos = func.end_token_pos(); | 1409 last_token_pos = func.end_token_pos(); |
1435 } | 1410 } |
1436 | 1411 |
1437 Code& code = Code::Handle(func.unoptimized_code()); | 1412 Code& code = Code::Handle(func.unoptimized_code()); |
1438 ASSERT(!code.IsNull()); | 1413 ASSERT(!code.IsNull()); |
1439 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); | 1414 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
| 1415 |
| 1416 // First pass: find the safe point which is closest to the beginning |
| 1417 // of the given token range. |
1440 intptr_t best_fit_pos = INT_MAX; | 1418 intptr_t best_fit_pos = INT_MAX; |
1441 uword lowest_pc = kUwordMax; | |
1442 intptr_t lowest_pc_token_pos = INT_MAX; | |
1443 PcDescriptors::Iterator iter(desc, kSafepointKind); | 1419 PcDescriptors::Iterator iter(desc, kSafepointKind); |
1444 while (iter.MoveNext()) { | 1420 while (iter.MoveNext()) { |
1445 const intptr_t desc_token_pos = iter.TokenPos(); | 1421 const intptr_t desc_token_pos = iter.TokenPos(); |
1446 ASSERT(desc_token_pos >= 0); | 1422 if ((desc_token_pos != Scanner::kNoSourcePos) && |
1447 if (desc_token_pos != Scanner::kNoSourcePos) { | 1423 (desc_token_pos < best_fit_pos) && |
1448 if ((desc_token_pos < requested_token_pos) || | 1424 (desc_token_pos >= requested_token_pos) && |
1449 (desc_token_pos > last_token_pos)) { | 1425 (desc_token_pos <= last_token_pos)) { |
1450 // This descriptor is outside the desired token range. | 1426 best_fit_pos = desc_token_pos; |
1451 continue; | 1427 } |
1452 } | 1428 } |
1453 if (desc_token_pos < best_fit_pos) { | 1429 // Second pass (if we found a safe point in the first pass): |
1454 // So far, this descriptor has the lowest token position after | 1430 // For all token positions on the same line, select the one |
1455 // the first acceptable token position. | 1431 // with the lowest compiled code address. E.g., in a line with |
1456 best_fit_pos = desc_token_pos; | 1432 // the nested function calls f(g(x)), the call g() will have a lower |
1457 } | 1433 // compiled code address but is not the lowest token position in the |
1458 if (iter.Pc() < lowest_pc) { | 1434 // line. |
1459 // This descriptor so far has the lowest code address. | 1435 if (best_fit_pos != INT_MAX) { |
| 1436 const Script& script = Script::Handle(func.script()); |
| 1437 const TokenStream& tokens = TokenStream::Handle(script.tokens()); |
| 1438 const intptr_t begin_pos = best_fit_pos; |
| 1439 const intptr_t end_of_line_pos = LastTokenOnLine(tokens, begin_pos); |
| 1440 uword lowest_pc = kUwordMax; |
| 1441 PcDescriptors::Iterator iter(desc, kSafepointKind); |
| 1442 while (iter.MoveNext()) { |
| 1443 const intptr_t pos = iter.TokenPos(); |
| 1444 if ((pos != Scanner::kNoSourcePos) && |
| 1445 (begin_pos <= pos) && (pos <= end_of_line_pos) && |
| 1446 (iter.Pc() < lowest_pc)) { |
1460 lowest_pc = iter.Pc(); | 1447 lowest_pc = iter.Pc(); |
1461 lowest_pc_token_pos = desc_token_pos; | 1448 best_fit_pos = pos; |
1462 } | 1449 } |
1463 } | 1450 } |
1464 } | |
1465 if (lowest_pc_token_pos != INT_MAX) { | |
1466 // We found the pc descriptor that has the lowest execution address. | |
1467 // This is the first possible breakpoint after the requested token | |
1468 // position. We use this instead of the nearest PC descriptor | |
1469 // measured in token index distance. | |
1470 return lowest_pc_token_pos; | |
1471 } | |
1472 if (best_fit_pos != INT_MAX) { | |
1473 return best_fit_pos; | 1451 return best_fit_pos; |
1474 } | 1452 } |
| 1453 |
1475 // We didn't find a safe point in the given token range. Try and find | 1454 // We didn't find a safe point in the given token range. Try and find |
1476 // a safe point in the remaining source code of the function. | 1455 // a safe point in the remaining source code of the function. |
1477 if (last_token_pos < func.end_token_pos()) { | 1456 if (last_token_pos < func.end_token_pos()) { |
1478 return ResolveBreakpointPos(func, last_token_pos, func.end_token_pos()); | 1457 return ResolveBreakpointPos(func, last_token_pos, func.end_token_pos()); |
1479 } | 1458 } |
1480 return -1; | 1459 return -1; |
1481 } | 1460 } |
1482 | 1461 |
1483 | 1462 |
1484 void Debugger::MakeCodeBreakpointAt(const Function& func, | 1463 void Debugger::MakeCodeBreakpointAt(const Function& func, |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1547 if (!functions.IsNull()) { | 1526 if (!functions.IsNull()) { |
1548 const intptr_t num_functions = functions.Length(); | 1527 const intptr_t num_functions = functions.Length(); |
1549 for (intptr_t pos = 0; pos < num_functions; pos++) { | 1528 for (intptr_t pos = 0; pos < num_functions; pos++) { |
1550 function ^= functions.At(pos); | 1529 function ^= functions.At(pos); |
1551 ASSERT(!function.IsNull()); | 1530 ASSERT(!function.IsNull()); |
1552 // Check token position first to avoid unnecessary calls | 1531 // Check token position first to avoid unnecessary calls |
1553 // to script() which allocates handles. | 1532 // to script() which allocates handles. |
1554 if ((function.token_pos() == start_pos) | 1533 if ((function.token_pos() == start_pos) |
1555 && (function.end_token_pos() == end_pos) | 1534 && (function.end_token_pos() == end_pos) |
1556 && (function.script() == script.raw())) { | 1535 && (function.script() == script.raw())) { |
1557 if (function.HasCode()) { | 1536 if (function.HasCode() && !function.IsAsyncFunction()) { |
1558 function_list->Add(function); | 1537 function_list->Add(function); |
1559 } | 1538 } |
1560 if (function.HasImplicitClosureFunction()) { | 1539 if (function.HasImplicitClosureFunction()) { |
1561 function = function.ImplicitClosureFunction(); | 1540 function = function.ImplicitClosureFunction(); |
1562 if (function.HasCode()) { | 1541 if (function.HasCode() && !function.IsAsyncFunction()) { |
1563 function_list->Add(function); | 1542 function_list->Add(function); |
1564 } | 1543 } |
1565 } | 1544 } |
1566 } | 1545 } |
1567 } | 1546 } |
1568 } | 1547 } |
1569 closures = cls.closures(); | 1548 closures = cls.closures(); |
1570 if (!closures.IsNull()) { | 1549 if (!closures.IsNull()) { |
1571 const intptr_t num_closures = closures.Length(); | 1550 const intptr_t num_closures = closures.Length(); |
1572 for (intptr_t pos = 0; pos < num_closures; pos++) { | 1551 for (intptr_t pos = 0; pos < num_closures; pos++) { |
1573 function ^= closures.At(pos); | 1552 function ^= closures.At(pos); |
1574 ASSERT(!function.IsNull()); | 1553 ASSERT(!function.IsNull()); |
1575 if ((function.token_pos() == start_pos) | 1554 if ((function.token_pos() == start_pos) |
1576 && (function.end_token_pos() == end_pos) | 1555 && (function.end_token_pos() == end_pos) |
1577 && (function.script() == script.raw())) { | 1556 && (function.script() == script.raw())) { |
1578 if (function.HasCode()) { | 1557 if (function.HasCode() && !function.IsAsyncFunction()) { |
1579 function_list->Add(function); | 1558 function_list->Add(function); |
1580 } | 1559 } |
1581 if (function.HasImplicitClosureFunction()) { | 1560 if (function.HasImplicitClosureFunction()) { |
1582 function = function.ImplicitClosureFunction(); | 1561 function = function.ImplicitClosureFunction(); |
1583 if (function.HasCode()) { | 1562 if (function.HasCode() && !function.IsAsyncFunction()) { |
1584 function_list->Add(function); | 1563 function_list->Add(function); |
1585 } | 1564 } |
1586 } | 1565 } |
1587 } | 1566 } |
1588 } | 1567 } |
1589 } | 1568 } |
1590 } | 1569 } |
1591 } | 1570 } |
1592 } | 1571 } |
1593 | 1572 |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1716 const intptr_t num_functions = functions.Length(); | 1695 const intptr_t num_functions = functions.Length(); |
1717 for (intptr_t i = 0; i < num_functions; i++) { | 1696 for (intptr_t i = 0; i < num_functions; i++) { |
1718 func ^= functions.At(i); | 1697 func ^= functions.At(i); |
1719 ASSERT(func.HasCode()); | 1698 ASSERT(func.HasCode()); |
1720 MakeCodeBreakpointAt(func, bpt); | 1699 MakeCodeBreakpointAt(func, bpt); |
1721 } | 1700 } |
1722 bpt->Enable(); | 1701 bpt->Enable(); |
1723 if (FLAG_verbose_debug) { | 1702 if (FLAG_verbose_debug) { |
1724 intptr_t line_number; | 1703 intptr_t line_number; |
1725 script.GetTokenLocation(breakpoint_pos, &line_number, NULL); | 1704 script.GetTokenLocation(breakpoint_pos, &line_number, NULL); |
1726 OS::Print("Resolved breakpoint for " | 1705 OS::Print("Resolved BP for " |
1727 "function '%s' at line %" Pd "\n", | 1706 "function '%s' at line %" Pd "\n", |
1728 func.ToFullyQualifiedCString(), | 1707 func.ToFullyQualifiedCString(), |
1729 line_number); | 1708 line_number); |
1730 } | 1709 } |
1731 SignalBpResolved(bpt); | 1710 SignalBpResolved(bpt); |
1732 return bpt; | 1711 return bpt; |
1733 } | 1712 } |
1734 } | 1713 } |
1735 // There is no compiled function at this token position. | 1714 // There is no compiled function at this token position. |
1736 // Register an unresolved breakpoint. | 1715 // Register an unresolved breakpoint. |
(...skipping 26 matching lines...) Expand all Loading... |
1763 } else { | 1742 } else { |
1764 cbpt->Disable(); | 1743 cbpt->Disable(); |
1765 } | 1744 } |
1766 } | 1745 } |
1767 cbpt = cbpt->next(); | 1746 cbpt = cbpt->next(); |
1768 } | 1747 } |
1769 } | 1748 } |
1770 | 1749 |
1771 | 1750 |
1772 RawError* Debugger::OneTimeBreakAtEntry(const Function& target_function) { | 1751 RawError* Debugger::OneTimeBreakAtEntry(const Function& target_function) { |
1773 Error& err = Error::Handle(); | 1752 SourceBreakpoint* bpt = SetBreakpointAtEntry(target_function); |
1774 err = SetInternalBreakpoints(target_function); | 1753 if (bpt != NULL) { |
1775 if (err.IsNull() && target_function.HasImplicitClosureFunction()) { | 1754 bpt->SetIsOneShot(); |
1776 const Function& closure_func = | |
1777 Function::Handle(target_function.ImplicitClosureFunction()); | |
1778 err = SetInternalBreakpoints(closure_func); | |
1779 } | 1755 } |
1780 return err.raw(); | 1756 return Error::null(); |
1781 } | 1757 } |
1782 | 1758 |
1783 | 1759 |
1784 SourceBreakpoint* Debugger::SetBreakpointAtEntry( | 1760 SourceBreakpoint* Debugger::SetBreakpointAtEntry( |
1785 const Function& target_function) { | 1761 const Function& target_function) { |
1786 ASSERT(!target_function.IsNull()); | 1762 ASSERT(!target_function.IsNull()); |
1787 const Script& script = Script::Handle(target_function.script()); | 1763 const Script& script = Script::Handle(target_function.script()); |
1788 return SetBreakpoint(script, | 1764 return SetBreakpoint(script, |
1789 target_function.token_pos(), | 1765 target_function.token_pos(), |
1790 target_function.end_token_pos()); | 1766 target_function.end_token_pos()); |
(...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2129 } | 2105 } |
2130 | 2106 |
2131 | 2107 |
2132 void Debugger::SignalPausedEvent(ActivationFrame* top_frame, | 2108 void Debugger::SignalPausedEvent(ActivationFrame* top_frame, |
2133 SourceBreakpoint* bpt) { | 2109 SourceBreakpoint* bpt) { |
2134 resume_action_ = kContinue; | 2110 resume_action_ = kContinue; |
2135 stepping_fp_ = 0; | 2111 stepping_fp_ = 0; |
2136 isolate_->set_single_step(false); | 2112 isolate_->set_single_step(false); |
2137 ASSERT(!IsPaused()); | 2113 ASSERT(!IsPaused()); |
2138 ASSERT(obj_cache_ == NULL); | 2114 ASSERT(obj_cache_ == NULL); |
| 2115 if ((bpt != NULL) && bpt->IsOneShot()) { |
| 2116 RemoveBreakpoint(bpt->id()); |
| 2117 bpt = NULL; |
| 2118 } |
2139 DebuggerEvent event(isolate_, DebuggerEvent::kBreakpointReached); | 2119 DebuggerEvent event(isolate_, DebuggerEvent::kBreakpointReached); |
2140 event.set_top_frame(top_frame); | 2120 event.set_top_frame(top_frame); |
2141 event.set_breakpoint(bpt); | 2121 event.set_breakpoint(bpt); |
2142 Pause(&event); | 2122 Pause(&event); |
2143 } | 2123 } |
2144 | 2124 |
2145 | 2125 |
2146 void Debugger::DebuggerStepCallback() { | 2126 void Debugger::DebuggerStepCallback() { |
2147 ASSERT(isolate_->single_step()); | 2127 ASSERT(isolate_->single_step()); |
2148 // We can't get here unless the debugger event handler enabled | 2128 // We can't get here unless the debugger event handler enabled |
(...skipping 348 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2497 } | 2477 } |
2498 | 2478 |
2499 | 2479 |
2500 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) { | 2480 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) { |
2501 ASSERT(bpt->next() == NULL); | 2481 ASSERT(bpt->next() == NULL); |
2502 bpt->set_next(code_breakpoints_); | 2482 bpt->set_next(code_breakpoints_); |
2503 code_breakpoints_ = bpt; | 2483 code_breakpoints_ = bpt; |
2504 } | 2484 } |
2505 | 2485 |
2506 } // namespace dart | 2486 } // namespace dart |
OLD | NEW |