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

Side by Side Diff: tools/ddbg.dart

Issue 99963004: Rework how we run/kill scripts and how we quit in ddbg. (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 | tools/ddbg/lib/commando.dart » ('j') | 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 12
12 import "ddbg/lib/commando.dart"; 13 import "ddbg/lib/commando.dart";
13 14
14 class TargetIsolate { 15 class TargetIsolate {
15 int id; 16 int id;
16 // The location of the last paused event. 17 // The location of the last paused event.
17 Map pausedLocation = null; 18 Map pausedLocation = null;
18 19
19 TargetIsolate(this.id); 20 TargetIsolate(this.id);
20 bool get isPaused => pausedLocation != null; 21 bool get isPaused => pausedLocation != null;
21 } 22 }
22 23
23 Map<int, TargetIsolate> targetIsolates= new Map<int, TargetIsolate>(); 24 Map<int, TargetIsolate> targetIsolates= new Map<int, TargetIsolate>();
24 25
25 Map<int, Completer> outstandingCommands; 26 Map<int, Completer> outstandingCommands;
26 27
27 Socket vmSock; 28 Socket vmSock;
28 String vmData; 29 String vmData;
30 var cmdSubscription;
29 Commando cmdo; 31 Commando cmdo;
30 var vmSubscription; 32 var vmSubscription;
31 int seqNum = 0; 33 int seqNum = 0;
32 34
35 bool isRunning = false;
33 Process targetProcess; 36 Process targetProcess;
37 bool suppressNextExitCode = false;
34 38
35 final verbose = false; 39 final verbose = false;
36 final printMessages = false; 40 final printMessages = false;
37 41
38 TargetIsolate currentIsolate; 42 TargetIsolate currentIsolate;
39 TargetIsolate mainIsolate; 43 TargetIsolate mainIsolate;
40 44
41 45 int debugPort = 5858;
42 void printHelp() {
43 print("""
44 q Quit debugger shell
45 bt Show backtrace
46 r Resume execution
47 s Single step
48 so Step over
49 si Step into
50 sbp [<file>] <line> Set breakpoint
51 rbp <id> Remove breakpoint with given id
52 po <id> Print object info for given id
53 eval obj <id> <expr> Evaluate expr on object id
54 eval cls <id> <expr> Evaluate expr on class id
55 eval lib <id> <expr> Evaluate expr in toplevel of library id
56 pl <id> <idx> [<len>] Print list element/slice
57 pc <id> Print class info for given id
58 ll List loaded libraries
59 plib <id> Print library info for given library id
60 slib <id> <true|false> Set library id debuggable
61 pg <id> Print all global variables visible within given library id
62 ls <lib_id> List loaded scripts in library
63 gs <lib_id> <script_url> Get source text of script in library
64 tok <lib_id> <script_url> Get line and token table of script in library
65 epi <none|all|unhandled> Set exception pause info
66 li List ids of all isolates in the VM
67 sci <id> Set current target isolate
68 i <id> Interrupt execution of given isolate id
69 h Print help
70 """);
71 }
72
73 46
74 String formatLocation(Map location) { 47 String formatLocation(Map location) {
75 if (location == null) return ""; 48 if (location == null) return "";
76 var fileName = location["url"].split("/").last; 49 var fileName = location["url"].split("/").last;
77 return "file: $fileName lib: ${location['libraryId']} token: ${location['token Offset']}"; 50 return "file: $fileName lib: ${location['libraryId']} token: ${location['token Offset']}";
78 } 51 }
79 52
80 53
81 void quitShell() {
82 vmSubscription.cancel();
83 vmSock.close();
84 cmdo.done();
85 }
86
87
88 Future sendCmd(Map<String, dynamic> cmd) { 54 Future sendCmd(Map<String, dynamic> cmd) {
89 var completer = new Completer(); 55 var completer = new Completer();
90 int id = cmd["id"]; 56 int id = cmd["id"];
91 outstandingCommands[id] = completer; 57 outstandingCommands[id] = completer;
92 if (verbose) { 58 if (verbose) {
93 print("sending: '${JSON.encode(cmd)}'"); 59 print("sending: '${JSON.encode(cmd)}'");
94 } 60 }
95 vmSock.write(JSON.encode(cmd)); 61 vmSock.write(JSON.encode(cmd));
96 return completer.future; 62 return completer.future;
97 } 63 }
98 64
99 65
100 bool checkCurrentIsolate() { 66 bool checkCurrentIsolate() {
101 if (currentIsolate != null) { 67 if (currentIsolate != null) {
102 return true; 68 return true;
103 } 69 }
104 print("Need valid current isolate"); 70 print("Need valid current isolate");
105 return false; 71 return false;
106 } 72 }
107 73
108 74
109 bool checkPaused() { 75 bool checkPaused() {
110 if (!checkCurrentIsolate()) return false; 76 if (!checkCurrentIsolate()) return false;
111 if (currentIsolate.isPaused) return true; 77 if (currentIsolate.isPaused) return true;
112 print("Current isolate must be paused"); 78 print("Current isolate must be paused");
113 return false; 79 return false;
114 } 80 }
115 81
82 // These settings are allowed in the 'set' and 'show' debugger commands.
83 var validSettings = ['vm', 'vmargs', 'script', 'args'];
84
85 // The current values for all settings.
86 var settings = new Map();
87
88 // Generates a string of 'count' spaces.
89 String _spaces(int count) {
90 return new List.filled(count, ' ').join('');
91 }
92
93 // TODO(turnidge): Move all commands here.
94 List<Command> commandList =
95 [ new HelpCommand(),
96 new QuitCommand(),
97 new RunCommand(),
98 new KillCommand(),
99 new SetCommand(),
100 new ShowCommand() ];
101
102
103 Command matchCommand(String commandName, bool exactMatchWins) {
104 List matches = [];
105 for (var command in commandList) {
106 if (command.name.startsWith(commandName)) {
107 if (exactMatchWins && command.name == commandName) {
108 // Exact match
109 return [command];
110 } else {
111 matches.add(command);
112 }
113 }
114 }
115 return matches;
116 }
117
118 abstract class Command {
119 String get name;
120 Future run(List<String> args);
121 }
122
123 class HelpCommand extends Command {
124 final name = 'help';
125 final helpShort = 'Show a list of debugger commands';
126 final helpLong ="""
127 Show a list of debugger commands or get more information about a
128 particular command.
129
130 Usage:
131 help
132 help <command>
133 """;
134
135 Future run(List<String> args) {
136 if (args.length == 1) {
137 print("Debugger commands:\n");
138 for (var command in commandList) {
139 const tabStop = 12;
140 var spaces = _spaces(max(1, (tabStop - command.name.length)));
141 print(' ${command.name}${spaces}${command.helpShort}');
142 }
143
144 // TODO(turnidge): Convert all commands to use the Command class.
145 print("""
146 bt Show backtrace
147 r Resume execution
148 s Single step
149 so Step over
150 si Step into
151 sbp [<file>] <line> Set breakpoint
152 rbp <id> Remove breakpoint with given id
153 po <id> Print object info for given id
154 eval obj <id> <expr> Evaluate expr on object id
155 eval cls <id> <expr> Evaluate expr on class id
156 eval lib <id> <expr> Evaluate expr in toplevel of library id
157 pl <id> <idx> [<len>] Print list element/slice
158 pc <id> Print class info for given id
159 ll List loaded libraries
160 plib <id> Print library info for given library id
161 slib <id> <true|false> Set library id debuggable
162 pg <id> Print all global variables visible within given library id
163 ls <lib_id> List loaded scripts in library
164 gs <lib_id> <script_url> Get source text of script in library
165 tok <lib_id> <script_url> Get line and token table of script in library
166 epi <none|all|unhandled> Set exception pause info
167 li List ids of all isolates in the VM
168 sci <id> Set current target isolate
169 i <id> Interrupt execution of given isolate id
170 """);
171
172 print("For more information about a particular command, type:\n\n"
173 " help <command>\n");
174
175 print("Commands may be abbreviated: e.g. type 'h' for 'help.\n");
176 } else if (args.length == 2) {
177 var commandName = args[1];
178 var matches = matchCommand(commandName, true);
179 if (matches.length == 0) {
180 print("Command '$commandName' not recognized. "
181 "Try 'help' for a list of commands.");
182 } else if (matches.length == 1) {
183 print(matches[0].helpLong);
184 } else {
185 var matchNames = matches.map((handler) => handler.name);
hausner 2013/12/04 00:26:35 You could just print help for all the commands tha
turnidge 2013/12/04 19:35:21 Nice idea. Done.
186 print("Ambigous command '$commandName' : ${matchNames.toList()}");
187 }
188 } else {
189 print("Command not recognized.");
hausner 2013/12/04 00:26:35 How about printing the usage here?
turnidge 2013/12/04 19:35:21 I have improved this message to this:
190 }
191
192 return new Future.value();
193 }
194 }
195
196
197 class QuitCommand extends Command {
198 final name = 'quit';
199 final helpShort = 'Quit the debugger.';
200 final helpLong ="""
201 Quit the debugger.
202
203 Usage:
204 quit
205 """;
206
207 Future run(List<String> args) {
208 if (args.length > 1) {
209 print("Unexpected arguments to $name command.");
210 return new Future.value();
211 }
212 return debuggerQuit();
213 }
214 }
215
216 class SetCommand extends Command {
217 final name = 'set';
218 final helpShort = 'Change the value of a debugger setting.';
219 final helpLong ="""
220 Change the value of a debugger setting.
221
222 Usage:
223 set <setting> <value>
224
225 Valid settings are:
226 ${validSettings.join('\n ')}.
227
228 See also 'help show'.
229 """;
230
231 Future run(List<String> args) {
232 if (args.length < 3 || !validSettings.contains(args[1])) {
233 print("Undefined $name command. Try 'help $name'.");
234 return new Future.value();
235 }
236 var option = args[1];
237 var value = args.getRange(2, args.length).join(' ');
238 settings[option] = value;
239 return new Future.value();
240 }
241 }
242
243 class ShowCommand extends Command {
244 final name = 'show';
245 final helpShort = 'Show the current value of a debugger setting.';
246 final helpLong ="""
247 Show the current value of a debugger setting.
248
249 Usage:
250 show
251 show <setting>
252
253 If no <setting> is specified, all current settings are shown.
254
255 Valid settings are:
256 ${validSettings.join('\n ')}.
257
258 See also 'help set'.
259 """;
260
261 Future run(List<String> args) {
262 if (args.length == 1) {
263 for (var option in validSettings) {
264 var value = settings[option];
265 print("$option = '$value'");
266 }
267 } else if (args.length == 2 && validSettings.contains(args[1])) {
268 var option = args[1];
269 var value = settings[option];
270 if (value == null) {
271 print('$option has not been set.');
272 } else {
273 print("$option = '$value'");
274 }
275 return new Future.value();
276 } else {
277 print("Undefined $name command. Try 'help $name'.");
278 }
279 return new Future.value();
280 }
281 }
282
283 class RunCommand extends Command {
284 final name = 'run';
285 final helpShort = "Run the currrent script.";
286 final helpLong ="""
287 Runs the current script.
288
289 Usage:
290 run
291 run <args>
292
293 The current script will be run on the current vm. The 'vm' and
294 'vmargs' settings are used to specify the current vm and vm arguments.
295 The 'script' and 'args' settings are used to specify the current
296 script and script arguments.
297
298 For more information on settings type 'help show' or 'help set'.
299
300 If <args> are provided to the run command, it is the same as typing
301 'set args <args>' followed by 'run'.
302 """;
303
304 Future run(List<String> cmdArgs) {
305 if (isRunning) {
306 // TODO(turnidge): Implement modal y/n dialog to stop running script.
307 print("There is already a running dart process. "
308 "Try 'kill'.");
309 return new Future.value();
310 }
311 assert(targetProcess == null);
312 if (settings['script'] == null) {
313 print("There is no script specified. "
314 "Use 'set script' to set the current script.");
315 return new Future.value();
316 }
317 if (cmdArgs.length > 1) {
318 settings['args'] = cmdArgs.getRange(1, cmdArgs.length);
319 }
320
321 // Build the process arguments.
322 var processArgs = ['--debug:$debugPort'];
323 if (verbose) {
324 processArgs.add('--verbose_debug');
325 }
326 if (settings['vmargs'] != null) {
327 processArgs.addAll(settings['vmargs'].split(' '));
328 }
329 processArgs.add(settings['script']);
330 if (settings['args'] != null) {
331 processArgs.addAll(settings['args'].split(' '));
332 }
333 String vm = settings['vm'];
334
335 isRunning = true;
336 cmdo.hide();
337 return Process.start(vm, processArgs).then((Process process) {
338 print("Started process ${process.pid} '$vm ${processArgs.join(' ')}'");
339 targetProcess = process;
340 process.stdin.close();
341
342 // TODO(turnidge): For now we only show full lines of output
343 // from the debugged process. Should show each character.
344 process.stdout
345 .transform(UTF8.decoder)
346 .transform(new LineSplitter())
347 .listen((String line) {
348 cmdo.hide();
349 // TODO(turnidge): Escape output in any way?
350 print(line);
351 cmdo.show();
352 });
353
354 process.stderr
355 .transform(UTF8.decoder)
356 .transform(new LineSplitter())
357 .listen((String line) {
358 cmdo.hide();
359 print(line);
360 cmdo.show();
361 });
362
363 process.exitCode.then((int exitCode) {
364 cmdo.hide();
365 if (suppressNextExitCode) {
366 suppressNextExitCode = false;
367 } else {
368 if (exitCode == 0) {
369 print('Process exited normally.');
370 } else {
371 print('Process exited with code $exitCode.');
372 }
373 }
374 isRunning = false;
375 targetProcess = null;
376 cmdo.show();
377 });
378
379 // Wait for the vm to open the debugging port.
380 return openVmSocket(0);
381 });
382 }
383 }
384
385 class KillCommand extends Command {
386 final name = 'kill';
387 final helpShort = 'Kill the currently executing script.';
388
389 Future run(List<String> cmdArgs) {
390 if (!isRunning) {
391 print('There is no running script.');
392 return new Future.value();
393 }
394 assert(targetProcess != null);
395 bool result = targetProcess.kill();
396 if (result) {
397 print('Process killed.');
398 suppressNextExitCode = true;
399 } else {
400 print('Unable to kill process ${targetProcess.pid}');
401 }
402 return new Future.value();
403 }
404 }
405
116 typedef void HandlerType(Map response); 406 typedef void HandlerType(Map response);
117 407
118 HandlerType showPromptAfter(void handler(Map response)) { 408 HandlerType showPromptAfter(void handler(Map response)) {
119 // Hide the command prompt immediately. 409 // Hide the command prompt immediately.
120 return (response) { 410 return (response) {
121 handler(response); 411 handler(response);
122 cmdo.show(); 412 cmdo.show();
123 }; 413 };
124 } 414 }
125 415
126
127 void processCommand(String cmdLine) { 416 void processCommand(String cmdLine) {
128 417
129 void huh() { 418 void huh() {
130 print("'$cmdLine' not understood, try h for help"); 419 print("'$cmdLine' not understood, try 'help' for help.");
131 } 420 }
132 421
422 cmdo.hide();
133 seqNum++; 423 seqNum++;
134 cmdLine = cmdLine.trim(); 424 cmdLine = cmdLine.trim();
135 var args = cmdLine.split(' '); 425 var args = cmdLine.split(' ');
136 if (args.length == 0) { 426 if (args.length == 0) {
137 return; 427 return;
138 } 428 }
139 var command = args[0]; 429 var command = args[0];
430
140 var resume_commands = 431 var resume_commands =
141 { 'r':'resume', 's':'stepOver', 'si':'stepInto', 'so':'stepOut'}; 432 { 'r':'resume', 's':'stepOver', 'si':'stepInto', 'so':'stepOut'};
142 if (resume_commands[command] != null) { 433 if (resume_commands[command] != null) {
143 if (!checkPaused()) return; 434 if (!checkPaused()) return;
144 var cmd = { "id": seqNum, 435 var cmd = { "id": seqNum,
145 "command": resume_commands[command], 436 "command": resume_commands[command],
146 "params": { "isolateId" : currentIsolate.id } }; 437 "params": { "isolateId" : currentIsolate.id } };
147 cmdo.hide();
148 sendCmd(cmd).then(showPromptAfter(handleResumedResponse)); 438 sendCmd(cmd).then(showPromptAfter(handleResumedResponse));
149 } else if (command == "bt") { 439 } else if (command == "bt") {
150 var cmd = { "id": seqNum, 440 var cmd = { "id": seqNum,
151 "command": "getStackTrace", 441 "command": "getStackTrace",
152 "params": { "isolateId" : currentIsolate.id } }; 442 "params": { "isolateId" : currentIsolate.id } };
153 cmdo.hide();
154 sendCmd(cmd).then(showPromptAfter(handleStackTraceResponse)); 443 sendCmd(cmd).then(showPromptAfter(handleStackTraceResponse));
155 } else if (command == "ll") { 444 } else if (command == "ll") {
156 var cmd = { "id": seqNum, 445 var cmd = { "id": seqNum,
157 "command": "getLibraries", 446 "command": "getLibraries",
158 "params": { "isolateId" : currentIsolate.id } }; 447 "params": { "isolateId" : currentIsolate.id } };
159 cmdo.hide();
160 sendCmd(cmd).then(showPromptAfter(handleGetLibraryResponse)); 448 sendCmd(cmd).then(showPromptAfter(handleGetLibraryResponse));
161 } else if (command == "sbp" && args.length >= 2) { 449 } else if (command == "sbp" && args.length >= 2) {
162 var url, line; 450 var url, line;
163 if (args.length == 2 && currentIsolate.pausedLocation != null) { 451 if (args.length == 2 && currentIsolate.pausedLocation != null) {
164 url = currentIsolate.pausedLocation["url"]; 452 url = currentIsolate.pausedLocation["url"];
165 assert(url != null); 453 assert(url != null);
166 line = int.parse(args[1]); 454 line = int.parse(args[1]);
167 } else { 455 } else {
168 url = args[1]; 456 url = args[1];
169 line = int.parse(args[2]); 457 line = int.parse(args[2]);
170 } 458 }
171 var cmd = { "id": seqNum, 459 var cmd = { "id": seqNum,
172 "command": "setBreakpoint", 460 "command": "setBreakpoint",
173 "params": { "isolateId" : currentIsolate.id, 461 "params": { "isolateId" : currentIsolate.id,
174 "url": url, 462 "url": url,
175 "line": line }}; 463 "line": line }};
176 cmdo.hide();
177 sendCmd(cmd).then(showPromptAfter(handleSetBpResponse)); 464 sendCmd(cmd).then(showPromptAfter(handleSetBpResponse));
178 } else if (command == "rbp" && args.length == 2) { 465 } else if (command == "rbp" && args.length == 2) {
179 var cmd = { "id": seqNum, 466 var cmd = { "id": seqNum,
180 "command": "removeBreakpoint", 467 "command": "removeBreakpoint",
181 "params": { "isolateId" : currentIsolate.id, 468 "params": { "isolateId" : currentIsolate.id,
182 "breakpointId": int.parse(args[1]) } }; 469 "breakpointId": int.parse(args[1]) } };
183 cmdo.hide();
184 sendCmd(cmd).then(showPromptAfter(handleGenericResponse)); 470 sendCmd(cmd).then(showPromptAfter(handleGenericResponse));
185 } else if (command == "ls" && args.length == 2) { 471 } else if (command == "ls" && args.length == 2) {
186 var cmd = { "id": seqNum, 472 var cmd = { "id": seqNum,
187 "command": "getScriptURLs", 473 "command": "getScriptURLs",
188 "params": { "isolateId" : currentIsolate.id, 474 "params": { "isolateId" : currentIsolate.id,
189 "libraryId": int.parse(args[1]) } }; 475 "libraryId": int.parse(args[1]) } };
190 cmdo.hide();
191 sendCmd(cmd).then(showPromptAfter(handleGetScriptsResponse)); 476 sendCmd(cmd).then(showPromptAfter(handleGetScriptsResponse));
192 } else if (command == "eval" && args.length > 3) { 477 } else if (command == "eval" && args.length > 3) {
193 var expr = args.getRange(3, args.length).join(" "); 478 var expr = args.getRange(3, args.length).join(" ");
194 var target = args[1]; 479 var target = args[1];
195 if (target == "obj") { 480 if (target == "obj") {
196 target = "objectId"; 481 target = "objectId";
197 } else if (target == "cls") { 482 } else if (target == "cls") {
198 target = "classId"; 483 target = "classId";
199 } else if (target == "lib") { 484 } else if (target == "lib") {
200 target = "libraryId"; 485 target = "libraryId";
201 } else { 486 } else {
202 huh(); 487 huh();
203 return; 488 return;
204 } 489 }
205 var cmd = { "id": seqNum, 490 var cmd = { "id": seqNum,
206 "command": "evaluateExpr", 491 "command": "evaluateExpr",
207 "params": { "isolateId": currentIsolate.id, 492 "params": { "isolateId": currentIsolate.id,
208 target: int.parse(args[2]), 493 target: int.parse(args[2]),
209 "expression": expr } }; 494 "expression": expr } };
210 cmdo.hide();
211 sendCmd(cmd).then(showPromptAfter(handleEvalResponse)); 495 sendCmd(cmd).then(showPromptAfter(handleEvalResponse));
212 } else if (command == "po" && args.length == 2) { 496 } else if (command == "po" && args.length == 2) {
213 var cmd = { "id": seqNum, 497 var cmd = { "id": seqNum,
214 "command": "getObjectProperties", 498 "command": "getObjectProperties",
215 "params": { "isolateId" : currentIsolate.id, 499 "params": { "isolateId" : currentIsolate.id,
216 "objectId": int.parse(args[1]) } }; 500 "objectId": int.parse(args[1]) } };
217 cmdo.hide();
218 sendCmd(cmd).then(showPromptAfter(handleGetObjPropsResponse)); 501 sendCmd(cmd).then(showPromptAfter(handleGetObjPropsResponse));
219 } else if (command == "pl" && args.length >= 3) { 502 } else if (command == "pl" && args.length >= 3) {
220 var cmd; 503 var cmd;
221 if (args.length == 3) { 504 if (args.length == 3) {
222 cmd = { "id": seqNum, 505 cmd = { "id": seqNum,
223 "command": "getListElements", 506 "command": "getListElements",
224 "params": { "isolateId" : currentIsolate.id, 507 "params": { "isolateId" : currentIsolate.id,
225 "objectId": int.parse(args[1]), 508 "objectId": int.parse(args[1]),
226 "index": int.parse(args[2]) } }; 509 "index": int.parse(args[2]) } };
227 } else { 510 } else {
228 cmd = { "id": seqNum, 511 cmd = { "id": seqNum,
229 "command": "getListElements", 512 "command": "getListElements",
230 "params": { "isolateId" : currentIsolate.id, 513 "params": { "isolateId" : currentIsolate.id,
231 "objectId": int.parse(args[1]), 514 "objectId": int.parse(args[1]),
232 "index": int.parse(args[2]), 515 "index": int.parse(args[2]),
233 "length": int.parse(args[3]) } }; 516 "length": int.parse(args[3]) } };
234 } 517 }
235 cmdo.hide();
236 sendCmd(cmd).then(showPromptAfter(handleGetListResponse)); 518 sendCmd(cmd).then(showPromptAfter(handleGetListResponse));
237 } else if (command == "pc" && args.length == 2) { 519 } else if (command == "pc" && args.length == 2) {
238 var cmd = { "id": seqNum, 520 var cmd = { "id": seqNum,
239 "command": "getClassProperties", 521 "command": "getClassProperties",
240 "params": { "isolateId" : currentIsolate.id, 522 "params": { "isolateId" : currentIsolate.id,
241 "classId": int.parse(args[1]) } }; 523 "classId": int.parse(args[1]) } };
242 cmdo.hide();
243 sendCmd(cmd).then(showPromptAfter(handleGetClassPropsResponse)); 524 sendCmd(cmd).then(showPromptAfter(handleGetClassPropsResponse));
244 } else if (command == "plib" && args.length == 2) { 525 } else if (command == "plib" && args.length == 2) {
245 var cmd = { "id": seqNum, 526 var cmd = { "id": seqNum,
246 "command": "getLibraryProperties", 527 "command": "getLibraryProperties",
247 "params": {"isolateId" : currentIsolate.id, 528 "params": {"isolateId" : currentIsolate.id,
248 "libraryId": int.parse(args[1]) } }; 529 "libraryId": int.parse(args[1]) } };
249 cmdo.hide();
250 sendCmd(cmd).then(showPromptAfter(handleGetLibraryPropsResponse)); 530 sendCmd(cmd).then(showPromptAfter(handleGetLibraryPropsResponse));
251 } else if (command == "slib" && args.length == 3) { 531 } else if (command == "slib" && args.length == 3) {
252 var cmd = { "id": seqNum, 532 var cmd = { "id": seqNum,
253 "command": "setLibraryProperties", 533 "command": "setLibraryProperties",
254 "params": {"isolateId" : currentIsolate.id, 534 "params": {"isolateId" : currentIsolate.id,
255 "libraryId": int.parse(args[1]), 535 "libraryId": int.parse(args[1]),
256 "debuggingEnabled": args[2] } }; 536 "debuggingEnabled": args[2] } };
257 cmdo.hide();
258 sendCmd(cmd).then(showPromptAfter(handleSetLibraryPropsResponse)); 537 sendCmd(cmd).then(showPromptAfter(handleSetLibraryPropsResponse));
259 } else if (command == "pg" && args.length == 2) { 538 } else if (command == "pg" && args.length == 2) {
260 var cmd = { "id": seqNum, 539 var cmd = { "id": seqNum,
261 "command": "getGlobalVariables", 540 "command": "getGlobalVariables",
262 "params": { "isolateId" : currentIsolate.id, 541 "params": { "isolateId" : currentIsolate.id,
263 "libraryId": int.parse(args[1]) } }; 542 "libraryId": int.parse(args[1]) } };
264 cmdo.hide();
265 sendCmd(cmd).then(showPromptAfter(handleGetGlobalVarsResponse)); 543 sendCmd(cmd).then(showPromptAfter(handleGetGlobalVarsResponse));
266 } else if (command == "gs" && args.length == 3) { 544 } else if (command == "gs" && args.length == 3) {
267 var cmd = { "id": seqNum, 545 var cmd = { "id": seqNum,
268 "command": "getScriptSource", 546 "command": "getScriptSource",
269 "params": { "isolateId" : currentIsolate.id, 547 "params": { "isolateId" : currentIsolate.id,
270 "libraryId": int.parse(args[1]), 548 "libraryId": int.parse(args[1]),
271 "url": args[2] } }; 549 "url": args[2] } };
272 cmdo.hide();
273 sendCmd(cmd).then(showPromptAfter(handleGetSourceResponse)); 550 sendCmd(cmd).then(showPromptAfter(handleGetSourceResponse));
274 } else if (command == "tok" && args.length == 3) { 551 } else if (command == "tok" && args.length == 3) {
275 var cmd = { "id": seqNum, 552 var cmd = { "id": seqNum,
276 "command": "getLineNumberTable", 553 "command": "getLineNumberTable",
277 "params": { "isolateId" : currentIsolate.id, 554 "params": { "isolateId" : currentIsolate.id,
278 "libraryId": int.parse(args[1]), 555 "libraryId": int.parse(args[1]),
279 "url": args[2] } }; 556 "url": args[2] } };
280 cmdo.hide();
281 sendCmd(cmd).then(showPromptAfter(handleGetLineTableResponse)); 557 sendCmd(cmd).then(showPromptAfter(handleGetLineTableResponse));
282 } else if (command == "epi" && args.length == 2) { 558 } else if (command == "epi" && args.length == 2) {
283 var cmd = { "id": seqNum, 559 var cmd = { "id": seqNum,
284 "command": "setPauseOnException", 560 "command": "setPauseOnException",
285 "params": { "isolateId" : currentIsolate.id, 561 "params": { "isolateId" : currentIsolate.id,
286 "exceptions": args[1] } }; 562 "exceptions": args[1] } };
287 cmdo.hide();
288 sendCmd(cmd).then(showPromptAfter(handleGenericResponse)); 563 sendCmd(cmd).then(showPromptAfter(handleGenericResponse));
289 } else if (command == "li") { 564 } else if (command == "li") {
290 var cmd = { "id": seqNum, "command": "getIsolateIds" }; 565 var cmd = { "id": seqNum, "command": "getIsolateIds" };
291 cmdo.hide();
292 sendCmd(cmd).then(showPromptAfter(handleGetIsolatesResponse)); 566 sendCmd(cmd).then(showPromptAfter(handleGetIsolatesResponse));
293 } else if (command == "sci" && args.length == 2) { 567 } else if (command == "sci" && args.length == 2) {
294 var id = int.parse(args[1]); 568 var id = int.parse(args[1]);
295 if (targetIsolates[id] != null) { 569 if (targetIsolates[id] != null) {
296 currentIsolate = targetIsolates[id]; 570 currentIsolate = targetIsolates[id];
297 print("Setting current target isolate to $id"); 571 print("Setting current target isolate to $id");
298 } else { 572 } else {
299 print("$id is not a valid isolate id"); 573 print("$id is not a valid isolate id");
300 } 574 }
575 cmdo.show();
301 } else if (command == "i" && args.length == 2) { 576 } else if (command == "i" && args.length == 2) {
302 var cmd = { "id": seqNum, 577 var cmd = { "id": seqNum,
303 "command": "interrupt", 578 "command": "interrupt",
304 "params": { "isolateId": int.parse(args[1]) } }; 579 "params": { "isolateId": int.parse(args[1]) } };
305 cmdo.hide();
306 sendCmd(cmd).then(showPromptAfter(handleGenericResponse)); 580 sendCmd(cmd).then(showPromptAfter(handleGenericResponse));
307 } else if (command == "q") { 581 } else if (command.length == 0) {
308 quitShell(); 582 huh();
309 } else if (command == "h") { 583 cmdo.show();
310 printHelp();
311 } else { 584 } else {
312 huh(); 585 // TODO(turnidge): Migrate all commands into this .
586 var matches = matchCommand(command, true);
587 if (matches.length == 0) {
588 huh();
589 cmdo.show();
590 } else if (matches.length == 1) {
591 matches[0].run(args).then((_) {
592 cmdo.show();
593 });
594 } else {
595 var matchNames = matches.map((handler) => handler.name);
596 print("Ambigous command '$command' : ${matchNames.toList()}");
597 cmdo.show();
598 }
313 } 599 }
314 } 600 }
315 601
316 602
603 void processError(error, trace) {
604 cmdo.hide();
605 print("\nInternal error:\n$error\n$trace");
606 cmdo.show();
607 }
608
609
610 void processDone() {
611 debuggerQuit();
612 }
613
614
317 String remoteObject(value) { 615 String remoteObject(value) {
318 var kind = value["kind"]; 616 var kind = value["kind"];
319 var text = value["text"]; 617 var text = value["text"];
320 var id = value["objectId"]; 618 var id = value["objectId"];
321 if (kind == "string") { 619 if (kind == "string") {
322 return "(string, id $id) '$text'"; 620 return "(string, id $id) '$text'";
323 } else if (kind == "list") { 621 } else if (kind == "list") {
324 var len = value["length"]; 622 var len = value["length"];
325 return "(list, id $id, len $len) $text"; 623 return "(list, id $id, len $len) $text";
326 } else if (kind == "object") { 624 } else if (kind == "object") {
(...skipping 430 matching lines...) Expand 10 before | Expand all | Expand 10 after
757 List<String> debuggerCommandCompleter(List<String> commandParts) { 1055 List<String> debuggerCommandCompleter(List<String> commandParts) {
758 List<String> completions = new List<String>(); 1056 List<String> completions = new List<String>();
759 1057
760 // TODO(turnidge): Have a global command table and use it to for 1058 // TODO(turnidge): Have a global command table and use it to for
761 // help messages, command completion, and command dispatching. For now 1059 // help messages, command completion, and command dispatching. For now
762 // we hardcode the list here. 1060 // we hardcode the list here.
763 // 1061 //
764 // TODO(turnidge): Implement completion for arguments as well. 1062 // TODO(turnidge): Implement completion for arguments as well.
765 List<String> allCommands = ['q', 'bt', 'r', 's', 'so', 'si', 'sbp', 'rbp', 1063 List<String> allCommands = ['q', 'bt', 'r', 's', 'so', 'si', 'sbp', 'rbp',
766 'po', 'eval', 'pl', 'pc', 'll', 'plib', 'slib', 1064 'po', 'eval', 'pl', 'pc', 'll', 'plib', 'slib',
767 'pg', 'ls', 'gs', 'tok', 'epi', 'li', 'i', 'h']; 1065 'pg', 'ls', 'gs', 'tok', 'epi', 'li', 'i',
1066 'help'];
hausner 2013/12/04 00:26:35 Don't you need to add the new set and show command
turnidge 2013/12/04 19:35:21 allCommands will be shrinking over time. The seco
768 1067
769 // Completion of first word in the command. 1068 // Completion of first word in the command.
770 if (commandParts.length == 1) { 1069 if (commandParts.length == 1) {
771 String prefix = commandParts.last; 1070 String prefix = commandParts.last;
772 for (String command in allCommands) { 1071 for (var command in allCommands) {
773 if (command.startsWith(prefix)) { 1072 if (command.startsWith(prefix)) {
774 completions.add(command); 1073 completions.add(command);
775 } 1074 }
776 } 1075 }
1076 for (var command in commandList) {
1077 if (command.name.startsWith(prefix)) {
1078 completions.add(command.name);
1079 }
1080 }
777 } 1081 }
778 1082
779 return completions; 1083 return completions;
780 } 1084 }
781 1085
782 void debuggerMain() { 1086 Future closeCommando() {
783 outstandingCommands = new Map<int, Completer>(); 1087 var subscription = cmdSubscription;
784 Socket.connect("127.0.0.1", 5858).then((s) { 1088 cmdSubscription = null;
785 vmSock = s; 1089 cmdo = null;
786 vmSock.setOption(SocketOption.TCP_NODELAY, true); 1090
787 var stringStream = vmSock.transform(UTF8.decoder); 1091 var future = subscription.cancel();
788 vmSubscription = stringStream.listen( 1092 if (future != null) {
789 (String data) { 1093 return future;
790 processVmData(data); 1094 } else {
791 }, 1095 return new Future.value();
792 onDone: () { 1096 }
793 print("VM debugger connection closed"); 1097 }
794 quitShell(); 1098
795 }, 1099
796 onError: (err) { 1100 Future openVmSocket(int attempt) {
797 print("Error in debug connection: $err"); 1101 return Socket.connect("127.0.0.1", debugPort).then((s) {
798 // TODO(floitsch): do we want to print the stack trace? 1102 vmSock = s;
799 quitShell(); 1103 vmSock.setOption(SocketOption.TCP_NODELAY, true);
1104 var stringStream = vmSock.transform(UTF8.decoder);
1105 outstandingCommands = new Map<int, Completer>();
1106 vmSubscription = stringStream.listen(
1107 (String data) {
1108 processVmData(data);
1109 },
1110 onDone: () {
1111 cmdo.hide();
1112 if (verbose) {
1113 print("VM debugger connection closed");
1114 }
1115 closeVmSocket().then((_) {
1116 cmdo.show();
1117 });
1118 },
1119 onError: (err) {
1120 cmdo.hide();
1121 // TODO(floitsch): do we want to print the stack trace?
1122 print("Error in debug connection: $err");
1123 closeVmSocket().then((_) {
1124 cmdo.show();
1125 });
1126 });
1127 },
1128 onError: (e) {
hausner 2013/12/04 00:26:35 This is where Dart code becomes pretty much unread
turnidge 2013/12/04 19:35:21 Yes, hard to read here for sure. The onError is a
1129 // We were unable to connect to the debugger's port.
1130 var delay;
1131 if (attempt < 10) {
1132 delay = new Duration(milliseconds:10);
1133 } else if (attempt < 20) {
1134 delay = new Duration(seconds:1);
1135 } else {
1136 // Too many retries. Give up.
1137 print('Timed out waiting for debugger to start.\nError: $e');
1138 return closeVmSocket();
1139 }
1140
1141 // Wait and retry.
1142 return new Future.delayed(delay, () {
1143 int tmp = attempt + 1;
1144 openVmSocket(tmp);
800 }); 1145 });
801 cmdo = new Commando(stdin, stdout, processCommand, 1146 });
802 completer : debuggerCommandCompleter); 1147 }
803 }); 1148
1149 Future closeVmSocket() {
1150 if (vmSubscription == null) {
1151 // Already closed, nothing to do.
1152 assert(vmSock == null);
1153 return new Future.value();
1154 }
1155
1156 var subscription = vmSubscription;
1157 var sock = vmSock;
1158
1159 // Wait for the socket to close and the subscription to be
1160 // cancelled. Perhaps overkill, but it means we know these will be
1161 // done.
1162 //
1163 // This is uglier than it needs to be since cancel can return null.
1164 var cleanupFutures = [sock.close()];
1165 var future = subscription.cancel();
1166 if (future != null) {
1167 cleanupFutures.add(future);
1168 }
1169
1170 vmSubscription = null;
1171 vmSock = null;
1172 outstandingCommands = null;
1173
1174 return Future.wait(cleanupFutures);
1175 }
1176
1177 Future debuggerQuit() {
1178 // Kill target process, if any.
1179 if (targetProcess != null) {
1180 if (!targetProcess.kill()) {
1181 print('Unable to kill process ${targetProcess.pid}');
1182 }
1183 }
1184
1185 // Restore terminal settings, close connections.
1186 return Future.wait([closeCommando(), closeVmSocket()]).then((_) {
1187 exit(0);
1188
1189 // Unreachable.
1190 return new Future.value();
1191 });
1192 }
1193
1194
1195 void parseArgs(List<String> args) {
1196 int pos = 0;
1197 settings['vm'] = Platform.executable;
1198 while (pos < args.length && args[pos].startsWith('-')) {
1199 pos++;
1200 }
1201 if (pos < args.length) {
1202 settings['vmargs'] = args.getRange(0, pos).join(' ');
1203 settings['script'] = args[pos];
1204 settings['args'] = args.getRange(pos + 1, args.length).join(' ');
1205 }
804 } 1206 }
805 1207
806 void main(List<String> args) { 1208 void main(List<String> args) {
hausner 2013/12/04 00:26:35 Is it still possible to invoke ddbg without a targ
turnidge 2013/12/04 19:35:21 No. I missed that. I have now added two new comm
807 if (args.length > 0) { 1209 parseArgs(args);
808 if (verbose) {
809 args = <String>['--debug', '--verbose_debug']..addAll(args);
810 } else {
811 args = <String>['--debug']..addAll(args);
812 }
813 Process.start(Platform.executable, args).then((Process process) {
814 targetProcess = process;
815 process.stdin.close();
816 1210
817 // TODO(turnidge): For now we only show full lines of output 1211 cmdo = new Commando(completer: debuggerCommandCompleter);
818 // from the debugged process. Should show each character. 1212 cmdSubscription = cmdo.commands.listen(processCommand,
819 process.stdout 1213 onError: processError,
820 .transform(UTF8.decoder) 1214 onDone: processDone);
821 .transform(new LineSplitter())
822 .listen((String line) {
823 // Hide/show command prompt across asynchronous output.
824 if (cmdo != null) {
825 cmdo.hide();
826 }
827 print("$line");
828 if (cmdo != null) {
829 cmdo.show();
830 }
831 });
832
833 process.exitCode.then((int exitCode) {
834 if (exitCode == 0) {
835 print('Program exited normally.');
836 } else {
837 print('Program exited with code $exitCode.');
838 }
839 });
840
841 debuggerMain();
842 });
843 } else {
844 debuggerMain();
845 }
846 } 1215 }
OLDNEW
« no previous file with comments | « no previous file | tools/ddbg/lib/commando.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698