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

Side by Side Diff: runtime/bin/vmservice/client/lib/src/service/object.dart

Issue 381383010: Add breakpoints and single-stepping to Observatory. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 5 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
OLDNEW
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 part of service; 5 part of service;
6 6
7 /// A [ServiceObject] is an object known to the VM service and is tied 7 /// A [ServiceObject] is an object known to the VM service and is tied
8 /// to an owning [Isolate]. 8 /// to an owning [Isolate].
9 abstract class ServiceObject extends Observable { 9 abstract class ServiceObject extends Observable {
10 static int LexicalSortName(ServiceObject o1, ServiceObject o2) { 10 static int LexicalSortName(ServiceObject o1, ServiceObject o2) {
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after
256 map = _parseJSON(eventMessage); 256 map = _parseJSON(eventMessage);
257 } catch (e, st) { 257 } catch (e, st) {
258 Logger.root.severe('Ignoring malformed event message: ${eventMessage}'); 258 Logger.root.severe('Ignoring malformed event message: ${eventMessage}');
259 return; 259 return;
260 } 260 }
261 if (map['type'] != 'ServiceEvent') { 261 if (map['type'] != 'ServiceEvent') {
262 Logger.root.severe( 262 Logger.root.severe(
263 "Expected 'ServiceEvent' but found '${map['type']}'"); 263 "Expected 'ServiceEvent' but found '${map['type']}'");
264 return; 264 return;
265 } 265 }
266
266 // Extract the owning isolate from the event itself. 267 // Extract the owning isolate from the event itself.
267 String owningIsolateId = map['isolate']['id']; 268 String owningIsolateId = map['isolate']['id'];
268 _getIsolate(owningIsolateId).then((owningIsolate) { 269 _getIsolate(owningIsolateId).then((owningIsolate) {
269 var event = new ServiceObject._fromMap(owningIsolate, map); 270 var event = new ServiceObject._fromMap(owningIsolate, map);
270 events.add(event); 271 events.add(event);
271 }); 272 });
272 } 273 }
273 274
274 static final RegExp _currentIsolateMatcher = new RegExp(r'isolates/\d+'); 275 static final RegExp _currentIsolateMatcher = new RegExp(r'isolates/\d+');
275 static final RegExp _currentObjectMatcher = new RegExp(r'isolates/\d+/'); 276 static final RegExp _currentObjectMatcher = new RegExp(r'isolates/\d+/');
(...skipping 466 matching lines...) Expand 10 before | Expand all | Expand 10 after
742 void _update(ObservableMap map, bool mapIsRef) { 743 void _update(ObservableMap map, bool mapIsRef) {
743 mainPort = map['mainPort']; 744 mainPort = map['mainPort'];
744 name = map['name']; 745 name = map['name'];
745 vmName = map['name']; 746 vmName = map['name'];
746 if (mapIsRef) { 747 if (mapIsRef) {
747 return; 748 return;
748 } 749 }
749 _loaded = true; 750 _loaded = true;
750 loading = false; 751 loading = false;
751 752
753 reloadBreakpoints();
754
752 // Remap DebuggerEvent to ServiceEvent so that the observatory can 755 // Remap DebuggerEvent to ServiceEvent so that the observatory can
753 // work against 1.5 vms in the short term. 756 // work against 1.5 vms in the short term.
754 // 757 //
755 // TODO(turnidge): Remove this when no longer needed. 758 // TODO(turnidge): Remove this when no longer needed.
756 var pause = map['pauseEvent']; 759 var pause = map['pauseEvent'];
757 if (pause != null) { 760 if (pause != null) {
758 if (pause['type'] == 'DebuggerEvent') { 761 if (pause['type'] == 'DebuggerEvent') {
759 pause['type'] = 'ServiceEvent'; 762 pause['type'] = 'ServiceEvent';
760 } 763 }
761 } 764 }
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
878 // Number of children. 881 // Number of children.
879 var children = _trieData[_trieDataCursor++]; 882 var children = _trieData[_trieDataCursor++];
880 // Recursively read child nodes. 883 // Recursively read child nodes.
881 for (var i = 0; i < children; i++) { 884 for (var i = 0; i < children; i++) {
882 var child = _readTrieNode(codeTable); 885 var child = _readTrieNode(codeTable);
883 node.children.add(child); 886 node.children.add(child);
884 node.summedChildCount += child.count; 887 node.summedChildCount += child.count;
885 } 888 }
886 return node; 889 return node;
887 } 890 }
891
892 ServiceMap breakpoints;
893
894 void _removeBreakpoint(ServiceMap bpt) {
895 var script = bpt['location']['script'];
896 var tokenPos = bpt['location']['tokenPos'];
897 assert(tokenPos != null);
898 if (script.loaded) {
899 var line = script.tokenToLine(tokenPos);
900 assert(line != null);
901 assert(script.lines[line - 1].bpt == bpt);
902 script.lines[line - 1].bpt = null;
903 }
904 }
905
906 void _addBreakpoint(ServiceMap bpt) {
907 var script = bpt['location']['script'];
908 var tokenPos = bpt['location']['tokenPos'];
909 assert(tokenPos != null);
910 if (script.loaded) {
911 var line = script.tokenToLine(tokenPos);
912 assert(line != null);
913 assert(script.lines[line - 1].bpt == null);
914 script.lines[line - 1].bpt = bpt;
915 } else {
916 // Load the script and then plop in the breakpoint.
917 script.load().then((_) {
918 _addBreakpoint(bpt);
919 });
920 }
921 }
922
923 void _updateBreakpoints(ServiceMap newBreakpoints) {
924 // Remove all of the old breakpoints from the Script lines.
925 if (breakpoints != null) {
926 for (var bpt in breakpoints['breakpoints']) {
927 _removeBreakpoint(bpt);
928 }
929 }
930 // Add all of the new breakpoints to the Script lines.
931 for (var bpt in newBreakpoints['breakpoints']) {
932 _addBreakpoint(bpt);
933 }
934 breakpoints = newBreakpoints;
935 }
936
937 Future<ServiceObject> _inProgressReloadBpts;
938
939 Future reloadBreakpoints() {
940 if (_inProgressReloadBpts == null) {
941 _inProgressReloadBpts =
942 get('debug/breakpoints').then((newBpts) {
943 _updateBreakpoints(newBpts);
944 }).whenComplete(() {
945 _inProgressReloadBpts = null;
946 });
947 }
948 return _inProgressReloadBpts;
949 }
950
951 Future<ServiceObject> setBreakpoint(Script script, int line) {
952 return get(script.id + "/setBreakpoint?line=${line}").then((result) {
953 if (result is DartError) {
954 // Unable to set a breakpoint at desired line.
955 script.lines[line - 1].possibleBpt = false;
956 }
957 return reloadBreakpoints();
958 });
959 }
960
961 Future clearBreakpoint(ServiceMap bpt) {
962 return get('${bpt.id}/clear').then((result) {
963 if (result is DartError) {
964 // TODO(turnidge): Handle this more gracefully.
965 Logger.root.severe(result.message);
966 }
967 if (pauseEvent != null &&
968 pauseEvent.breakpoint != null &&
969 (pauseEvent.breakpoint['id'] == bpt['id'])) {
970 return isolate.reload();
971 } else {
972 return reloadBreakpoints();
973 }
974 });
975 }
976
977 Future pause() {
978 return get("debug/pause").then((result) {
979 if (result is DartError) {
980 // TODO(turnidge): Handle this more gracefully.
981 Logger.root.severe(result.message);
982 }
983 return isolate.reload();
984 });
985 }
986
987 Future resume() {
988 return get("debug/resume").then((result) {
989 if (result is DartError) {
990 // TODO(turnidge): Handle this more gracefully.
991 Logger.root.severe(result.message);
992 }
993 return isolate.reload();
994 });
995 }
996
997 Future stepInto() {
998 print('isolate.stepInto');
999 return get("debug/resume?step=into").then((result) {
1000 if (result is DartError) {
1001 // TODO(turnidge): Handle this more gracefully.
1002 Logger.root.severe(result.message);
1003 }
1004 return isolate.reload();
1005 });
1006 }
1007
1008 Future stepOver() {
1009 return get("debug/resume?step=over").then((result) {
1010 if (result is DartError) {
1011 // TODO(turnidge): Handle this more gracefully.
1012 Logger.root.severe(result.message);
1013 }
1014 return isolate.reload();
1015 });
1016 }
1017
1018 Future stepOut() {
1019 return get("debug/resume?step=out").then((result) {
1020 if (result is DartError) {
1021 // TODO(turnidge): Handle this more gracefully.
1022 Logger.root.severe(result.message);
1023 }
1024 return isolate.reload();
1025 });
1026 }
888 } 1027 }
889 1028
890 /// A [ServiceObject] which implements [ObservableMap]. 1029 /// A [ServiceObject] which implements [ObservableMap].
891 class ServiceMap extends ServiceObject implements ObservableMap { 1030 class ServiceMap extends ServiceObject implements ObservableMap {
892 final ObservableMap _map = new ObservableMap(); 1031 final ObservableMap _map = new ObservableMap();
893 static String objectIdRingPrefix = 'objects/'; 1032 static String objectIdRingPrefix = 'objects/';
894 1033
895 bool get canCache { 1034 bool get canCache {
896 return (_serviceType == 'Class' || 1035 return (_serviceType == 'Class' ||
897 _serviceType == 'Function' || 1036 _serviceType == 'Function' ||
(...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after
1211 } 1350 }
1212 children.add(cls); 1351 children.add(cls);
1213 } 1352 }
1214 1353
1215 Future<ServiceObject> get(String command) { 1354 Future<ServiceObject> get(String command) {
1216 return isolate.get(id + "/$command"); 1355 return isolate.get(id + "/$command");
1217 } 1356 }
1218 } 1357 }
1219 1358
1220 class ScriptLine extends Observable { 1359 class ScriptLine extends Observable {
1360 final Script script;
1221 final int line; 1361 final int line;
1222 final String text; 1362 final String text;
1223 @observable int hits; 1363 @observable int hits;
1224 ScriptLine(this.line, this.text); 1364 @observable ServiceMap bpt;
1365 @observable bool possibleBpt = true;
1366
1367 static bool _isTrivialToken(String token) {
1368 if (token == 'else') {
1369 return true;
1370 }
1371 for (var c in token.split('')) {
1372 switch (c) {
1373 case '{':
1374 case '}':
1375 case '(':
1376 case ')':
1377 case ';':
1378 break;
1379 default:
1380 return false;
1381 }
1382 }
1383 return true;
1384 }
1385
1386 static bool _isTrivialLine(String text) {
1387 var wsTokens = text.split(new RegExp(r"(\s)+"));
1388 for (var wsToken in wsTokens) {
1389 var tokens = wsToken.split(new RegExp(r"(\b)"));
1390 for (var token in tokens) {
1391 if (!_isTrivialToken(token)) {
1392 return false;
1393 }
1394 }
1395 }
1396 return true;
1397 }
1398
1399 ScriptLine(this.script, this.line, this.text) {
1400 possibleBpt = !_isTrivialLine(text);
1401
1402 // TODO(turnidge): This is not so efficient. Consider improving.
1403 for (var bpt in this.script.isolate.breakpoints['breakpoints']) {
1404 var bptScript = bpt['location']['script'];
1405 var bptTokenPos = bpt['location']['tokenPos'];
1406 if (bptScript == this.script &&
1407 bptScript.tokenToLine(bptTokenPos) == line) {
1408 this.bpt = bpt;
1409 }
1410 }
1411 }
1225 } 1412 }
1226 1413
1227 class Script extends ServiceObject with Coverage { 1414 class Script extends ServiceObject with Coverage {
1228 final lines = new ObservableList<ScriptLine>(); 1415 final lines = new ObservableList<ScriptLine>();
1229 final _hits = new Map<int, int>(); 1416 final _hits = new Map<int, int>();
1230 @observable String kind; 1417 @observable String kind;
1231 @observable int firstTokenPos; 1418 @observable int firstTokenPos;
1232 @observable int lastTokenPos; 1419 @observable int lastTokenPos;
1233 bool get canCache => true; 1420 bool get canCache => true;
1234 bool get immutable => true; 1421 bool get immutable => true;
(...skipping 27 matching lines...) Expand all
1262 } 1449 }
1263 1450
1264 void _parseTokenPosTable(List<List<int>> table) { 1451 void _parseTokenPosTable(List<List<int>> table) {
1265 if (table == null) { 1452 if (table == null) {
1266 return; 1453 return;
1267 } 1454 }
1268 _tokenToLine.clear(); 1455 _tokenToLine.clear();
1269 _tokenToCol.clear(); 1456 _tokenToCol.clear();
1270 firstTokenPos = null; 1457 firstTokenPos = null;
1271 lastTokenPos = null; 1458 lastTokenPos = null;
1459 var lineSet = new Set();
1460
1272 for (var line in table) { 1461 for (var line in table) {
1273 // Each entry begins with a line number... 1462 // Each entry begins with a line number...
1274 var lineNumber = line[0]; 1463 var lineNumber = line[0];
1464 lineSet.add(lineNumber);
1275 for (var pos = 1; pos < line.length; pos += 2) { 1465 for (var pos = 1; pos < line.length; pos += 2) {
1276 // ...and is followed by (token offset, col number) pairs. 1466 // ...and is followed by (token offset, col number) pairs.
1277 var tokenOffset = line[pos]; 1467 var tokenOffset = line[pos];
1278 var colNumber = line[pos+1]; 1468 var colNumber = line[pos+1];
1279 if (firstTokenPos == null) { 1469 if (firstTokenPos == null) {
1280 // Mark first token position. 1470 // Mark first token position.
1281 firstTokenPos = tokenOffset; 1471 firstTokenPos = tokenOffset;
1282 lastTokenPos = tokenOffset; 1472 lastTokenPos = tokenOffset;
1283 } else { 1473 } else {
1284 // Keep track of max and min token positions. 1474 // Keep track of max and min token positions.
1285 firstTokenPos = (firstTokenPos <= tokenOffset) ? 1475 firstTokenPos = (firstTokenPos <= tokenOffset) ?
1286 firstTokenPos : tokenOffset; 1476 firstTokenPos : tokenOffset;
1287 lastTokenPos = (lastTokenPos >= tokenOffset) ? 1477 lastTokenPos = (lastTokenPos >= tokenOffset) ?
1288 lastTokenPos : tokenOffset; 1478 lastTokenPos : tokenOffset;
1289 } 1479 }
1290 _tokenToLine[tokenOffset] = lineNumber; 1480 _tokenToLine[tokenOffset] = lineNumber;
1291 _tokenToCol[tokenOffset] = colNumber; 1481 _tokenToCol[tokenOffset] = colNumber;
1292 } 1482 }
1293 } 1483 }
1484
1485 for (var line in lines) {
1486 // Remove possible breakpoints on lines with no tokens.
1487 if (!lineSet.contains(line.line)) {
1488 line.possibleBpt = false;
1489 }
1490 }
1294 } 1491 }
1295 1492
1296 void _processHits(List scriptHits) { 1493 void _processHits(List scriptHits) {
1297 // Update hits table. 1494 // Update hits table.
1298 for (var i = 0; i < scriptHits.length; i += 2) { 1495 for (var i = 0; i < scriptHits.length; i += 2) {
1299 var line = scriptHits[i]; 1496 var line = scriptHits[i];
1300 var hit = scriptHits[i + 1]; // hit status. 1497 var hit = scriptHits[i + 1]; // hit status.
1301 assert(line >= 1); // Lines start at 1. 1498 assert(line >= 1); // Lines start at 1.
1302 var oldHits = _hits[line]; 1499 var oldHits = _hits[line];
1303 if (oldHits != null) { 1500 if (oldHits != null) {
(...skipping 12 matching lines...) Expand all
1316 } 1513 }
1317 var sourceLines = source.split('\n'); 1514 var sourceLines = source.split('\n');
1318 if (sourceLines.length == 0) { 1515 if (sourceLines.length == 0) {
1319 return; 1516 return;
1320 } 1517 }
1321 // We have the source to the script. This is now loaded. 1518 // We have the source to the script. This is now loaded.
1322 _loaded = true; 1519 _loaded = true;
1323 lines.clear(); 1520 lines.clear();
1324 Logger.root.info('Adding ${sourceLines.length} source lines for ${_url}'); 1521 Logger.root.info('Adding ${sourceLines.length} source lines for ${_url}');
1325 for (var i = 0; i < sourceLines.length; i++) { 1522 for (var i = 0; i < sourceLines.length; i++) {
1326 lines.add(new ScriptLine(i + 1, sourceLines[i])); 1523 lines.add(new ScriptLine(this, i + 1, sourceLines[i]));
1327 } 1524 }
1328 _applyHitsToLines(); 1525 _applyHitsToLines();
1329 } 1526 }
1330 1527
1331 void _applyHitsToLines() { 1528 void _applyHitsToLines() {
1332 if (lines.length == 0) {
1333 return;
1334 }
1335 for (var line in lines) { 1529 for (var line in lines) {
1336 var hits = _hits[line.line]; 1530 var hits = _hits[line.line];
1337 line.hits = hits; 1531 line.hits = hits;
1338 } 1532 }
1339 } 1533 }
1340 } 1534 }
1341 1535
1342 class CodeTick { 1536 class CodeTick {
1343 final int address; 1537 final int address;
1344 final int exclusiveTicks; 1538 final int exclusiveTicks;
(...skipping 571 matching lines...) Expand 10 before | Expand all | Expand 10 after
1916 var v = list[i]; 2110 var v = list[i];
1917 if ((v is ObservableMap) && _isServiceMap(v)) { 2111 if ((v is ObservableMap) && _isServiceMap(v)) {
1918 list[i] = owner.getFromMap(v); 2112 list[i] = owner.getFromMap(v);
1919 } else if (v is ObservableList) { 2113 } else if (v is ObservableList) {
1920 _upgradeObservableList(v, owner); 2114 _upgradeObservableList(v, owner);
1921 } else if (v is ObservableMap) { 2115 } else if (v is ObservableMap) {
1922 _upgradeObservableMap(v, owner); 2116 _upgradeObservableMap(v, owner);
1923 } 2117 }
1924 } 2118 }
1925 } 2119 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698