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

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 175 matching lines...) Expand 10 before | Expand all | Expand 10 after
329 processArgs.addAll(settings['vmargs'].split(' ')); 346 processArgs.addAll(settings['vmargs'].split(' '));
330 } 347 }
331 processArgs.add(settings['script']); 348 processArgs.add(settings['script']);
332 if (settings['args'] != null) { 349 if (settings['args'] != null) {
333 processArgs.addAll(settings['args'].split(' ')); 350 processArgs.addAll(settings['args'].split(' '));
334 } 351 }
335 String vm = settings['vm']; 352 String vm = settings['vm'];
336 353
337 isDebugging = true; 354 isDebugging = true;
338 cmdo.hide(); 355 cmdo.hide();
339 return Process.start(vm, processArgs).then((Process process) { 356 return Process.start(vm, processArgs).then((process) {
340 print("Started process ${process.pid} '$vm ${processArgs.join(' ')}'"); 357 print("Started process ${process.pid} '$vm ${processArgs.join(' ')}'");
341 targetProcess = process; 358 targetProcess = process;
342 process.stdin.close(); 359 process.stdin.close();
343 360
344 // TODO(turnidge): For now we only show full lines of output 361 // TODO(turnidge): For now we only show full lines of output
345 // from the debugged process. Should show each character. 362 // from the debugged process. Should show each character.
346 process.stdout 363 process.stdout
347 .transform(UTF8.decoder) 364 .transform(UTF8.decoder)
348 .transform(new LineSplitter()) 365 .transform(new LineSplitter())
349 .listen((String line) { 366 .listen((String line) {
(...skipping 155 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 // Send the source command
986 var sourceResponse = sendCmd(sourceCmd).then((response) {
987 Map result = response["result"];
988 script.source = result['text'];
989 // Line numbers are 1-based so add a dummy for line 0.
990 script.lineToSource = [''];
991 script.lineToSource.addAll(script.source.split('\n'));
992 });
993
994 // Send the line numbers command
995 var lineNumberResponse = sendCmd(lineNumberCmd).then((response) {
996 Map result = response["result"];
997 script.tokenToLine = parseLineNumberTable(result['lines']);
998 });
999
1000 script = new TargetScript();
1001 return Future.wait([sourceResponse, lineNumberResponse]).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((script) {
1012 var lineNumber = script.tokenToLine[location['tokenOffset']];
1013 var text = script.lineToSource[lineNumber];
1014 if (label != null) {
1015 var fileName = location['url'].split("/").last;
1016 print("$label \n"
1017 " at $fileName:$lineNumber");
1018 }
1019 print("${_leftJustify(lineNumber, 8)}$text");
1020 });
1021 }
1022
1023
1024 Future handlePausedEvent(msg) {
928 assert(msg["params"] != null); 1025 assert(msg["params"] != null);
929 var reason = msg["params"]["reason"]; 1026 var reason = msg["params"]["reason"];
930 int isolateId = msg["params"]["isolateId"]; 1027 int isolateId = msg["params"]["isolateId"];
931 assert(isolateId != null); 1028 assert(isolateId != null);
932 var isolate = targetIsolates[isolateId]; 1029 var isolate = targetIsolates[isolateId];
933 assert(isolate != null); 1030 assert(isolate != null);
934 assert(!isolate.isPaused); 1031 assert(!isolate.isPaused);
935 var location = msg["params"]["location"];; 1032 var location = msg["params"]["location"];;
936 assert(location != null); 1033 assert(location != null);
937 isolate.pausedLocation = location; 1034 isolate.pausedLocation = location;
938 if (reason == "breakpoint") { 1035 if (reason == "breakpoint") {
939 print("Isolate $isolateId paused on breakpoint"); 1036 return printLocation((stepMode ? null : "Breakpoint"), location);
940 print("location: ${formatLocation(location)}");
941 } else if (reason == "interrupted") { 1037 } else if (reason == "interrupted") {
942 print("Isolate $isolateId paused due to an interrupt"); 1038 stepMode = false;
943 print("location: ${formatLocation(location)}"); 1039 return printLocation("Interrupted", location);
944 } else { 1040 } else {
945 assert(reason == "exception"); 1041 assert(reason == "exception");
946 var excObj = msg["params"]["exception"]; 1042 var excObj = msg["params"]["exception"];
947 print("Isolate $isolateId paused on exception"); 1043 print("Isolate $isolateId paused on exception");
948 print(remoteObject(excObj)); 1044 print(remoteObject(excObj));
1045 return new Future.value();
949 } 1046 }
950 } 1047 }
951 1048
952 void handleIsolateEvent(msg) { 1049 void handleIsolateEvent(msg) {
953 Map params = msg["params"]; 1050 Map params = msg["params"];
954 assert(params != null); 1051 assert(params != null);
955 var isolateId = params["id"]; 1052 var isolateId = params["id"];
956 var reason = params["reason"]; 1053 var reason = params["reason"];
957 if (reason == "created") { 1054 if (reason == "created") {
958 print("Isolate $isolateId has been created."); 1055 print("Isolate $isolateId has been created.");
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
994 } 1091 }
995 var event = msg["event"]; 1092 var event = msg["event"];
996 if (event == "isolate") { 1093 if (event == "isolate") {
997 cmdo.hide(); 1094 cmdo.hide();
998 handleIsolateEvent(msg); 1095 handleIsolateEvent(msg);
999 cmdo.show(); 1096 cmdo.show();
1000 return; 1097 return;
1001 } 1098 }
1002 if (event == "paused") { 1099 if (event == "paused") {
1003 cmdo.hide(); 1100 cmdo.hide();
1004 handlePausedEvent(msg); 1101 handlePausedEvent(msg).then((_) {
1005 cmdo.show(); 1102 cmdo.show();
1103 });
1006 return; 1104 return;
1007 } 1105 }
1008 if (event == "breakpointResolved") { 1106 if (event == "breakpointResolved") {
1009 Map params = msg["params"]; 1107 Map params = msg["params"];
1010 assert(params != null); 1108 assert(params != null);
1011 var isolateId = params["isolateId"]; 1109 var isolateId = params["isolateId"];
1012 var location = formatLocation(params["location"]); 1110 var location = formatLocation(params["location"]);
1013 cmdo.hide(); 1111 cmdo.hide();
1014 print("BP ${params["breakpointId"]} resolved in isolate $isolateId" 1112 print("Breakpoint ${params["breakpointId"]} resolved in isolate $isolateId"
1015 " at $location."); 1113 " at $location.");
1016 cmdo.show(); 1114 cmdo.show();
1017 return; 1115 return;
1018 } 1116 }
1019 if (msg["id"] != null) { 1117 if (msg["id"] != null) {
1020 var id = msg["id"]; 1118 var id = msg["id"];
1021 if (outstandingCommands.containsKey(id)) { 1119 if (outstandingCommands.containsKey(id)) {
1022 var completer = outstandingCommands.remove(id); 1120 var completer = outstandingCommands.remove(id);
1023 if (msg["error"] != null) { 1121 if (msg["error"] != null) {
1024 print("VM says: ${msg["error"]}"); 1122 print("VM says: ${msg["error"]}");
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after
1260 cleanupFutures.add(future); 1358 cleanupFutures.add(future);
1261 } 1359 }
1262 1360
1263 vmSubscription = null; 1361 vmSubscription = null;
1264 vmSock = null; 1362 vmSock = null;
1265 outstandingCommands = null; 1363 outstandingCommands = null;
1266 1364
1267 return Future.wait(cleanupFutures); 1365 return Future.wait(cleanupFutures);
1268 } 1366 }
1269 1367
1368 void debuggerError(self, parent, zone, error, StackTrace trace) {
1369 print('\n--------\nExiting due to unexpected error:\n'
1370 ' $error\n$trace\n');
1371 debuggerQuit();
1372 }
1373
1270 Future debuggerQuit() { 1374 Future debuggerQuit() {
1271 // Kill target process, if any. 1375 // Kill target process, if any.
1272 if (targetProcess != null) { 1376 if (targetProcess != null) {
1273 if (!targetProcess.kill()) { 1377 if (!targetProcess.kill()) {
1274 print('Unable to kill process ${targetProcess.pid}'); 1378 print('Unable to kill process ${targetProcess.pid}');
1275 } 1379 }
1276 } 1380 }
1277 1381
1278 // Restore terminal settings, close connections. 1382 // Restore terminal settings, close connections.
1279 return Future.wait([closeCommando(), closeVmSocket()]).then((_) { 1383 return Future.wait([closeCommando(), closeVmSocket()]).then((_) {
(...skipping 12 matching lines...) Expand all
1292 pos++; 1396 pos++;
1293 } 1397 }
1294 if (pos < args.length) { 1398 if (pos < args.length) {
1295 settings['vmargs'] = args.getRange(0, pos).join(' '); 1399 settings['vmargs'] = args.getRange(0, pos).join(' ');
1296 settings['script'] = args[pos]; 1400 settings['script'] = args[pos];
1297 settings['args'] = args.getRange(pos + 1, args.length).join(' '); 1401 settings['args'] = args.getRange(pos + 1, args.length).join(' ');
1298 } 1402 }
1299 } 1403 }
1300 1404
1301 void main(List<String> args) { 1405 void main(List<String> args) {
1302 parseArgs(args); 1406 // Setup a zone which will exit the debugger cleanly on any uncaught
1407 // exception.
1408 var zone = Zone.ROOT.fork(specification:new ZoneSpecification(
1409 handleUncaughtError: debuggerError));
1303 1410
1304 cmdo = new Commando(completer: debuggerCommandCompleter); 1411 zone.run(() {
1305 cmdSubscription = cmdo.commands.listen(processCommand, 1412 parseArgs(args);
1306 onError: processError, 1413 cmdo = new Commando(completer: debuggerCommandCompleter);
1307 onDone: processDone); 1414 cmdSubscription = cmdo.commands.listen(processCommand,
1415 onError: processError,
1416 onDone: processDone);
1417 });
1308 } 1418 }
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