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/cli.dart'; | 10 import 'package:observatory/cli.dart'; |
(...skipping 836 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
847 } | 847 } |
848 | 848 |
849 String helpShort = 'Refresh debugging information of various sorts'; | 849 String helpShort = 'Refresh debugging information of various sorts'; |
850 | 850 |
851 String helpLong = | 851 String helpLong = |
852 'Refresh debugging information of various sorts.\n' | 852 'Refresh debugging information of various sorts.\n' |
853 '\n' | 853 '\n' |
854 'Syntax: refresh <subcommand>\n'; | 854 'Syntax: refresh <subcommand>\n'; |
855 } | 855 } |
856 | 856 |
| 857 class _VMStreamPrinter { |
| 858 ObservatoryDebugger _debugger; |
| 859 |
| 860 _VMStreamPrinter(this._debugger); |
| 861 |
| 862 String _savedStream; |
| 863 String _savedIsolate; |
| 864 String _savedLine; |
| 865 List<String> _buffer = []; |
| 866 |
| 867 void onEvent(String streamName, ServiceEvent event) { |
| 868 String isolateName = event.isolate.name; |
| 869 // If we get a line from a different isolate/stream, flush |
| 870 // any pending output, even if it is not newline-terminated. |
| 871 if ((_savedIsolate != null && isolateName != _savedIsolate) || |
| 872 (_savedStream != null && streamName != _savedStream)) { |
| 873 flush(); |
| 874 } |
| 875 String data = event.bytesAsString; |
| 876 bool hasNewline = data.endsWith('\n'); |
| 877 if (_savedLine != null) { |
| 878 data = _savedLine + data; |
| 879 _savedIsolate = null; |
| 880 _savedStream = null; |
| 881 _savedLine = null; |
| 882 } |
| 883 var lines = data.split('\n').where((line) => line != '').toList(); |
| 884 if (lines.isEmpty) { |
| 885 return; |
| 886 } |
| 887 int limit = (hasNewline ? lines.length : lines.length - 1); |
| 888 for (int i = 0; i < limit; i++) { |
| 889 _buffer.add(_format(isolateName, streamName, lines[i])); |
| 890 } |
| 891 // If there is no newline, we save the last line of output for next time. |
| 892 if (!hasNewline) { |
| 893 _savedIsolate = isolateName; |
| 894 _savedStream = streamName; |
| 895 _savedLine = lines[lines.length - 1]; |
| 896 } |
| 897 } |
| 898 |
| 899 void flush() { |
| 900 // If there is any saved output, flush it now. |
| 901 if (_savedLine != null) { |
| 902 _buffer.add(_format(_savedIsolate, _savedStream, _savedLine)); |
| 903 _savedIsolate = null; |
| 904 _savedStream = null; |
| 905 _savedLine = null; |
| 906 } |
| 907 if (_buffer.isNotEmpty) { |
| 908 _debugger.console.printStdio(_buffer); |
| 909 _buffer.clear(); |
| 910 } |
| 911 } |
| 912 |
| 913 String _format(String isolateName, String streamName, String line) { |
| 914 return '${isolateName}:${streamName}> ${line}'; |
| 915 } |
| 916 } |
| 917 |
857 // Tracks the state for an isolate debugging session. | 918 // Tracks the state for an isolate debugging session. |
858 class ObservatoryDebugger extends Debugger { | 919 class ObservatoryDebugger extends Debugger { |
859 RootCommand cmd; | 920 RootCommand cmd; |
860 DebuggerPageElement page; | 921 DebuggerPageElement page; |
861 DebuggerConsoleElement console; | 922 DebuggerConsoleElement console; |
862 DebuggerInputElement input; | 923 DebuggerInputElement input; |
863 DebuggerStackElement stackElement; | 924 DebuggerStackElement stackElement; |
864 ServiceMap stack; | 925 ServiceMap stack; |
865 String exceptions = "none"; // Last known setting. | 926 String exceptions = "none"; // Last known setting. |
866 | 927 |
(...skipping 24 matching lines...) Expand all Loading... |
891 new StepCommand(this), | 952 new StepCommand(this), |
892 new FinishCommand(this), | 953 new FinishCommand(this), |
893 new BreakCommand(this), | 954 new BreakCommand(this), |
894 new SetCommand(this), | 955 new SetCommand(this), |
895 new ClearCommand(this), | 956 new ClearCommand(this), |
896 new DeleteCommand(this), | 957 new DeleteCommand(this), |
897 new InfoCommand(this), | 958 new InfoCommand(this), |
898 new IsolateCommand(this), | 959 new IsolateCommand(this), |
899 new RefreshCommand(this), | 960 new RefreshCommand(this), |
900 ]); | 961 ]); |
| 962 _stdioPrinter = new _VMStreamPrinter(this); |
901 } | 963 } |
902 | 964 |
903 VM get vm => page.app.vm; | 965 VM get vm => page.app.vm; |
904 | 966 |
905 void updateIsolate(Isolate iso) { | 967 void updateIsolate(Isolate iso) { |
906 _isolate = iso; | 968 _isolate = iso; |
907 if (_isolate != null) { | 969 if (_isolate != null) { |
908 if (exceptions != iso.exceptionsPauseInfo) { | 970 if (exceptions != iso.exceptionsPauseInfo) { |
909 exceptions = iso.exceptionsPauseInfo; | 971 exceptions = iso.exceptionsPauseInfo; |
910 console.print("Now pausing for $exceptions exceptions"); | 972 console.print("Now pausing for $exceptions exceptions"); |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
990 if (stack['frames'].length > 0) { | 1052 if (stack['frames'].length > 0) { |
991 currentFrame = 0; | 1053 currentFrame = 0; |
992 } else { | 1054 } else { |
993 currentFrame = null; | 1055 currentFrame = null; |
994 } | 1056 } |
995 input.focus(); | 1057 input.focus(); |
996 }); | 1058 }); |
997 } | 1059 } |
998 | 1060 |
999 void reportStatus() { | 1061 void reportStatus() { |
| 1062 flushStdio(); |
1000 if (_isolate == null) { | 1063 if (_isolate == null) { |
1001 console.print('No current isolate'); | 1064 console.print('No current isolate'); |
1002 } else if (_isolate.idle) { | 1065 } else if (_isolate.idle) { |
1003 console.print('Isolate is idle'); | 1066 console.print('Isolate is idle'); |
1004 } else if (_isolate.running) { | 1067 } else if (_isolate.running) { |
1005 console.print("Isolate is running (type 'pause' to interrupt)"); | 1068 console.print("Isolate is running (type 'pause' to interrupt)"); |
1006 } else if (_isolate.pauseEvent != null) { | 1069 } else if (_isolate.pauseEvent != null) { |
1007 _reportPause(_isolate.pauseEvent); | 1070 _reportPause(_isolate.pauseEvent); |
1008 } else { | 1071 } else { |
1009 console.print('Isolate is in unknown state'); | 1072 console.print('Isolate is in unknown state'); |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1109 console.print("Isolate ${iso.number} renamed to '${iso.name}'"); | 1172 console.print("Isolate ${iso.number} renamed to '${iso.name}'"); |
1110 break; | 1173 break; |
1111 | 1174 |
1112 case ServiceEvent.kPauseStart: | 1175 case ServiceEvent.kPauseStart: |
1113 case ServiceEvent.kPauseExit: | 1176 case ServiceEvent.kPauseExit: |
1114 case ServiceEvent.kPauseBreakpoint: | 1177 case ServiceEvent.kPauseBreakpoint: |
1115 case ServiceEvent.kPauseInterrupted: | 1178 case ServiceEvent.kPauseInterrupted: |
1116 case ServiceEvent.kPauseException: | 1179 case ServiceEvent.kPauseException: |
1117 if (event.owner == isolate) { | 1180 if (event.owner == isolate) { |
1118 _refreshStack(event).then((_) { | 1181 _refreshStack(event).then((_) { |
| 1182 flushStdio(); |
1119 _reportPause(event); | 1183 _reportPause(event); |
1120 }); | 1184 }); |
1121 } | 1185 } |
1122 break; | 1186 break; |
1123 | 1187 |
1124 case ServiceEvent.kResume: | 1188 case ServiceEvent.kResume: |
1125 if (event.owner == isolate) { | 1189 if (event.owner == isolate) { |
| 1190 flushStdio(); |
1126 console.print('Continuing...'); | 1191 console.print('Continuing...'); |
1127 } | 1192 } |
1128 break; | 1193 break; |
1129 | 1194 |
1130 case ServiceEvent.kBreakpointAdded: | 1195 case ServiceEvent.kBreakpointAdded: |
1131 case ServiceEvent.kBreakpointResolved: | 1196 case ServiceEvent.kBreakpointResolved: |
1132 case ServiceEvent.kBreakpointRemoved: | 1197 case ServiceEvent.kBreakpointRemoved: |
1133 if (event.owner == isolate) { | 1198 if (event.owner == isolate) { |
1134 _reportBreakpointEvent(event); | 1199 _reportBreakpointEvent(event); |
1135 } | 1200 } |
1136 break; | 1201 break; |
1137 | 1202 |
1138 case ServiceEvent.kIsolateStart: | 1203 case ServiceEvent.kIsolateStart: |
1139 case ServiceEvent.kGraph: | 1204 case ServiceEvent.kGraph: |
1140 case ServiceEvent.kGC: | 1205 case ServiceEvent.kGC: |
1141 case ServiceEvent.kInspect: | 1206 case ServiceEvent.kInspect: |
1142 break; | 1207 break; |
1143 | 1208 |
1144 default: | 1209 default: |
1145 console.print('Unrecognized event: $event'); | 1210 console.print('Unrecognized event: $event'); |
1146 break; | 1211 break; |
1147 } | 1212 } |
1148 } | 1213 } |
1149 | 1214 |
| 1215 _VMStreamPrinter _stdioPrinter; |
| 1216 |
| 1217 void flushStdio() { |
| 1218 _stdioPrinter.flush(); |
| 1219 } |
| 1220 |
| 1221 void onStdout(ServiceEvent event) { |
| 1222 _stdioPrinter.onEvent('stdout', event); |
| 1223 } |
| 1224 |
| 1225 void onStderr(ServiceEvent event) { |
| 1226 _stdioPrinter.onEvent('stderr', event); |
| 1227 } |
| 1228 |
1150 static String _commonPrefix(String a, String b) { | 1229 static String _commonPrefix(String a, String b) { |
1151 int pos = 0; | 1230 int pos = 0; |
1152 while (pos < a.length && pos < b.length) { | 1231 while (pos < a.length && pos < b.length) { |
1153 if (a.codeUnitAt(pos) != b.codeUnitAt(pos)) { | 1232 if (a.codeUnitAt(pos) != b.codeUnitAt(pos)) { |
1154 break; | 1233 break; |
1155 } | 1234 } |
1156 pos++; | 1235 pos++; |
1157 } | 1236 } |
1158 return a.substring(0, pos); | 1237 return a.substring(0, pos); |
1159 } | 1238 } |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1229 } | 1308 } |
1230 } | 1309 } |
1231 ObservatoryDebugger debugger = new ObservatoryDebugger(); | 1310 ObservatoryDebugger debugger = new ObservatoryDebugger(); |
1232 | 1311 |
1233 DebuggerPageElement.created() : super.created() { | 1312 DebuggerPageElement.created() : super.created() { |
1234 debugger.page = this; | 1313 debugger.page = this; |
1235 } | 1314 } |
1236 | 1315 |
1237 Future<StreamSubscription> _isolateSubscriptionFuture; | 1316 Future<StreamSubscription> _isolateSubscriptionFuture; |
1238 Future<StreamSubscription> _debugSubscriptionFuture; | 1317 Future<StreamSubscription> _debugSubscriptionFuture; |
| 1318 Future<StreamSubscription> _stdoutSubscriptionFuture; |
| 1319 Future<StreamSubscription> _stderrSubscriptionFuture; |
1239 | 1320 |
1240 @override | 1321 @override |
1241 void attached() { | 1322 void attached() { |
1242 super.attached(); | 1323 super.attached(); |
1243 | 1324 |
1244 var navbarDiv = $['navbarDiv']; | 1325 var navbarDiv = $['navbarDiv']; |
1245 var stackDiv = $['stackDiv']; | 1326 var stackDiv = $['stackDiv']; |
1246 var splitterDiv = $['splitterDiv']; | 1327 var splitterDiv = $['splitterDiv']; |
1247 var cmdDiv = $['commandDiv']; | 1328 var cmdDiv = $['commandDiv']; |
1248 | 1329 |
(...skipping 13 matching lines...) Expand all Loading... |
1262 stackElement.debugger = debugger; | 1343 stackElement.debugger = debugger; |
1263 debugger.console = $['console']; | 1344 debugger.console = $['console']; |
1264 debugger.input = $['commandline']; | 1345 debugger.input = $['commandline']; |
1265 debugger.input.debugger = debugger; | 1346 debugger.input.debugger = debugger; |
1266 debugger.init(); | 1347 debugger.init(); |
1267 | 1348 |
1268 _isolateSubscriptionFuture = | 1349 _isolateSubscriptionFuture = |
1269 app.vm.listenEventStream(VM.kIsolateStream, debugger.onEvent); | 1350 app.vm.listenEventStream(VM.kIsolateStream, debugger.onEvent); |
1270 _debugSubscriptionFuture = | 1351 _debugSubscriptionFuture = |
1271 app.vm.listenEventStream(VM.kDebugStream, debugger.onEvent); | 1352 app.vm.listenEventStream(VM.kDebugStream, debugger.onEvent); |
| 1353 _stdoutSubscriptionFuture = |
| 1354 app.vm.listenEventStream(VM.kStdoutStream, debugger.onStdout); |
| 1355 _stderrSubscriptionFuture = |
| 1356 app.vm.listenEventStream(VM.kStderrStream, debugger.onStderr); |
| 1357 |
| 1358 // Turn on the periodic poll timer for this page. |
| 1359 pollPeriod = const Duration(milliseconds:100); |
1272 | 1360 |
1273 onClick.listen((event) { | 1361 onClick.listen((event) { |
1274 // Random clicks should focus on the text box. If the user selects | 1362 // Random clicks should focus on the text box. If the user selects |
1275 // a range, don't interfere. | 1363 // a range, don't interfere. |
1276 var selection = window.getSelection(); | 1364 var selection = window.getSelection(); |
1277 if (selection == null || selection.type == 'Caret') { | 1365 if (selection == null || selection.type == 'Caret') { |
1278 debugger.input.focus(); | 1366 debugger.input.focus(); |
1279 } | 1367 } |
1280 }); | 1368 }); |
1281 } | 1369 } |
1282 | 1370 |
| 1371 void onPoll() { |
| 1372 debugger.flushStdio(); |
| 1373 } |
| 1374 |
1283 @override | 1375 @override |
1284 void detached() { | 1376 void detached() { |
1285 cancelFutureSubscription(_isolateSubscriptionFuture); | 1377 cancelFutureSubscription(_isolateSubscriptionFuture); |
1286 _isolateSubscriptionFuture = null; | 1378 _isolateSubscriptionFuture = null; |
1287 cancelFutureSubscription(_debugSubscriptionFuture); | 1379 cancelFutureSubscription(_debugSubscriptionFuture); |
1288 _debugSubscriptionFuture = null; | 1380 _debugSubscriptionFuture = null; |
| 1381 cancelFutureSubscription(_stdoutSubscriptionFuture); |
| 1382 _stdoutSubscriptionFuture = null; |
| 1383 cancelFutureSubscription(_stderrSubscriptionFuture); |
| 1384 _stderrSubscriptionFuture = null; |
1289 super.detached(); | 1385 super.detached(); |
1290 } | 1386 } |
1291 } | 1387 } |
1292 | 1388 |
1293 @CustomTag('debugger-stack') | 1389 @CustomTag('debugger-stack') |
1294 class DebuggerStackElement extends ObservatoryElement { | 1390 class DebuggerStackElement extends ObservatoryElement { |
1295 @published Isolate isolate; | 1391 @published Isolate isolate; |
1296 @observable bool hasStack = false; | 1392 @observable bool hasStack = false; |
1297 @observable bool hasMessages = false; | 1393 @observable bool hasMessages = false; |
1298 @observable bool isSampled = false; | 1394 @observable bool isSampled = false; |
(...skipping 359 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1658 var span = new SpanElement(); | 1754 var span = new SpanElement(); |
1659 span.classes.add('red'); | 1755 span.classes.add('red'); |
1660 span.appendText(line); | 1756 span.appendText(line); |
1661 if (newline) { | 1757 if (newline) { |
1662 span.appendText('\n'); | 1758 span.appendText('\n'); |
1663 } | 1759 } |
1664 $['consoleText'].children.add(span); | 1760 $['consoleText'].children.add(span); |
1665 span.scrollIntoView(); | 1761 span.scrollIntoView(); |
1666 } | 1762 } |
1667 | 1763 |
| 1764 void printStdio(List<String> lines) { |
| 1765 var lastSpan; |
| 1766 for (var line in lines) { |
| 1767 var span = new SpanElement(); |
| 1768 span.classes.add('green'); |
| 1769 span.appendText(line); |
| 1770 span.appendText('\n'); |
| 1771 $['consoleText'].children.add(span); |
| 1772 lastSpan = span; |
| 1773 } |
| 1774 if (lastSpan != null) { |
| 1775 lastSpan.scrollIntoView(); |
| 1776 } |
| 1777 } |
| 1778 |
1668 void printRef(Instance ref, { bool newline:true }) { | 1779 void printRef(Instance ref, { bool newline:true }) { |
1669 var refElement = new Element.tag('instance-ref'); | 1780 var refElement = new Element.tag('instance-ref'); |
1670 refElement.ref = ref; | 1781 refElement.ref = ref; |
1671 $['consoleText'].children.add(refElement); | 1782 $['consoleText'].children.add(refElement); |
1672 if (newline) { | 1783 if (newline) { |
1673 this.newline(); | 1784 this.newline(); |
1674 } | 1785 } |
1675 refElement.scrollIntoView(); | 1786 refElement.scrollIntoView(); |
1676 } | 1787 } |
1677 | 1788 |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1740 }); | 1851 }); |
1741 } | 1852 } |
1742 | 1853 |
1743 void focus() { | 1854 void focus() { |
1744 $['textBox'].focus(); | 1855 $['textBox'].focus(); |
1745 } | 1856 } |
1746 | 1857 |
1747 DebuggerInputElement.created() : super.created(); | 1858 DebuggerInputElement.created() : super.created(); |
1748 } | 1859 } |
1749 | 1860 |
OLD | NEW |