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

Side by Side Diff: tools/ddbg.dart

Issue 106743008: When we hit a breakpoint in the debugger, show the source of current line. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 // Simple interactive debugger shell that connects to the Dart VM's debugger 5 // Simple interactive debugger shell that connects to the Dart VM's debugger
6 // connection port. 6 // connection port.
7 7
8 import "dart:convert"; 8 import "dart:convert";
9 import "dart:io"; 9 import "dart:io";
10 import "dart:async"; 10 import "dart:async";
11 import "dart:math"; 11 import "dart:math";
12 12
13 import "ddbg/lib/commando.dart"; 13 import "ddbg/lib/commando.dart";
14 14
15 class TargetScript {
16 // The text of a script.
17 String source = null;
18
19 // A mapping from line number to source text.
20 List<String> lineToSource = null;
21
22 // A mapping from token offset to line number.
23 Map<int,int> tokenToLine = null;
24 }
25
26
15 class TargetIsolate { 27 class TargetIsolate {
16 int id; 28 int id;
17 // The location of the last paused event. 29 // The location of the last paused event.
18 Map pausedLocation = null; 30 Map pausedLocation = null;
19 31
20 TargetIsolate(this.id); 32 TargetIsolate(this.id);
21 bool get isPaused => pausedLocation != null; 33 bool get isPaused => pausedLocation != null;
34
35 Map<String, TargetScript> scripts = {};
22 } 36 }
23 37
24 Map<int, TargetIsolate> targetIsolates= new Map<int, TargetIsolate>(); 38 Map<int, TargetIsolate> targetIsolates= new Map<int, TargetIsolate>();
25 39
26 Map<int, Completer> outstandingCommands; 40 Map<int, Completer> outstandingCommands;
27 41
28 Socket vmSock; 42 Socket vmSock;
29 String vmData; 43 String vmData;
30 var cmdSubscription; 44 var cmdSubscription;
31 Commando cmdo; 45 Commando cmdo;
32 var vmSubscription; 46 var vmSubscription;
33 int seqNum = 0; 47 int seqNum = 0;
34 48
35 bool isDebugging = false; 49 bool isDebugging = false;
50 bool stepMode = false;
36 Process targetProcess = null; 51 Process targetProcess = null;
37 bool suppressNextExitCode = false; 52 bool suppressNextExitCode = false;
38 53
39 final verbose = false; 54 final verbose = false;
40 final printMessages = false; 55 final printMessages = false;
41 56
42 TargetIsolate currentIsolate; 57 TargetIsolate currentIsolate;
43 TargetIsolate mainIsolate; 58 TargetIsolate mainIsolate;
44 59
45 int debugPort = 5858; 60 int debugPort = 5858;
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
78 print("Current isolate must be paused"); 93 print("Current isolate must be paused");
79 return false; 94 return false;
80 } 95 }
81 96
82 // These settings are allowed in the 'set' and 'show' debugger commands. 97 // These settings are allowed in the 'set' and 'show' debugger commands.
83 var validSettings = ['vm', 'vmargs', 'script', 'args']; 98 var validSettings = ['vm', 'vmargs', 'script', 'args'];
84 99
85 // The current values for all settings. 100 // The current values for all settings.
86 var settings = new Map(); 101 var settings = new Map();
87 102
88 // Generates a string of 'count' spaces. 103 String _leftJustify(text, int width) {
89 String _spaces(int count) { 104 StringBuffer buffer = new StringBuffer();
90 return new List.filled(count, ' ').join(''); 105 buffer.write(text);
106 while (buffer.length < width) {
107 buffer.write(' ');
108 }
109 return buffer.toString();
91 } 110 }
92 111
93 // TODO(turnidge): Move all commands here. 112 // TODO(turnidge): Move all commands here.
94 List<Command> commandList = 113 List<Command> commandList =
95 [ new HelpCommand(), 114 [ new HelpCommand(),
96 new QuitCommand(), 115 new QuitCommand(),
97 new RunCommand(), 116 new RunCommand(),
98 new KillCommand(), 117 new KillCommand(),
99 new ConnectCommand(), 118 new ConnectCommand(),
100 new DisconnectCommand(), 119 new DisconnectCommand(),
(...skipping 30 matching lines...) Expand all
131 150
132 Usage: 151 Usage:
133 help 152 help
134 help <command> 153 help <command>
135 """; 154 """;
136 155
137 Future run(List<String> args) { 156 Future run(List<String> args) {
138 if (args.length == 1) { 157 if (args.length == 1) {
139 print("Debugger commands:\n"); 158 print("Debugger commands:\n");
140 for (var command in commandList) { 159 for (var command in commandList) {
141 const tabStop = 12; 160 print(' ${_leftJustify(command.name, 11)} ${command.helpShort}');
142 var spaces = _spaces(max(1, (tabStop - command.name.length)));
143 print(' ${command.name}${spaces}${command.helpShort}');
144 } 161 }
145 162
146 // TODO(turnidge): Convert all commands to use the Command class. 163 // TODO(turnidge): Convert all commands to use the Command class.
147 print(""" 164 print("""
148 bt Show backtrace 165 bt Show backtrace
149 r Resume execution 166 r Resume execution
150 s Single step 167 s Single step
151 so Step over 168 so Step over
152 si Step into 169 si Step into
153 sbp [<file>] <line> Set breakpoint 170 sbp [<file>] <line> Set breakpoint
(...skipping 351 matching lines...) Expand 10 before | Expand all | Expand 10 after
505 var args = cmdLine.split(' '); 522 var args = cmdLine.split(' ');
506 if (args.length == 0) { 523 if (args.length == 0) {
507 return; 524 return;
508 } 525 }
509 var command = args[0]; 526 var command = args[0];
510 527
511 var resume_commands = 528 var resume_commands =
512 { 'r':'resume', 's':'stepOver', 'si':'stepInto', 'so':'stepOut'}; 529 { 'r':'resume', 's':'stepOver', 'si':'stepInto', 'so':'stepOut'};
513 if (resume_commands[command] != null) { 530 if (resume_commands[command] != null) {
514 if (!checkPaused()) return; 531 if (!checkPaused()) return;
532 // TODO(turnidge): step mode isn't quite right yet.
533 stepMode = (command != 'r');
515 var cmd = { "id": seqNum, 534 var cmd = { "id": seqNum,
516 "command": resume_commands[command], 535 "command": resume_commands[command],
517 "params": { "isolateId" : currentIsolate.id } }; 536 "params": { "isolateId" : currentIsolate.id } };
518 sendCmd(cmd).then(showPromptAfter(handleResumedResponse)); 537 sendCmd(cmd).then(showPromptAfter(handleResumedResponse));
519 } else if (command == "bt") { 538 } else if (command == "bt") {
520 var cmd = { "id": seqNum, 539 var cmd = { "id": seqNum,
521 "command": "getStackTrace", 540 "command": "getStackTrace",
522 "params": { "isolateId" : currentIsolate.id } }; 541 "params": { "isolateId" : currentIsolate.id } };
523 sendCmd(cmd).then(showPromptAfter(handleStackTraceResponse)); 542 sendCmd(cmd).then(showPromptAfter(handleStackTraceResponse));
524 } else if (command == "ll") { 543 } else if (command == "ll") {
(...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after
902 Map result = response["result"]; 921 Map result = response["result"];
903 List callFrames = result["callFrames"]; 922 List callFrames = result["callFrames"];
904 assert(callFrames != null); 923 assert(callFrames != null);
905 printStackTrace(callFrames); 924 printStackTrace(callFrames);
906 } 925 }
907 926
908 927
909 void printStackFrame(frame_num, Map frame) { 928 void printStackFrame(frame_num, Map frame) {
910 var fname = frame["functionName"]; 929 var fname = frame["functionName"];
911 var loc = formatLocation(frame["location"]); 930 var loc = formatLocation(frame["location"]);
912 print("$frame_num $fname ($loc)"); 931 print("#${_leftJustify(frame_num,2)} $fname at $loc");
913 List locals = frame["locals"]; 932 List locals = frame["locals"];
914 for (int i = 0; i < locals.length; i++) { 933 for (int i = 0; i < locals.length; i++) {
915 printNamedObject(locals[i]); 934 printNamedObject(locals[i]);
916 } 935 }
917 } 936 }
918 937
919 938
920 void printStackTrace(List frames) { 939 void printStackTrace(List frames) {
921 for (int i = 0; i < frames.length; i++) { 940 for (int i = 0; i < frames.length; i++) {
922 printStackFrame(i, frames[i]); 941 printStackFrame(i, frames[i]);
923 } 942 }
924 } 943 }
925 944
926 945
927 void handlePausedEvent(msg) { 946 Map<int, int> parseLineNumberTable(List<List<int>> table) {
947 Map tokenToLine = {};
948 for (var line in table) {
949 // Each entry begins with a line number...
950 var lineNumber = line[0];
951 for (var pos = 1; pos < line.length; pos += 2) {
952 // ...and is followed by (token offset, col number) pairs.
953 // We ignore the column numbers.
954 var tokenOffset = line[pos];
955 tokenToLine[tokenOffset] = lineNumber;
956 }
957 }
958 return tokenToLine;
959 }
960
961
962 Future<TargetScript> getTargetScript(Map location) {
963 var isolate = targetIsolates[currentIsolate.id];
964 var url = location['url'];
965 var script = isolate.scripts[url];
966 if (script != null) {
967 return new Future.value(script);
968 }
969
970 // Ask the vm for the source and line number table.
971 var sourceCmd = {
972 "id": seqNum++,
973 "command": "getScriptSource",
974 "params": { "isolateId": currentIsolate.id,
975 "libraryId": location['libraryId'],
976 "url": url } };
977
978 var lineNumberCmd = {
979 "id": seqNum++,
980 "command": "getLineNumberTable",
981 "params": { "isolateId": currentIsolate.id,
982 "libraryId": location['libraryId'],
983 "url": url } };
984
985 script = new TargetScript();
986 return Future.wait([
hausner 2013/12/05 23:57:53 Your comments helped a lot to understand the contr
Bob Nystrom 2013/12/06 00:08:09 Drive-by!
turnidge 2013/12/06 17:19:43 Done.
turnidge 2013/12/06 17:19:43 Done.
987 // Send the source command
988 sendCmd(sourceCmd).then(
989 (Map response) {
990 Map result = response["result"];
991 script.source = result['text'];
992 // Line numbers are 1-based so add a dummy for line 0.
993 script.lineToSource = [''];
994 script.lineToSource.addAll(script.source.split('\n'));
995 }),
996 // Send the line numbers command
997 sendCmd(lineNumberCmd).then(
998 (Map response) {
999 Map result = response["result"];
1000 script.tokenToLine = parseLineNumberTable(result['lines']);
1001 })]).then((_) {
1002 // When both commands complete, cache the result.
1003 isolate.scripts[url] = script;
1004 return script;
1005 });
1006 }
1007
1008
1009 Future printLocation(String label, Map location) {
1010 // Figure out the line number.
1011 return getTargetScript(location).then(
1012 (script) {
1013 var lineNumber = script.tokenToLine[location['tokenOffset']];
1014 var text = script.lineToSource[lineNumber];
1015 if (label != null) {
1016 var fileName = location['url'].split("/").last;
1017 print("$label \n"
1018 " at $fileName:$lineNumber");
1019 }
1020 print("${_leftJustify(lineNumber, 8)}$text");
1021 });
1022 }
1023
1024
1025 Future handlePausedEvent(msg) {
928 assert(msg["params"] != null); 1026 assert(msg["params"] != null);
929 var reason = msg["params"]["reason"]; 1027 var reason = msg["params"]["reason"];
930 int isolateId = msg["params"]["isolateId"]; 1028 int isolateId = msg["params"]["isolateId"];
931 assert(isolateId != null); 1029 assert(isolateId != null);
932 var isolate = targetIsolates[isolateId]; 1030 var isolate = targetIsolates[isolateId];
933 assert(isolate != null); 1031 assert(isolate != null);
934 assert(!isolate.isPaused); 1032 assert(!isolate.isPaused);
935 var location = msg["params"]["location"];; 1033 var location = msg["params"]["location"];;
936 assert(location != null); 1034 assert(location != null);
937 isolate.pausedLocation = location; 1035 isolate.pausedLocation = location;
938 if (reason == "breakpoint") { 1036 if (reason == "breakpoint") {
939 print("Isolate $isolateId paused on breakpoint"); 1037 return printLocation((stepMode ? null : "Breakpoint"), location);
hausner 2013/12/05 23:57:53 A cleaner way would be to add to the wire protocol
turnidge 2013/12/06 17:19:43 That would be nice. Also, it might be helpful to
940 print("location: ${formatLocation(location)}");
941 } else if (reason == "interrupted") { 1038 } else if (reason == "interrupted") {
942 print("Isolate $isolateId paused due to an interrupt"); 1039 stepMode = false;
943 print("location: ${formatLocation(location)}"); 1040 return printLocation("Interrupted", location);
944 } else { 1041 } else {
945 assert(reason == "exception"); 1042 assert(reason == "exception");
946 var excObj = msg["params"]["exception"]; 1043 var excObj = msg["params"]["exception"];
947 print("Isolate $isolateId paused on exception"); 1044 print("Isolate $isolateId paused on exception");
948 print(remoteObject(excObj)); 1045 print(remoteObject(excObj));
1046 return new Future.value();
949 } 1047 }
950 } 1048 }
951 1049
952 void handleIsolateEvent(msg) { 1050 void handleIsolateEvent(msg) {
953 Map params = msg["params"]; 1051 Map params = msg["params"];
954 assert(params != null); 1052 assert(params != null);
955 var isolateId = params["id"]; 1053 var isolateId = params["id"];
956 var reason = params["reason"]; 1054 var reason = params["reason"];
957 if (reason == "created") { 1055 if (reason == "created") {
958 print("Isolate $isolateId has been created."); 1056 print("Isolate $isolateId has been created.");
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
994 } 1092 }
995 var event = msg["event"]; 1093 var event = msg["event"];
996 if (event == "isolate") { 1094 if (event == "isolate") {
997 cmdo.hide(); 1095 cmdo.hide();
998 handleIsolateEvent(msg); 1096 handleIsolateEvent(msg);
999 cmdo.show(); 1097 cmdo.show();
1000 return; 1098 return;
1001 } 1099 }
1002 if (event == "paused") { 1100 if (event == "paused") {
1003 cmdo.hide(); 1101 cmdo.hide();
1004 handlePausedEvent(msg); 1102 handlePausedEvent(msg).then((_) {
1005 cmdo.show(); 1103 cmdo.show();
1104 });
1006 return; 1105 return;
1007 } 1106 }
1008 if (event == "breakpointResolved") { 1107 if (event == "breakpointResolved") {
1009 Map params = msg["params"]; 1108 Map params = msg["params"];
1010 assert(params != null); 1109 assert(params != null);
1011 var isolateId = params["isolateId"]; 1110 var isolateId = params["isolateId"];
1012 var location = formatLocation(params["location"]); 1111 var location = formatLocation(params["location"]);
1013 cmdo.hide(); 1112 cmdo.hide();
1014 print("BP ${params["breakpointId"]} resolved in isolate $isolateId" 1113 print("Breakpoint ${params["breakpointId"]} resolved in isolate $isolateId"
1015 " at $location."); 1114 " at $location.");
1016 cmdo.show(); 1115 cmdo.show();
1017 return; 1116 return;
1018 } 1117 }
1019 if (msg["id"] != null) { 1118 if (msg["id"] != null) {
1020 var id = msg["id"]; 1119 var id = msg["id"];
1021 if (outstandingCommands.containsKey(id)) { 1120 if (outstandingCommands.containsKey(id)) {
1022 var completer = outstandingCommands.remove(id); 1121 var completer = outstandingCommands.remove(id);
1023 if (msg["error"] != null) { 1122 if (msg["error"] != null) {
1024 print("VM says: ${msg["error"]}"); 1123 print("VM says: ${msg["error"]}");
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after
1260 cleanupFutures.add(future); 1359 cleanupFutures.add(future);
1261 } 1360 }
1262 1361
1263 vmSubscription = null; 1362 vmSubscription = null;
1264 vmSock = null; 1363 vmSock = null;
1265 outstandingCommands = null; 1364 outstandingCommands = null;
1266 1365
1267 return Future.wait(cleanupFutures); 1366 return Future.wait(cleanupFutures);
1268 } 1367 }
1269 1368
1369 void debuggerError(error, StackTrace trace) {
1370 print('\n--------\nExiting due to unexpected error:\n'
1371 ' $error\n$trace\n');
1372 debuggerQuit();
1373 }
1374
1270 Future debuggerQuit() { 1375 Future debuggerQuit() {
1271 // Kill target process, if any. 1376 // Kill target process, if any.
1272 if (targetProcess != null) { 1377 if (targetProcess != null) {
1273 if (!targetProcess.kill()) { 1378 if (!targetProcess.kill()) {
1274 print('Unable to kill process ${targetProcess.pid}'); 1379 print('Unable to kill process ${targetProcess.pid}');
1275 } 1380 }
1276 } 1381 }
1277 1382
1278 // Restore terminal settings, close connections. 1383 // Restore terminal settings, close connections.
1279 return Future.wait([closeCommando(), closeVmSocket()]).then((_) { 1384 return Future.wait([closeCommando(), closeVmSocket()]).then((_) {
(...skipping 12 matching lines...) Expand all
1292 pos++; 1397 pos++;
1293 } 1398 }
1294 if (pos < args.length) { 1399 if (pos < args.length) {
1295 settings['vmargs'] = args.getRange(0, pos).join(' '); 1400 settings['vmargs'] = args.getRange(0, pos).join(' ');
1296 settings['script'] = args[pos]; 1401 settings['script'] = args[pos];
1297 settings['args'] = args.getRange(pos + 1, args.length).join(' '); 1402 settings['args'] = args.getRange(pos + 1, args.length).join(' ');
1298 } 1403 }
1299 } 1404 }
1300 1405
1301 void main(List<String> args) { 1406 void main(List<String> args) {
1302 parseArgs(args); 1407 // Setup a zone which will exit the debugger cleanly on any uncaught
1408 // exception.
1409 var zone =
1410 Zone.ROOT.fork(
1411 specification:new ZoneSpecification(
1412 handleUncaughtError:
1413 (self, parent, zone, error, trace) {
1414 debuggerError(error, trace);
1415 }));
1303 1416
1304 cmdo = new Commando(completer: debuggerCommandCompleter); 1417 zone.run(() {
1305 cmdSubscription = cmdo.commands.listen(processCommand, 1418 parseArgs(args);
1306 onError: processError, 1419 cmdo = new Commando(completer: debuggerCommandCompleter);
1307 onDone: processDone); 1420 cmdSubscription = cmdo.commands.listen(processCommand,
1421 onError: processError,
1422 onDone: processDone);
1423 });
1308 } 1424 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698