OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library debugger_page_element; | 5 library debugger_page_element; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:html'; | 8 import 'dart:html'; |
9 import 'observatory_element.dart'; | 9 import 'observatory_element.dart'; |
10 import 'package:observatory/app.dart'; | 10 import 'package:observatory/app.dart'; |
(...skipping 1069 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1080 } | 1080 } |
1081 | 1081 |
1082 String helpShort = 'Refresh debugging information of various sorts'; | 1082 String helpShort = 'Refresh debugging information of various sorts'; |
1083 | 1083 |
1084 String helpLong = | 1084 String helpLong = |
1085 'Refresh debugging information of various sorts.\n' | 1085 'Refresh debugging information of various sorts.\n' |
1086 '\n' | 1086 '\n' |
1087 'Syntax: refresh <subcommand>\n'; | 1087 'Syntax: refresh <subcommand>\n'; |
1088 } | 1088 } |
1089 | 1089 |
| 1090 class VmRestartCommand extends DebuggerCommand { |
| 1091 VmRestartCommand(Debugger debugger) : super(debugger, 'restart', []); |
| 1092 |
| 1093 Future handleModalInput(String line) async { |
| 1094 if (line == 'yes') { |
| 1095 debugger.console.printRed('Restarting VM...'); |
| 1096 await debugger.vm.restart(); |
| 1097 debugger.input.exitMode(); |
| 1098 } else if (line == 'no') { |
| 1099 debugger.console.printRed('VM restart canceled.'); |
| 1100 debugger.input.exitMode(); |
| 1101 } else { |
| 1102 debugger.console.printRed("Please type 'yes' or 'no'"); |
| 1103 } |
| 1104 } |
| 1105 |
| 1106 Future run(List<String> args) async { |
| 1107 debugger.input.enterMode('Restart vm? (yes/no)', handleModalInput); |
| 1108 } |
| 1109 |
| 1110 String helpShort = 'Restart a Dart virtual machine'; |
| 1111 |
| 1112 String helpLong = |
| 1113 'Restart a Dart virtual machine.\n' |
| 1114 '\n' |
| 1115 'Syntax: vm restart\n'; |
| 1116 } |
| 1117 |
| 1118 class VmCommand extends DebuggerCommand { |
| 1119 VmCommand(Debugger debugger) : super(debugger, 'vm', [ |
| 1120 new VmRestartCommand(debugger), |
| 1121 ]); |
| 1122 |
| 1123 Future run(List<String> args) async { |
| 1124 debugger.console.print("'vm' expects a subcommand (see 'help vm')"); |
| 1125 } |
| 1126 |
| 1127 String helpShort = 'Manage a Dart virtual machine'; |
| 1128 |
| 1129 String helpLong = |
| 1130 'Manage a Dart virtual machine.\n' |
| 1131 '\n' |
| 1132 'Syntax: vm <subcommand>\n'; |
| 1133 } |
| 1134 |
1090 class _ConsoleStreamPrinter { | 1135 class _ConsoleStreamPrinter { |
1091 ObservatoryDebugger _debugger; | 1136 ObservatoryDebugger _debugger; |
1092 | 1137 |
1093 _ConsoleStreamPrinter(this._debugger); | 1138 _ConsoleStreamPrinter(this._debugger); |
1094 Level _minimumLogLevel = Level.OFF; | 1139 Level _minimumLogLevel = Level.OFF; |
1095 String _savedStream; | 1140 String _savedStream; |
1096 String _savedIsolate; | 1141 String _savedIsolate; |
1097 String _savedLine; | 1142 String _savedLine; |
1098 List<String> _buffer = []; | 1143 List<String> _buffer = []; |
1099 | 1144 |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1227 new IsolateCommand(this), | 1272 new IsolateCommand(this), |
1228 new LogCommand(this), | 1273 new LogCommand(this), |
1229 new PauseCommand(this), | 1274 new PauseCommand(this), |
1230 new PrintCommand(this), | 1275 new PrintCommand(this), |
1231 new RefreshCommand(this), | 1276 new RefreshCommand(this), |
1232 new SetCommand(this), | 1277 new SetCommand(this), |
1233 new SmartNextCommand(this), | 1278 new SmartNextCommand(this), |
1234 new StepCommand(this), | 1279 new StepCommand(this), |
1235 new SyncNextCommand(this), | 1280 new SyncNextCommand(this), |
1236 new UpCommand(this), | 1281 new UpCommand(this), |
| 1282 new VmCommand(this), |
1237 ]); | 1283 ]); |
1238 _consolePrinter = new _ConsoleStreamPrinter(this); | 1284 _consolePrinter = new _ConsoleStreamPrinter(this); |
1239 } | 1285 } |
1240 | 1286 |
1241 void _loadSettings() { | 1287 void _loadSettings() { |
1242 _upIsDown = settings.get('up-is-down'); | 1288 _upIsDown = settings.get('up-is-down'); |
1243 } | 1289 } |
1244 | 1290 |
1245 VM get vm => page.app.vm; | 1291 VM get vm => page.app.vm; |
1246 | 1292 |
1247 void updateIsolate(Isolate iso) { | 1293 void updateIsolate(Isolate iso) { |
1248 _isolate = iso; | 1294 _isolate = iso; |
1249 if (_isolate != null) { | 1295 if (_isolate != null) { |
1250 if ((breakOnException != iso.exceptionsPauseInfo) && | 1296 if ((breakOnException != iso.exceptionsPauseInfo) && |
1251 (iso.exceptionsPauseInfo != null)) { | 1297 (iso.exceptionsPauseInfo != null)) { |
1252 breakOnException = iso.exceptionsPauseInfo; | 1298 breakOnException = iso.exceptionsPauseInfo; |
1253 console.print("Now pausing for exceptions: $breakOnException"); | 1299 console.print("Now pausing for exceptions: $breakOnException"); |
1254 } | 1300 } |
1255 | 1301 |
1256 _isolate.reload().then((response) { | 1302 _isolate.reload().then((response) { |
| 1303 if (response.isSentinel) { |
| 1304 // The isolate has gone away. The IsolateExit event will |
| 1305 // clear the isolate for the debugger page. |
| 1306 return; |
| 1307 } |
1257 // TODO(turnidge): Currently the debugger relies on all libs | 1308 // TODO(turnidge): Currently the debugger relies on all libs |
1258 // being loaded. Fix this. | 1309 // being loaded. Fix this. |
1259 var pending = []; | 1310 var pending = []; |
1260 for (var lib in _isolate.libraries) { | 1311 for (var lib in response.libraries) { |
1261 if (!lib.loaded) { | 1312 if (!lib.loaded) { |
1262 pending.add(lib.load()); | 1313 pending.add(lib.load()); |
1263 } | 1314 } |
1264 } | 1315 } |
1265 Future.wait(pending).then((_) { | 1316 Future.wait(pending).then((_) { |
1266 _refreshStack(isolate.pauseEvent).then((_) { | 1317 refreshStack(); |
1267 reportStatus(); | 1318 }).catchError((e) { |
1268 }); | 1319 print("UNEXPECTED ERROR $e"); |
1269 }).catchError((_) { | 1320 reportStatus(); |
1270 // Error loading libraries, try and display stack. | |
1271 _refreshStack(isolate.pauseEvent).then((_) { | |
1272 reportStatus(); | |
1273 }); | |
1274 }); | 1321 }); |
1275 }); | 1322 }); |
1276 } else { | 1323 } else { |
1277 reportStatus(); | 1324 reportStatus(); |
1278 } | 1325 } |
1279 } | 1326 } |
1280 | 1327 |
1281 set isolate(Isolate iso) { | 1328 set isolate(Isolate iso) { |
1282 // Setting the page's isolate will trigger updateIsolate to be called. | 1329 // Setting the page's isolate will trigger updateIsolate to be called. |
1283 // | 1330 // |
1284 // TODO(turnidge): Rework ownership of the ObservatoryDebugger in another | 1331 // TODO(turnidge): Rework ownership of the ObservatoryDebugger in another |
1285 // change. | 1332 // change. |
1286 page.isolate = iso; | 1333 page.isolate = iso; |
1287 } | 1334 } |
1288 Isolate get isolate => _isolate; | 1335 Isolate get isolate => _isolate; |
1289 Isolate _isolate; | 1336 Isolate _isolate; |
1290 | 1337 |
1291 void init() { | 1338 void init() { |
1292 console.newline(); | 1339 console.newline(); |
1293 console.printBold("Type 'h' for help"); | 1340 console.printBold("Type 'h' for help"); |
1294 // Wait a bit and if polymer still hasn't set up the isolate, | 1341 // Wait a bit and if polymer still hasn't set up the isolate, |
1295 // report this to the user. | 1342 // report this to the user. |
1296 new Timer(const Duration(seconds:1), () { | 1343 new Timer(const Duration(seconds:1), () { |
1297 if (isolate == null) { | 1344 if (isolate == null) { |
1298 reportStatus(); | 1345 reportStatus(); |
1299 } | 1346 } |
1300 }); | 1347 }); |
1301 } | 1348 } |
1302 | 1349 |
1303 Future refreshStack() { | 1350 Future refreshStack() async { |
1304 return _refreshStack(isolate.pauseEvent).then((_) { | 1351 try { |
| 1352 if (_isolate != null) { |
| 1353 await _refreshStack(_isolate.pauseEvent); |
| 1354 } |
| 1355 flushStdio(); |
1305 reportStatus(); | 1356 reportStatus(); |
1306 }); | 1357 } catch (e, st) { |
| 1358 console.printRed("Unexpected error in refreshStack: $e\n$st"); |
| 1359 } |
1307 } | 1360 } |
1308 | 1361 |
1309 bool isolatePaused() { | 1362 bool isolatePaused() { |
1310 // TODO(turnidge): Stop relying on the isolate to track the last | 1363 // TODO(turnidge): Stop relying on the isolate to track the last |
1311 // pause event. Since we listen to events directly in the | 1364 // pause event. Since we listen to events directly in the |
1312 // debugger, this could introduce a race. | 1365 // debugger, this could introduce a race. |
1313 return (isolate != null && | 1366 return (isolate != null && |
1314 isolate.pauseEvent != null && | 1367 isolate.pauseEvent != null && |
1315 isolate.pauseEvent.kind != ServiceEvent.kResume); | 1368 isolate.pauseEvent.kind != ServiceEvent.kResume); |
1316 } | 1369 } |
1317 | 1370 |
1318 void warnOutOfDate() { | 1371 void warnOutOfDate() { |
1319 // Wait a bit, then tell the user that the stack may be out of date. | 1372 // Wait a bit, then tell the user that the stack may be out of date. |
1320 new Timer(const Duration(seconds:2), () { | 1373 new Timer(const Duration(seconds:2), () { |
1321 if (!isolatePaused()) { | 1374 if (!isolatePaused()) { |
1322 stackElement.isSampled = true; | 1375 stackElement.isSampled = true; |
1323 } | 1376 } |
1324 }); | 1377 }); |
1325 } | 1378 } |
1326 | 1379 |
1327 Future<ServiceMap> _refreshStack(ServiceEvent pauseEvent) { | 1380 Future<ServiceMap> _refreshStack(ServiceEvent pauseEvent) { |
1328 return isolate.getStack().then((result) { | 1381 return isolate.getStack().then((result) { |
| 1382 if (result.isSentinel) { |
| 1383 // The isolate has gone away. The IsolateExit event will |
| 1384 // clear the isolate for the debugger page. |
| 1385 return; |
| 1386 } |
1329 stack = result; | 1387 stack = result; |
1330 // TODO(turnidge): Replace only the changed part of the stack to | |
1331 // reduce flicker. | |
1332 stackElement.updateStack(stack, pauseEvent); | 1388 stackElement.updateStack(stack, pauseEvent); |
1333 if (stack['frames'].length > 0) { | 1389 if (stack['frames'].length > 0) { |
1334 currentFrame = 0; | 1390 currentFrame = 0; |
1335 } else { | 1391 } else { |
1336 currentFrame = null; | 1392 currentFrame = null; |
1337 } | 1393 } |
1338 input.focus(); | 1394 input.focus(); |
1339 }); | 1395 }); |
1340 } | 1396 } |
1341 | 1397 |
(...skipping 15 matching lines...) Expand all Loading... |
1357 | 1413 |
1358 void _reportPause(ServiceEvent event) { | 1414 void _reportPause(ServiceEvent event) { |
1359 if (event.kind == ServiceEvent.kPauseStart) { | 1415 if (event.kind == ServiceEvent.kPauseStart) { |
1360 console.print( | 1416 console.print( |
1361 "Paused at isolate start " | 1417 "Paused at isolate start " |
1362 "(type 'continue' [F7] or 'step' [F10] to start the isolate')"); | 1418 "(type 'continue' [F7] or 'step' [F10] to start the isolate')"); |
1363 } else if (event.kind == ServiceEvent.kPauseExit) { | 1419 } else if (event.kind == ServiceEvent.kPauseExit) { |
1364 console.print( | 1420 console.print( |
1365 "Paused at isolate exit " | 1421 "Paused at isolate exit " |
1366 "(type 'continue' or [F7] to exit the isolate')"); | 1422 "(type 'continue' or [F7] to exit the isolate')"); |
1367 } | 1423 } else if (stack['frames'].length > 0) { |
1368 if (stack['frames'].length > 0) { | |
1369 Frame frame = stack['frames'][0]; | 1424 Frame frame = stack['frames'][0]; |
1370 var script = frame.location.script; | 1425 var script = frame.location.script; |
1371 script.load().then((_) { | 1426 script.load().then((_) { |
1372 var line = script.tokenToLine(frame.location.tokenPos); | 1427 var line = script.tokenToLine(frame.location.tokenPos); |
1373 var col = script.tokenToCol(frame.location.tokenPos); | 1428 var col = script.tokenToCol(frame.location.tokenPos); |
1374 if (event.breakpoint != null) { | 1429 if (event.breakpoint != null) { |
1375 var bpId = event.breakpoint.number; | 1430 var bpId = event.breakpoint.number; |
1376 console.print('Paused at breakpoint ${bpId} at ' | 1431 console.print('Paused at breakpoint ${bpId} at ' |
1377 '${script.name}:${line}:${col}'); | 1432 '${script.name}:${line}:${col}'); |
1378 } else if (event.exception != null) { | 1433 } else if (event.exception != null) { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1422 } | 1477 } |
1423 } | 1478 } |
1424 | 1479 |
1425 void onEvent(ServiceEvent event) { | 1480 void onEvent(ServiceEvent event) { |
1426 switch(event.kind) { | 1481 switch(event.kind) { |
1427 case ServiceEvent.kIsolateStart: | 1482 case ServiceEvent.kIsolateStart: |
1428 { | 1483 { |
1429 var iso = event.owner; | 1484 var iso = event.owner; |
1430 console.print( | 1485 console.print( |
1431 "Isolate ${iso.number} '${iso.name}' has been created"); | 1486 "Isolate ${iso.number} '${iso.name}' has been created"); |
| 1487 if (isolate == null) { |
| 1488 console.print("Switching to isolate ${iso.number} '${iso.name}'"); |
| 1489 isolate = iso; |
| 1490 } |
1432 } | 1491 } |
1433 break; | 1492 break; |
1434 | 1493 |
1435 case ServiceEvent.kIsolateExit: | 1494 case ServiceEvent.kIsolateExit: |
1436 { | 1495 { |
1437 var iso = event.owner; | 1496 var iso = event.owner; |
1438 if (iso == isolate) { | 1497 if (iso == isolate) { |
1439 console.print("The current isolate has exited"); | 1498 console.print("The current isolate ${iso.number} '${iso.name}' " |
| 1499 "has exited"); |
| 1500 var isolates = vm.isolates; |
| 1501 if (isolates.length > 0) { |
| 1502 var newIsolate = isolates.first; |
| 1503 console.print("Switching to isolate " |
| 1504 "${newIsolate.number} '${newIsolate.name}'"); |
| 1505 isolate = newIsolate; |
| 1506 } else { |
| 1507 isolate = null; |
| 1508 } |
1440 } else { | 1509 } else { |
1441 console.print( | 1510 console.print( |
1442 "Isolate ${iso.number} '${iso.name}' has exited"); | 1511 "Isolate ${iso.number} '${iso.name}' has exited"); |
1443 } | 1512 } |
1444 } | 1513 } |
1445 break; | 1514 break; |
1446 | 1515 |
1447 case ServiceEvent.kDebuggerSettingsUpdate: | 1516 case ServiceEvent.kDebuggerSettingsUpdate: |
1448 if (breakOnException != event.exceptions) { | 1517 if (breakOnException != event.exceptions) { |
1449 breakOnException = event.exceptions; | 1518 breakOnException = event.exceptions; |
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1686 return new Future.value(null); | 1755 return new Future.value(null); |
1687 } | 1756 } |
1688 } | 1757 } |
1689 } | 1758 } |
1690 | 1759 |
1691 @CustomTag('debugger-page') | 1760 @CustomTag('debugger-page') |
1692 class DebuggerPageElement extends ObservatoryElement { | 1761 class DebuggerPageElement extends ObservatoryElement { |
1693 @published Isolate isolate; | 1762 @published Isolate isolate; |
1694 | 1763 |
1695 isolateChanged(oldValue) { | 1764 isolateChanged(oldValue) { |
1696 if (isolate != null) { | 1765 debugger.updateIsolate(isolate); |
1697 debugger.updateIsolate(isolate); | |
1698 } | |
1699 } | 1766 } |
1700 ObservatoryDebugger debugger = new ObservatoryDebugger(); | 1767 ObservatoryDebugger debugger = new ObservatoryDebugger(); |
1701 | 1768 |
1702 DebuggerPageElement.created() : super.created() { | 1769 DebuggerPageElement.created() : super.created() { |
1703 debugger.page = this; | 1770 debugger.page = this; |
1704 } | 1771 } |
1705 | 1772 |
1706 StreamSubscription _resizeSubscription; | 1773 StreamSubscription _resizeSubscription; |
1707 Future<StreamSubscription> _isolateSubscriptionFuture; | 1774 Future<StreamSubscription> _isolateSubscriptionFuture; |
1708 Future<StreamSubscription> _debugSubscriptionFuture; | 1775 Future<StreamSubscription> _debugSubscriptionFuture; |
(...skipping 534 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2243 consoleTextElement.children.clear(); | 2310 consoleTextElement.children.clear(); |
2244 } | 2311 } |
2245 } | 2312 } |
2246 | 2313 |
2247 @CustomTag('debugger-input') | 2314 @CustomTag('debugger-input') |
2248 class DebuggerInputElement extends ObservatoryElement { | 2315 class DebuggerInputElement extends ObservatoryElement { |
2249 @published Isolate isolate; | 2316 @published Isolate isolate; |
2250 @published String text = ''; | 2317 @published String text = ''; |
2251 @observable ObservatoryDebugger debugger; | 2318 @observable ObservatoryDebugger debugger; |
2252 @observable bool busy = false; | 2319 @observable bool busy = false; |
| 2320 @observable String modalPrompt = null; |
| 2321 var modalCallback = null; |
| 2322 |
| 2323 void enterMode(String prompt, callback) { |
| 2324 assert(prompt == null); |
| 2325 modalPrompt = prompt; |
| 2326 modalCallback = callback; |
| 2327 } |
| 2328 |
| 2329 void exitMode() { |
| 2330 assert(prompt != null); |
| 2331 modalPrompt = null; |
| 2332 modalCallback = null; |
| 2333 } |
2253 | 2334 |
2254 @override | 2335 @override |
2255 void ready() { | 2336 void ready() { |
2256 super.ready(); | 2337 super.ready(); |
2257 var textBox = $['textBox']; | 2338 var textBox = $['textBox']; |
2258 textBox.select(); | 2339 textBox.select(); |
2259 textBox.onKeyDown.listen((KeyboardEvent e) { | 2340 textBox.onKeyDown.listen((KeyboardEvent e) { |
2260 if (busy) { | 2341 if (busy) { |
2261 e.preventDefault(); | 2342 e.preventDefault(); |
2262 return; | 2343 return; |
2263 } | 2344 } |
2264 busy = true; | 2345 busy = true; |
2265 » switch (e.keyCode) { | 2346 if (modalCallback != null) { |
| 2347 if (e.keyCode == KeyCode.ENTER) { |
| 2348 var response = text; |
| 2349 modalCallback(response).whenComplete(() { |
| 2350 text = ''; |
| 2351 busy = false; |
| 2352 }); |
| 2353 } else { |
| 2354 busy = false; |
| 2355 } |
| 2356 return; |
| 2357 } |
| 2358 switch (e.keyCode) { |
2266 case KeyCode.TAB: | 2359 case KeyCode.TAB: |
2267 e.preventDefault(); | 2360 e.preventDefault(); |
2268 int cursorPos = textBox.selectionStart; | 2361 int cursorPos = textBox.selectionStart; |
2269 debugger.complete(text.substring(0, cursorPos)).then((completion) { | 2362 debugger.complete(text.substring(0, cursorPos)).then((completion) { |
2270 text = completion + text.substring(cursorPos); | 2363 text = completion + text.substring(cursorPos); |
2271 // TODO(turnidge): Move the cursor to the end of the | 2364 // TODO(turnidge): Move the cursor to the end of the |
2272 // completion, rather than the end of the string. | 2365 // completion, rather than the end of the string. |
2273 }).whenComplete(() { | 2366 }).whenComplete(() { |
2274 busy = false; | 2367 busy = false; |
2275 }); | 2368 }); |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2351 busy = false; | 2444 busy = false; |
2352 }); | 2445 }); |
2353 } else { | 2446 } else { |
2354 busy = false; | 2447 busy = false; |
2355 } | 2448 } |
2356 break; | 2449 break; |
2357 | 2450 |
2358 default: | 2451 default: |
2359 busy = false; | 2452 busy = false; |
2360 break; | 2453 break; |
2361 » } | 2454 } |
2362 }); | 2455 }); |
2363 } | 2456 } |
2364 | 2457 |
2365 void focus() { | 2458 void focus() { |
2366 $['textBox'].focus(); | 2459 $['textBox'].focus(); |
2367 } | 2460 } |
2368 | 2461 |
2369 DebuggerInputElement.created() : super.created(); | 2462 DebuggerInputElement.created() : super.created(); |
2370 } | 2463 } |
OLD | NEW |