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 // with the lowest compiled code address within the token range. |
1424 intptr_t Debugger::ResolveBreakpointPos(const Function& func, | 1398 intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
1425 intptr_t requested_token_pos, | 1399 intptr_t requested_token_pos, |
1426 intptr_t last_token_pos) { | 1400 intptr_t last_token_pos) { |
1427 ASSERT(func.HasCode()); | 1401 ASSERT(func.HasCode()); |
1428 ASSERT(!func.HasOptimizedCode()); | 1402 ASSERT(!func.HasOptimizedCode()); |
1429 | 1403 |
1430 if (requested_token_pos < func.token_pos()) { | 1404 if (requested_token_pos < func.token_pos()) { |
1431 requested_token_pos = func.token_pos(); | 1405 requested_token_pos = func.token_pos(); |
1432 } | 1406 } |
1433 if (last_token_pos > func.end_token_pos()) { | 1407 if (last_token_pos > func.end_token_pos()) { |
1434 last_token_pos = func.end_token_pos(); | 1408 last_token_pos = func.end_token_pos(); |
1435 } | 1409 } |
1436 | 1410 |
1437 Code& code = Code::Handle(func.unoptimized_code()); | 1411 Code& code = Code::Handle(func.unoptimized_code()); |
1438 ASSERT(!code.IsNull()); | 1412 ASSERT(!code.IsNull()); |
1439 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); | 1413 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
1440 intptr_t best_fit_pos = INT_MAX; | 1414 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); | 1415 PcDescriptors::Iterator iter(desc, kSafepointKind); |
1416 | |
1417 // First pass: find the lowest token position within the given range. | |
Ivan Posva
2014/12/15 16:36:54
What you are looking for here is the lowest breakp
hausner
2014/12/15 18:21:46
Yes. I clarified the comments.
| |
1444 while (iter.MoveNext()) { | 1418 while (iter.MoveNext()) { |
1445 const intptr_t desc_token_pos = iter.TokenPos(); | 1419 const intptr_t desc_token_pos = iter.TokenPos(); |
1446 ASSERT(desc_token_pos >= 0); | 1420 ASSERT(desc_token_pos >= 0); |
1447 if (desc_token_pos != Scanner::kNoSourcePos) { | 1421 if ((desc_token_pos != Scanner::kNoSourcePos) && |
1448 if ((desc_token_pos < requested_token_pos) || | 1422 (desc_token_pos < best_fit_pos) && |
1449 (desc_token_pos > last_token_pos)) { | 1423 (desc_token_pos >= requested_token_pos) && |
1450 // This descriptor is outside the desired token range. | 1424 (desc_token_pos <= last_token_pos)) { |
1451 continue; | 1425 best_fit_pos = desc_token_pos; |
1452 } | 1426 } |
1453 if (desc_token_pos < best_fit_pos) { | 1427 } |
1454 // So far, this descriptor has the lowest token position after | 1428 // Second pass: for all token positions on the same line, select the |
1455 // the first acceptable token position. | 1429 // one with the lowest compiled code address. E.g., in a line with |
1456 best_fit_pos = desc_token_pos; | 1430 // the nested function calls f(g(x)), the call g() will have a lower |
1457 } | 1431 // compiled code address but is not the lowest token position in the |
1458 if (iter.Pc() < lowest_pc) { | 1432 // line. |
1459 // This descriptor so far has the lowest code address. | 1433 if (best_fit_pos != INT_MAX) { |
1434 const Script& script = Script::Handle(func.script()); | |
1435 const TokenStream& tokens = TokenStream::Handle(script.tokens()); | |
1436 const intptr_t begin_pos = best_fit_pos; | |
1437 const intptr_t end_of_line_pos = LastTokenOnLine(tokens, begin_pos); | |
1438 uword lowest_pc = kUwordMax; | |
1439 PcDescriptors::Iterator iter(desc, kSafepointKind); | |
1440 while (iter.MoveNext()) { | |
1441 const intptr_t pos = iter.TokenPos(); | |
1442 if ((pos != Scanner::kNoSourcePos) && | |
1443 (begin_pos <= pos) && (pos <= end_of_line_pos) && | |
1444 (iter.Pc() < lowest_pc)) { | |
1460 lowest_pc = iter.Pc(); | 1445 lowest_pc = iter.Pc(); |
1461 lowest_pc_token_pos = desc_token_pos; | 1446 best_fit_pos = pos; |
1462 } | 1447 } |
1463 } | 1448 } |
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; | 1449 return best_fit_pos; |
1474 } | 1450 } |
1451 | |
1475 // We didn't find a safe point in the given token range. Try and find | 1452 // 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. | 1453 // a safe point in the remaining source code of the function. |
1477 if (last_token_pos < func.end_token_pos()) { | 1454 if (last_token_pos < func.end_token_pos()) { |
1478 return ResolveBreakpointPos(func, last_token_pos, func.end_token_pos()); | 1455 return ResolveBreakpointPos(func, last_token_pos, func.end_token_pos()); |
1479 } | 1456 } |
1480 return -1; | 1457 return -1; |
1481 } | 1458 } |
1482 | 1459 |
1483 | 1460 |
1484 void Debugger::MakeCodeBreakpointAt(const Function& func, | 1461 void Debugger::MakeCodeBreakpointAt(const Function& func, |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1547 if (!functions.IsNull()) { | 1524 if (!functions.IsNull()) { |
1548 const intptr_t num_functions = functions.Length(); | 1525 const intptr_t num_functions = functions.Length(); |
1549 for (intptr_t pos = 0; pos < num_functions; pos++) { | 1526 for (intptr_t pos = 0; pos < num_functions; pos++) { |
1550 function ^= functions.At(pos); | 1527 function ^= functions.At(pos); |
1551 ASSERT(!function.IsNull()); | 1528 ASSERT(!function.IsNull()); |
1552 // Check token position first to avoid unnecessary calls | 1529 // Check token position first to avoid unnecessary calls |
1553 // to script() which allocates handles. | 1530 // to script() which allocates handles. |
1554 if ((function.token_pos() == start_pos) | 1531 if ((function.token_pos() == start_pos) |
1555 && (function.end_token_pos() == end_pos) | 1532 && (function.end_token_pos() == end_pos) |
1556 && (function.script() == script.raw())) { | 1533 && (function.script() == script.raw())) { |
1557 if (function.HasCode()) { | 1534 if (function.HasCode() && !function.IsAsyncFunction()) { |
1558 function_list->Add(function); | 1535 function_list->Add(function); |
1559 } | 1536 } |
1560 if (function.HasImplicitClosureFunction()) { | 1537 if (function.HasImplicitClosureFunction()) { |
1561 function = function.ImplicitClosureFunction(); | 1538 function = function.ImplicitClosureFunction(); |
1562 if (function.HasCode()) { | 1539 if (function.HasCode() && !function.IsAsyncFunction()) { |
1563 function_list->Add(function); | 1540 function_list->Add(function); |
1564 } | 1541 } |
1565 } | 1542 } |
1566 } | 1543 } |
1567 } | 1544 } |
1568 } | 1545 } |
1569 closures = cls.closures(); | 1546 closures = cls.closures(); |
1570 if (!closures.IsNull()) { | 1547 if (!closures.IsNull()) { |
1571 const intptr_t num_closures = closures.Length(); | 1548 const intptr_t num_closures = closures.Length(); |
1572 for (intptr_t pos = 0; pos < num_closures; pos++) { | 1549 for (intptr_t pos = 0; pos < num_closures; pos++) { |
1573 function ^= closures.At(pos); | 1550 function ^= closures.At(pos); |
1574 ASSERT(!function.IsNull()); | 1551 ASSERT(!function.IsNull()); |
1575 if ((function.token_pos() == start_pos) | 1552 if ((function.token_pos() == start_pos) |
1576 && (function.end_token_pos() == end_pos) | 1553 && (function.end_token_pos() == end_pos) |
1577 && (function.script() == script.raw())) { | 1554 && (function.script() == script.raw())) { |
1578 if (function.HasCode()) { | 1555 if (function.HasCode() && !function.IsAsyncFunction()) { |
1579 function_list->Add(function); | 1556 function_list->Add(function); |
1580 } | 1557 } |
1581 if (function.HasImplicitClosureFunction()) { | 1558 if (function.HasImplicitClosureFunction()) { |
1582 function = function.ImplicitClosureFunction(); | 1559 function = function.ImplicitClosureFunction(); |
1583 if (function.HasCode()) { | 1560 if (function.HasCode() && !function.IsAsyncFunction()) { |
1584 function_list->Add(function); | 1561 function_list->Add(function); |
1585 } | 1562 } |
1586 } | 1563 } |
1587 } | 1564 } |
1588 } | 1565 } |
1589 } | 1566 } |
1590 } | 1567 } |
1591 } | 1568 } |
1592 } | 1569 } |
1593 | 1570 |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1716 const intptr_t num_functions = functions.Length(); | 1693 const intptr_t num_functions = functions.Length(); |
1717 for (intptr_t i = 0; i < num_functions; i++) { | 1694 for (intptr_t i = 0; i < num_functions; i++) { |
1718 func ^= functions.At(i); | 1695 func ^= functions.At(i); |
1719 ASSERT(func.HasCode()); | 1696 ASSERT(func.HasCode()); |
1720 MakeCodeBreakpointAt(func, bpt); | 1697 MakeCodeBreakpointAt(func, bpt); |
1721 } | 1698 } |
1722 bpt->Enable(); | 1699 bpt->Enable(); |
1723 if (FLAG_verbose_debug) { | 1700 if (FLAG_verbose_debug) { |
1724 intptr_t line_number; | 1701 intptr_t line_number; |
1725 script.GetTokenLocation(breakpoint_pos, &line_number, NULL); | 1702 script.GetTokenLocation(breakpoint_pos, &line_number, NULL); |
1726 OS::Print("Resolved breakpoint for " | 1703 OS::Print("Resolved BP for " |
1727 "function '%s' at line %" Pd "\n", | 1704 "function '%s' at line %" Pd "\n", |
1728 func.ToFullyQualifiedCString(), | 1705 func.ToFullyQualifiedCString(), |
1729 line_number); | 1706 line_number); |
1730 } | 1707 } |
1731 SignalBpResolved(bpt); | 1708 SignalBpResolved(bpt); |
1732 return bpt; | 1709 return bpt; |
1733 } | 1710 } |
1734 } | 1711 } |
1735 // There is no compiled function at this token position. | 1712 // There is no compiled function at this token position. |
1736 // Register an unresolved breakpoint. | 1713 // Register an unresolved breakpoint. |
(...skipping 26 matching lines...) Expand all Loading... | |
1763 } else { | 1740 } else { |
1764 cbpt->Disable(); | 1741 cbpt->Disable(); |
1765 } | 1742 } |
1766 } | 1743 } |
1767 cbpt = cbpt->next(); | 1744 cbpt = cbpt->next(); |
1768 } | 1745 } |
1769 } | 1746 } |
1770 | 1747 |
1771 | 1748 |
1772 RawError* Debugger::OneTimeBreakAtEntry(const Function& target_function) { | 1749 RawError* Debugger::OneTimeBreakAtEntry(const Function& target_function) { |
1773 Error& err = Error::Handle(); | 1750 SourceBreakpoint* bpt = SetBreakpointAtEntry(target_function); |
1774 err = SetInternalBreakpoints(target_function); | 1751 if (bpt != NULL) { |
1775 if (err.IsNull() && target_function.HasImplicitClosureFunction()) { | 1752 bpt->SetIsOneShot(); |
1776 const Function& closure_func = | |
1777 Function::Handle(target_function.ImplicitClosureFunction()); | |
1778 err = SetInternalBreakpoints(closure_func); | |
1779 } | 1753 } |
1780 return err.raw(); | 1754 return Error::null(); |
1781 } | 1755 } |
1782 | 1756 |
1783 | 1757 |
1784 SourceBreakpoint* Debugger::SetBreakpointAtEntry( | 1758 SourceBreakpoint* Debugger::SetBreakpointAtEntry( |
1785 const Function& target_function) { | 1759 const Function& target_function) { |
1786 ASSERT(!target_function.IsNull()); | 1760 ASSERT(!target_function.IsNull()); |
1787 const Script& script = Script::Handle(target_function.script()); | 1761 const Script& script = Script::Handle(target_function.script()); |
1788 return SetBreakpoint(script, | 1762 return SetBreakpoint(script, |
1789 target_function.token_pos(), | 1763 target_function.token_pos(), |
1790 target_function.end_token_pos()); | 1764 target_function.end_token_pos()); |
(...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2129 } | 2103 } |
2130 | 2104 |
2131 | 2105 |
2132 void Debugger::SignalPausedEvent(ActivationFrame* top_frame, | 2106 void Debugger::SignalPausedEvent(ActivationFrame* top_frame, |
2133 SourceBreakpoint* bpt) { | 2107 SourceBreakpoint* bpt) { |
2134 resume_action_ = kContinue; | 2108 resume_action_ = kContinue; |
2135 stepping_fp_ = 0; | 2109 stepping_fp_ = 0; |
2136 isolate_->set_single_step(false); | 2110 isolate_->set_single_step(false); |
2137 ASSERT(!IsPaused()); | 2111 ASSERT(!IsPaused()); |
2138 ASSERT(obj_cache_ == NULL); | 2112 ASSERT(obj_cache_ == NULL); |
2113 if ((bpt != NULL) && bpt->IsOneShot()) { | |
2114 RemoveBreakpoint(bpt->id()); | |
2115 bpt = NULL; | |
2116 } | |
2139 DebuggerEvent event(isolate_, DebuggerEvent::kBreakpointReached); | 2117 DebuggerEvent event(isolate_, DebuggerEvent::kBreakpointReached); |
2140 event.set_top_frame(top_frame); | 2118 event.set_top_frame(top_frame); |
2141 event.set_breakpoint(bpt); | 2119 event.set_breakpoint(bpt); |
2142 Pause(&event); | 2120 Pause(&event); |
2143 } | 2121 } |
2144 | 2122 |
2145 | 2123 |
2146 void Debugger::DebuggerStepCallback() { | 2124 void Debugger::DebuggerStepCallback() { |
2147 ASSERT(isolate_->single_step()); | 2125 ASSERT(isolate_->single_step()); |
2148 // We can't get here unless the debugger event handler enabled | 2126 // 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 } | 2475 } |
2498 | 2476 |
2499 | 2477 |
2500 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) { | 2478 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) { |
2501 ASSERT(bpt->next() == NULL); | 2479 ASSERT(bpt->next() == NULL); |
2502 bpt->set_next(code_breakpoints_); | 2480 bpt->set_next(code_breakpoints_); |
2503 code_breakpoints_ = bpt; | 2481 code_breakpoints_ = bpt; |
2504 } | 2482 } |
2505 | 2483 |
2506 } // namespace dart | 2484 } // namespace dart |
OLD | NEW |