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

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
33 Process targetProcess; 35 bool isDebugging = false;
36 Process targetProcess = null;
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 ConnectCommand(),
100 new DisconnectCommand(),
101 new SetCommand(),
102 new ShowCommand() ];
103
104
105 Command matchCommand(String commandName, bool exactMatchWins) {
106 List matches = [];
107 for (var command in commandList) {
108 if (command.name.startsWith(commandName)) {
109 if (exactMatchWins && command.name == commandName) {
110 // Exact match
111 return [command];
112 } else {
113 matches.add(command);
114 }
115 }
116 }
117 return matches;
118 }
119
120 abstract class Command {
121 String get name;
122 Future run(List<String> args);
123 }
124
125 class HelpCommand extends Command {
126 final name = 'help';
127 final helpShort = 'Show a list of debugger commands';
128 final helpLong ="""
129 Show a list of debugger commands or get more information about a
130 particular command.
131
132 Usage:
133 help
134 help <command>
135 """;
136
137 Future run(List<String> args) {
138 if (args.length == 1) {
139 print("Debugger commands:\n");
140 for (var command in commandList) {
141 const tabStop = 12;
142 var spaces = _spaces(max(1, (tabStop - command.name.length)));
143 print(' ${command.name}${spaces}${command.helpShort}');
144 }
145
146 // TODO(turnidge): Convert all commands to use the Command class.
147 print("""
148 bt Show backtrace
149 r Resume execution
150 s Single step
151 so Step over
152 si Step into
153 sbp [<file>] <line> Set breakpoint
154 rbp <id> Remove breakpoint with given id
155 po <id> Print object info for given id
156 eval obj <id> <expr> Evaluate expr on object id
157 eval cls <id> <expr> Evaluate expr on class id
158 eval lib <id> <expr> Evaluate expr in toplevel of library id
159 pl <id> <idx> [<len>] Print list element/slice
160 pc <id> Print class info for given id
161 ll List loaded libraries
162 plib <id> Print library info for given library id
163 slib <id> <true|false> Set library id debuggable
164 pg <id> Print all global variables visible within given library id
165 ls <lib_id> List loaded scripts in library
166 gs <lib_id> <script_url> Get source text of script in library
167 tok <lib_id> <script_url> Get line and token table of script in library
168 epi <none|all|unhandled> Set exception pause info
169 li List ids of all isolates in the VM
170 sci <id> Set current target isolate
171 i <id> Interrupt execution of given isolate id
172 """);
173
174 print("For more information about a particular command, type:\n\n"
175 " help <command>\n");
176
177 print("Commands may be abbreviated: e.g. type 'h' for 'help.\n");
178 } else if (args.length == 2) {
179 var commandName = args[1];
180 var matches = matchCommand(commandName, true);
181 if (matches.length == 0) {
182 print("Command '$commandName' not recognized. "
183 "Try 'help' for a list of commands.");
184 } else {
185 for (var command in matches) {
186 print("---- ${command.name} ----\n${command.helpLong}");
187 }
188 }
189 } else {
190 print("Command '$command' not recognized. "
191 "Try 'help' for a list of commands.");
192 }
193
194 return new Future.value();
195 }
196 }
197
198
199 class QuitCommand extends Command {
200 final name = 'quit';
201 final helpShort = 'Quit the debugger.';
202 final helpLong ="""
203 Quit the debugger.
204
205 Usage:
206 quit
207 """;
208
209 Future run(List<String> args) {
210 if (args.length > 1) {
211 print("Unexpected arguments to $name command.");
212 return new Future.value();
213 }
214 return debuggerQuit();
215 }
216 }
217
218 class SetCommand extends Command {
219 final name = 'set';
220 final helpShort = 'Change the value of a debugger setting.';
221 final helpLong ="""
222 Change the value of a debugger setting.
223
224 Usage:
225 set <setting> <value>
226
227 Valid settings are:
228 ${validSettings.join('\n ')}.
229
230 See also 'help show'.
231 """;
232
233 Future run(List<String> args) {
234 if (args.length < 3 || !validSettings.contains(args[1])) {
235 print("Undefined $name command. Try 'help $name'.");
236 return new Future.value();
237 }
238 var option = args[1];
239 var value = args.getRange(2, args.length).join(' ');
240 settings[option] = value;
241 return new Future.value();
242 }
243 }
244
245 class ShowCommand extends Command {
246 final name = 'show';
247 final helpShort = 'Show the current value of a debugger setting.';
248 final helpLong ="""
249 Show the current value of a debugger setting.
250
251 Usage:
252 show
253 show <setting>
254
255 If no <setting> is specified, all current settings are shown.
256
257 Valid settings are:
258 ${validSettings.join('\n ')}.
259
260 See also 'help set'.
261 """;
262
263 Future run(List<String> args) {
264 if (args.length == 1) {
265 for (var option in validSettings) {
266 var value = settings[option];
267 print("$option = '$value'");
268 }
269 } else if (args.length == 2 && validSettings.contains(args[1])) {
270 var option = args[1];
271 var value = settings[option];
272 if (value == null) {
273 print('$option has not been set.');
274 } else {
275 print("$option = '$value'");
276 }
277 return new Future.value();
278 } else {
279 print("Undefined $name command. Try 'help $name'.");
280 }
281 return new Future.value();
282 }
283 }
284
285 class RunCommand extends Command {
286 final name = 'run';
287 final helpShort = "Run the currrent script.";
288 final helpLong ="""
289 Runs the current script.
290
291 Usage:
292 run
293 run <args>
294
295 The current script will be run on the current vm. The 'vm' and
296 'vmargs' settings are used to specify the current vm and vm arguments.
297 The 'script' and 'args' settings are used to specify the current
298 script and script arguments.
299
300 For more information on settings type 'help show' or 'help set'.
301
302 If <args> are provided to the run command, it is the same as typing
303 'set args <args>' followed by 'run'.
304 """;
305
306 Future run(List<String> cmdArgs) {
307 if (isDebugging) {
308 // TODO(turnidge): Implement modal y/n dialog to stop running script.
309 print("There is already a running dart process. "
310 "Try 'kill'.");
311 return new Future.value();
312 }
313 assert(targetProcess == null);
314 if (settings['script'] == null) {
315 print("There is no script specified. "
316 "Use 'set script' to set the current script.");
317 return new Future.value();
318 }
319 if (cmdArgs.length > 1) {
320 settings['args'] = cmdArgs.getRange(1, cmdArgs.length);
321 }
322
323 // Build the process arguments.
324 var processArgs = ['--debug:$debugPort'];
325 if (verbose) {
326 processArgs.add('--verbose_debug');
327 }
328 if (settings['vmargs'] != null) {
329 processArgs.addAll(settings['vmargs'].split(' '));
330 }
331 processArgs.add(settings['script']);
332 if (settings['args'] != null) {
333 processArgs.addAll(settings['args'].split(' '));
334 }
335 String vm = settings['vm'];
336
337 isDebugging = true;
338 cmdo.hide();
339 return Process.start(vm, processArgs).then((Process process) {
340 print("Started process ${process.pid} '$vm ${processArgs.join(' ')}'");
341 targetProcess = process;
342 process.stdin.close();
343
344 // TODO(turnidge): For now we only show full lines of output
345 // from the debugged process. Should show each character.
346 process.stdout
347 .transform(UTF8.decoder)
348 .transform(new LineSplitter())
349 .listen((String line) {
350 cmdo.hide();
351 // TODO(turnidge): Escape output in any way?
352 print(line);
353 cmdo.show();
354 });
355
356 process.stderr
357 .transform(UTF8.decoder)
358 .transform(new LineSplitter())
359 .listen((String line) {
360 cmdo.hide();
361 print(line);
362 cmdo.show();
363 });
364
365 process.exitCode.then((int exitCode) {
366 cmdo.hide();
367 if (suppressNextExitCode) {
368 suppressNextExitCode = false;
369 } else {
370 if (exitCode == 0) {
371 print('Process exited normally.');
372 } else {
373 print('Process exited with code $exitCode.');
374 }
375 }
376 targetProcess = null;
377 cmdo.show();
378 });
379
380 // Wait for the vm to open the debugging port.
381 return openVmSocket(0);
382 });
383 }
384 }
385
386 class KillCommand extends Command {
387 final name = 'kill';
388 final helpShort = 'Kill the currently executing script.';
389 final helpLong ="""
390 Kill the currently executing script.
391
392 Usage:
393 kill
394 """;
395
396 Future run(List<String> cmdArgs) {
397 if (!isDebugging) {
398 print('There is no running script.');
399 return new Future.value();
400 }
401 if (targetProcess == null) {
402 print("The active dart process was not started with 'run'. "
403 "Try 'disconnect' instead.");
404 return new Future.value();
405 }
406 assert(targetProcess != null);
407 bool result = targetProcess.kill();
408 if (result) {
409 print('Process killed.');
410 suppressNextExitCode = true;
411 } else {
412 print('Unable to kill process ${targetProcess.pid}');
413 }
414 return new Future.value();
415 }
416 }
417
418 class ConnectCommand extends Command {
419 final name = 'connect';
420 final helpShort = "Connect to a running dart script.";
421 final helpLong ="""
422 Connect to a running dart script.
423
424 Usage:
425 connect
426 connect <port>
427
428 The debugger will connect to a dart script which has already been
429 started with the --debug option. If no port is provided, the debugger
430 will attempt to connect on the default debugger port.
431 """;
432
433 Future run(List<String> cmdArgs) {
434 if (cmdArgs.length > 2) {
435 print("Too many arguments to 'connect'.");
436 }
437 if (isDebugging) {
438 // TODO(turnidge): Implement modal y/n dialog to stop running script.
439 print("There is already a running dart process. "
440 "Try 'kill'.");
441 return new Future.value();
442 }
443 assert(targetProcess == null);
444 if (cmdArgs.length == 2) {
445 debugPort = int.parse(cmdArgs[1]);
446 }
447
448 isDebugging = true;
449 cmdo.hide();
450 return openVmSocket(0);
451 }
452 }
453
454 class DisconnectCommand extends Command {
455 final name = 'disconnect';
456 final helpShort = "Disconnect from a running dart script.";
457 final helpLong ="""
458 Disconnect from a running dart script.
459
460 Usage:
461 disconnect
462
463 The debugger will disconnect from a dart script's debugging port. The
464 script must have been connected to earlier with the 'connect' command.
465 """;
466
467 Future run(List<String> cmdArgs) {
468 if (cmdArgs.length > 1) {
469 print("Too many arguments to 'disconnect'.");
470 }
471 if (!isDebugging) {
472 // TODO(turnidge): Implement modal y/n dialog to stop running script.
473 print("There is no active dart process. "
474 "Try 'connect'.");
475 return new Future.value();
476 }
477 if (targetProcess != null) {
478 print("The active dart process was started with 'run'. "
479 "Try 'kill'.");
480 }
481
482 cmdo.hide();
483 return closeVmSocket();
484 }
485 }
486
116 typedef void HandlerType(Map response); 487 typedef void HandlerType(Map response);
117 488
118 HandlerType showPromptAfter(void handler(Map response)) { 489 HandlerType showPromptAfter(void handler(Map response)) {
119 // Hide the command prompt immediately. 490 // Hide the command prompt immediately.
120 return (response) { 491 return (response) {
121 handler(response); 492 handler(response);
122 cmdo.show(); 493 cmdo.show();
123 }; 494 };
124 } 495 }
125 496
126
127 void processCommand(String cmdLine) { 497 void processCommand(String cmdLine) {
128 498
129 void huh() { 499 void huh() {
130 print("'$cmdLine' not understood, try h for help"); 500 print("'$cmdLine' not understood, try 'help' for help.");
131 } 501 }
132 502
503 cmdo.hide();
133 seqNum++; 504 seqNum++;
134 cmdLine = cmdLine.trim(); 505 cmdLine = cmdLine.trim();
135 var args = cmdLine.split(' '); 506 var args = cmdLine.split(' ');
136 if (args.length == 0) { 507 if (args.length == 0) {
137 return; 508 return;
138 } 509 }
139 var command = args[0]; 510 var command = args[0];
511
140 var resume_commands = 512 var resume_commands =
141 { 'r':'resume', 's':'stepOver', 'si':'stepInto', 'so':'stepOut'}; 513 { 'r':'resume', 's':'stepOver', 'si':'stepInto', 'so':'stepOut'};
142 if (resume_commands[command] != null) { 514 if (resume_commands[command] != null) {
143 if (!checkPaused()) return; 515 if (!checkPaused()) return;
144 var cmd = { "id": seqNum, 516 var cmd = { "id": seqNum,
145 "command": resume_commands[command], 517 "command": resume_commands[command],
146 "params": { "isolateId" : currentIsolate.id } }; 518 "params": { "isolateId" : currentIsolate.id } };
147 cmdo.hide();
148 sendCmd(cmd).then(showPromptAfter(handleResumedResponse)); 519 sendCmd(cmd).then(showPromptAfter(handleResumedResponse));
149 } else if (command == "bt") { 520 } else if (command == "bt") {
150 var cmd = { "id": seqNum, 521 var cmd = { "id": seqNum,
151 "command": "getStackTrace", 522 "command": "getStackTrace",
152 "params": { "isolateId" : currentIsolate.id } }; 523 "params": { "isolateId" : currentIsolate.id } };
153 cmdo.hide();
154 sendCmd(cmd).then(showPromptAfter(handleStackTraceResponse)); 524 sendCmd(cmd).then(showPromptAfter(handleStackTraceResponse));
155 } else if (command == "ll") { 525 } else if (command == "ll") {
156 var cmd = { "id": seqNum, 526 var cmd = { "id": seqNum,
157 "command": "getLibraries", 527 "command": "getLibraries",
158 "params": { "isolateId" : currentIsolate.id } }; 528 "params": { "isolateId" : currentIsolate.id } };
159 cmdo.hide();
160 sendCmd(cmd).then(showPromptAfter(handleGetLibraryResponse)); 529 sendCmd(cmd).then(showPromptAfter(handleGetLibraryResponse));
161 } else if (command == "sbp" && args.length >= 2) { 530 } else if (command == "sbp" && args.length >= 2) {
162 var url, line; 531 var url, line;
163 if (args.length == 2 && currentIsolate.pausedLocation != null) { 532 if (args.length == 2 && currentIsolate.pausedLocation != null) {
164 url = currentIsolate.pausedLocation["url"]; 533 url = currentIsolate.pausedLocation["url"];
165 assert(url != null); 534 assert(url != null);
166 line = int.parse(args[1]); 535 line = int.parse(args[1]);
167 } else { 536 } else {
168 url = args[1]; 537 url = args[1];
169 line = int.parse(args[2]); 538 line = int.parse(args[2]);
170 } 539 }
171 var cmd = { "id": seqNum, 540 var cmd = { "id": seqNum,
172 "command": "setBreakpoint", 541 "command": "setBreakpoint",
173 "params": { "isolateId" : currentIsolate.id, 542 "params": { "isolateId" : currentIsolate.id,
174 "url": url, 543 "url": url,
175 "line": line }}; 544 "line": line }};
176 cmdo.hide();
177 sendCmd(cmd).then(showPromptAfter(handleSetBpResponse)); 545 sendCmd(cmd).then(showPromptAfter(handleSetBpResponse));
178 } else if (command == "rbp" && args.length == 2) { 546 } else if (command == "rbp" && args.length == 2) {
179 var cmd = { "id": seqNum, 547 var cmd = { "id": seqNum,
180 "command": "removeBreakpoint", 548 "command": "removeBreakpoint",
181 "params": { "isolateId" : currentIsolate.id, 549 "params": { "isolateId" : currentIsolate.id,
182 "breakpointId": int.parse(args[1]) } }; 550 "breakpointId": int.parse(args[1]) } };
183 cmdo.hide();
184 sendCmd(cmd).then(showPromptAfter(handleGenericResponse)); 551 sendCmd(cmd).then(showPromptAfter(handleGenericResponse));
185 } else if (command == "ls" && args.length == 2) { 552 } else if (command == "ls" && args.length == 2) {
186 var cmd = { "id": seqNum, 553 var cmd = { "id": seqNum,
187 "command": "getScriptURLs", 554 "command": "getScriptURLs",
188 "params": { "isolateId" : currentIsolate.id, 555 "params": { "isolateId" : currentIsolate.id,
189 "libraryId": int.parse(args[1]) } }; 556 "libraryId": int.parse(args[1]) } };
190 cmdo.hide();
191 sendCmd(cmd).then(showPromptAfter(handleGetScriptsResponse)); 557 sendCmd(cmd).then(showPromptAfter(handleGetScriptsResponse));
192 } else if (command == "eval" && args.length > 3) { 558 } else if (command == "eval" && args.length > 3) {
193 var expr = args.getRange(3, args.length).join(" "); 559 var expr = args.getRange(3, args.length).join(" ");
194 var target = args[1]; 560 var target = args[1];
195 if (target == "obj") { 561 if (target == "obj") {
196 target = "objectId"; 562 target = "objectId";
197 } else if (target == "cls") { 563 } else if (target == "cls") {
198 target = "classId"; 564 target = "classId";
199 } else if (target == "lib") { 565 } else if (target == "lib") {
200 target = "libraryId"; 566 target = "libraryId";
201 } else { 567 } else {
202 huh(); 568 huh();
203 return; 569 return;
204 } 570 }
205 var cmd = { "id": seqNum, 571 var cmd = { "id": seqNum,
206 "command": "evaluateExpr", 572 "command": "evaluateExpr",
207 "params": { "isolateId": currentIsolate.id, 573 "params": { "isolateId": currentIsolate.id,
208 target: int.parse(args[2]), 574 target: int.parse(args[2]),
209 "expression": expr } }; 575 "expression": expr } };
210 cmdo.hide();
211 sendCmd(cmd).then(showPromptAfter(handleEvalResponse)); 576 sendCmd(cmd).then(showPromptAfter(handleEvalResponse));
212 } else if (command == "po" && args.length == 2) { 577 } else if (command == "po" && args.length == 2) {
213 var cmd = { "id": seqNum, 578 var cmd = { "id": seqNum,
214 "command": "getObjectProperties", 579 "command": "getObjectProperties",
215 "params": { "isolateId" : currentIsolate.id, 580 "params": { "isolateId" : currentIsolate.id,
216 "objectId": int.parse(args[1]) } }; 581 "objectId": int.parse(args[1]) } };
217 cmdo.hide();
218 sendCmd(cmd).then(showPromptAfter(handleGetObjPropsResponse)); 582 sendCmd(cmd).then(showPromptAfter(handleGetObjPropsResponse));
219 } else if (command == "pl" && args.length >= 3) { 583 } else if (command == "pl" && args.length >= 3) {
220 var cmd; 584 var cmd;
221 if (args.length == 3) { 585 if (args.length == 3) {
222 cmd = { "id": seqNum, 586 cmd = { "id": seqNum,
223 "command": "getListElements", 587 "command": "getListElements",
224 "params": { "isolateId" : currentIsolate.id, 588 "params": { "isolateId" : currentIsolate.id,
225 "objectId": int.parse(args[1]), 589 "objectId": int.parse(args[1]),
226 "index": int.parse(args[2]) } }; 590 "index": int.parse(args[2]) } };
227 } else { 591 } else {
228 cmd = { "id": seqNum, 592 cmd = { "id": seqNum,
229 "command": "getListElements", 593 "command": "getListElements",
230 "params": { "isolateId" : currentIsolate.id, 594 "params": { "isolateId" : currentIsolate.id,
231 "objectId": int.parse(args[1]), 595 "objectId": int.parse(args[1]),
232 "index": int.parse(args[2]), 596 "index": int.parse(args[2]),
233 "length": int.parse(args[3]) } }; 597 "length": int.parse(args[3]) } };
234 } 598 }
235 cmdo.hide();
236 sendCmd(cmd).then(showPromptAfter(handleGetListResponse)); 599 sendCmd(cmd).then(showPromptAfter(handleGetListResponse));
237 } else if (command == "pc" && args.length == 2) { 600 } else if (command == "pc" && args.length == 2) {
238 var cmd = { "id": seqNum, 601 var cmd = { "id": seqNum,
239 "command": "getClassProperties", 602 "command": "getClassProperties",
240 "params": { "isolateId" : currentIsolate.id, 603 "params": { "isolateId" : currentIsolate.id,
241 "classId": int.parse(args[1]) } }; 604 "classId": int.parse(args[1]) } };
242 cmdo.hide();
243 sendCmd(cmd).then(showPromptAfter(handleGetClassPropsResponse)); 605 sendCmd(cmd).then(showPromptAfter(handleGetClassPropsResponse));
244 } else if (command == "plib" && args.length == 2) { 606 } else if (command == "plib" && args.length == 2) {
245 var cmd = { "id": seqNum, 607 var cmd = { "id": seqNum,
246 "command": "getLibraryProperties", 608 "command": "getLibraryProperties",
247 "params": {"isolateId" : currentIsolate.id, 609 "params": {"isolateId" : currentIsolate.id,
248 "libraryId": int.parse(args[1]) } }; 610 "libraryId": int.parse(args[1]) } };
249 cmdo.hide();
250 sendCmd(cmd).then(showPromptAfter(handleGetLibraryPropsResponse)); 611 sendCmd(cmd).then(showPromptAfter(handleGetLibraryPropsResponse));
251 } else if (command == "slib" && args.length == 3) { 612 } else if (command == "slib" && args.length == 3) {
252 var cmd = { "id": seqNum, 613 var cmd = { "id": seqNum,
253 "command": "setLibraryProperties", 614 "command": "setLibraryProperties",
254 "params": {"isolateId" : currentIsolate.id, 615 "params": {"isolateId" : currentIsolate.id,
255 "libraryId": int.parse(args[1]), 616 "libraryId": int.parse(args[1]),
256 "debuggingEnabled": args[2] } }; 617 "debuggingEnabled": args[2] } };
257 cmdo.hide();
258 sendCmd(cmd).then(showPromptAfter(handleSetLibraryPropsResponse)); 618 sendCmd(cmd).then(showPromptAfter(handleSetLibraryPropsResponse));
259 } else if (command == "pg" && args.length == 2) { 619 } else if (command == "pg" && args.length == 2) {
260 var cmd = { "id": seqNum, 620 var cmd = { "id": seqNum,
261 "command": "getGlobalVariables", 621 "command": "getGlobalVariables",
262 "params": { "isolateId" : currentIsolate.id, 622 "params": { "isolateId" : currentIsolate.id,
263 "libraryId": int.parse(args[1]) } }; 623 "libraryId": int.parse(args[1]) } };
264 cmdo.hide();
265 sendCmd(cmd).then(showPromptAfter(handleGetGlobalVarsResponse)); 624 sendCmd(cmd).then(showPromptAfter(handleGetGlobalVarsResponse));
266 } else if (command == "gs" && args.length == 3) { 625 } else if (command == "gs" && args.length == 3) {
267 var cmd = { "id": seqNum, 626 var cmd = { "id": seqNum,
268 "command": "getScriptSource", 627 "command": "getScriptSource",
269 "params": { "isolateId" : currentIsolate.id, 628 "params": { "isolateId" : currentIsolate.id,
270 "libraryId": int.parse(args[1]), 629 "libraryId": int.parse(args[1]),
271 "url": args[2] } }; 630 "url": args[2] } };
272 cmdo.hide();
273 sendCmd(cmd).then(showPromptAfter(handleGetSourceResponse)); 631 sendCmd(cmd).then(showPromptAfter(handleGetSourceResponse));
274 } else if (command == "tok" && args.length == 3) { 632 } else if (command == "tok" && args.length == 3) {
275 var cmd = { "id": seqNum, 633 var cmd = { "id": seqNum,
276 "command": "getLineNumberTable", 634 "command": "getLineNumberTable",
277 "params": { "isolateId" : currentIsolate.id, 635 "params": { "isolateId" : currentIsolate.id,
278 "libraryId": int.parse(args[1]), 636 "libraryId": int.parse(args[1]),
279 "url": args[2] } }; 637 "url": args[2] } };
280 cmdo.hide();
281 sendCmd(cmd).then(showPromptAfter(handleGetLineTableResponse)); 638 sendCmd(cmd).then(showPromptAfter(handleGetLineTableResponse));
282 } else if (command == "epi" && args.length == 2) { 639 } else if (command == "epi" && args.length == 2) {
283 var cmd = { "id": seqNum, 640 var cmd = { "id": seqNum,
284 "command": "setPauseOnException", 641 "command": "setPauseOnException",
285 "params": { "isolateId" : currentIsolate.id, 642 "params": { "isolateId" : currentIsolate.id,
286 "exceptions": args[1] } }; 643 "exceptions": args[1] } };
287 cmdo.hide();
288 sendCmd(cmd).then(showPromptAfter(handleGenericResponse)); 644 sendCmd(cmd).then(showPromptAfter(handleGenericResponse));
289 } else if (command == "li") { 645 } else if (command == "li") {
290 var cmd = { "id": seqNum, "command": "getIsolateIds" }; 646 var cmd = { "id": seqNum, "command": "getIsolateIds" };
291 cmdo.hide();
292 sendCmd(cmd).then(showPromptAfter(handleGetIsolatesResponse)); 647 sendCmd(cmd).then(showPromptAfter(handleGetIsolatesResponse));
293 } else if (command == "sci" && args.length == 2) { 648 } else if (command == "sci" && args.length == 2) {
294 var id = int.parse(args[1]); 649 var id = int.parse(args[1]);
295 if (targetIsolates[id] != null) { 650 if (targetIsolates[id] != null) {
296 currentIsolate = targetIsolates[id]; 651 currentIsolate = targetIsolates[id];
297 print("Setting current target isolate to $id"); 652 print("Setting current target isolate to $id");
298 } else { 653 } else {
299 print("$id is not a valid isolate id"); 654 print("$id is not a valid isolate id");
300 } 655 }
656 cmdo.show();
301 } else if (command == "i" && args.length == 2) { 657 } else if (command == "i" && args.length == 2) {
302 var cmd = { "id": seqNum, 658 var cmd = { "id": seqNum,
303 "command": "interrupt", 659 "command": "interrupt",
304 "params": { "isolateId": int.parse(args[1]) } }; 660 "params": { "isolateId": int.parse(args[1]) } };
305 cmdo.hide();
306 sendCmd(cmd).then(showPromptAfter(handleGenericResponse)); 661 sendCmd(cmd).then(showPromptAfter(handleGenericResponse));
307 } else if (command == "q") { 662 } else if (command.length == 0) {
308 quitShell(); 663 huh();
309 } else if (command == "h") { 664 cmdo.show();
310 printHelp();
311 } else { 665 } else {
312 huh(); 666 // TODO(turnidge): Use this for all commands.
667 var matches = matchCommand(command, true);
668 if (matches.length == 0) {
669 huh();
670 cmdo.show();
671 } else if (matches.length == 1) {
672 matches[0].run(args).then((_) {
673 cmdo.show();
674 });
675 } else {
676 var matchNames = matches.map((handler) => handler.name);
677 print("Ambigous command '$command' : ${matchNames.toList()}");
678 cmdo.show();
679 }
313 } 680 }
314 } 681 }
315 682
316 683
684 void processError(error, trace) {
685 cmdo.hide();
686 print("\nInternal error:\n$error\n$trace");
687 cmdo.show();
688 }
689
690
691 void processDone() {
692 debuggerQuit();
693 }
694
695
317 String remoteObject(value) { 696 String remoteObject(value) {
318 var kind = value["kind"]; 697 var kind = value["kind"];
319 var text = value["text"]; 698 var text = value["text"];
320 var id = value["objectId"]; 699 var id = value["objectId"];
321 if (kind == "string") { 700 if (kind == "string") {
322 return "(string, id $id) '$text'"; 701 return "(string, id $id) '$text'";
323 } else if (kind == "list") { 702 } else if (kind == "list") {
324 var len = value["length"]; 703 var len = value["length"];
325 return "(list, id $id, len $len) $text"; 704 return "(list, id $id, len $len) $text";
326 } else if (kind == "object") { 705 } else if (kind == "object") {
(...skipping 428 matching lines...) Expand 10 before | Expand all | Expand 10 after
755 } 1134 }
756 1135
757 List<String> debuggerCommandCompleter(List<String> commandParts) { 1136 List<String> debuggerCommandCompleter(List<String> commandParts) {
758 List<String> completions = new List<String>(); 1137 List<String> completions = new List<String>();
759 1138
760 // TODO(turnidge): Have a global command table and use it to for 1139 // TODO(turnidge): Have a global command table and use it to for
761 // help messages, command completion, and command dispatching. For now 1140 // help messages, command completion, and command dispatching. For now
762 // we hardcode the list here. 1141 // we hardcode the list here.
763 // 1142 //
764 // TODO(turnidge): Implement completion for arguments as well. 1143 // TODO(turnidge): Implement completion for arguments as well.
765 List<String> allCommands = ['q', 'bt', 'r', 's', 'so', 'si', 'sbp', 'rbp', 1144 List<String> oldCommands = ['bt', 'r', 's', 'so', 'si', 'sbp', 'rbp',
766 'po', 'eval', 'pl', 'pc', 'll', 'plib', 'slib', 1145 'po', 'eval', 'pl', 'pc', 'll', 'plib', 'slib',
767 'pg', 'ls', 'gs', 'tok', 'epi', 'li', 'i', 'h']; 1146 'pg', 'ls', 'gs', 'tok', 'epi', 'li', 'i' ];
768 1147
769 // Completion of first word in the command. 1148 // Completion of first word in the command.
770 if (commandParts.length == 1) { 1149 if (commandParts.length == 1) {
771 String prefix = commandParts.last; 1150 String prefix = commandParts.last;
772 for (String command in allCommands) { 1151 for (var command in oldCommands) {
773 if (command.startsWith(prefix)) { 1152 if (command.startsWith(prefix)) {
774 completions.add(command); 1153 completions.add(command);
775 } 1154 }
776 } 1155 }
1156 for (var command in commandList) {
1157 if (command.name.startsWith(prefix)) {
1158 completions.add(command.name);
1159 }
1160 }
777 } 1161 }
778 1162
779 return completions; 1163 return completions;
780 } 1164 }
781 1165
782 void debuggerMain() { 1166 Future closeCommando() {
1167 var subscription = cmdSubscription;
1168 cmdSubscription = null;
1169 cmdo = null;
1170
1171 var future = subscription.cancel();
1172 if (future != null) {
1173 return future;
1174 } else {
1175 return new Future.value();
1176 }
1177 }
1178
1179
1180 Future openVmSocket(int attempt) {
1181 return Socket.connect("127.0.0.1", debugPort).then(
1182 setupVmSocket,
1183 onError: (e) {
1184 // We were unable to connect to the debugger's port. Try again.
1185 retryOpenVmSocket(e, attempt);
1186 });
1187 }
1188
1189
1190 void setupVmSocket(Socket s) {
1191 vmSock = s;
1192 vmSock.setOption(SocketOption.TCP_NODELAY, true);
1193 var stringStream = vmSock.transform(UTF8.decoder);
783 outstandingCommands = new Map<int, Completer>(); 1194 outstandingCommands = new Map<int, Completer>();
784 Socket.connect("127.0.0.1", 5858).then((s) { 1195 vmSubscription = stringStream.listen(
785 vmSock = s; 1196 (String data) {
786 vmSock.setOption(SocketOption.TCP_NODELAY, true); 1197 processVmData(data);
787 var stringStream = vmSock.transform(UTF8.decoder); 1198 },
788 vmSubscription = stringStream.listen( 1199 onDone: () {
789 (String data) { 1200 cmdo.hide();
790 processVmData(data); 1201 if (verbose) {
791 },
792 onDone: () {
793 print("VM debugger connection closed"); 1202 print("VM debugger connection closed");
794 quitShell(); 1203 }
795 }, 1204 closeVmSocket().then((_) {
796 onError: (err) { 1205 cmdo.show();
797 print("Error in debug connection: $err"); 1206 });
798 // TODO(floitsch): do we want to print the stack trace? 1207 },
799 quitShell(); 1208 onError: (err) {
800 }); 1209 cmdo.hide();
801 cmdo = new Commando(stdin, stdout, processCommand, 1210 // TODO(floitsch): do we want to print the stack trace?
802 completer : debuggerCommandCompleter); 1211 print("Error in debug connection: $err");
803 }); 1212
1213 // TODO(turnidge): Kill the debugged process here?
1214 closeVmSocket().then((_) {
1215 cmdo.show();
1216 });
1217 });
1218 }
1219
1220
1221 Future retryOpenVmSocket(error, int attempt) {
1222 var delay;
1223 if (attempt < 10) {
1224 delay = new Duration(milliseconds:10);
1225 } else if (attempt < 20) {
1226 delay = new Duration(seconds:1);
1227 } else {
1228 // Too many retries. Give up.
1229 //
1230 // TODO(turnidge): Kill the debugged process here?
1231 print('Timed out waiting for debugger to start.\nError: $e');
1232 return closeVmSocket();
1233 }
1234
1235 // Wait and retry.
1236 return new Future.delayed(delay, () {
1237 openVmSocket(attempt + 1);
1238 });
1239 }
1240
1241
1242 Future closeVmSocket() {
1243 if (vmSubscription == null) {
1244 // Already closed, nothing to do.
1245 assert(vmSock == null);
1246 return new Future.value();
1247 }
1248
1249 isDebugging = false;
1250 var subscription = vmSubscription;
1251 var sock = vmSock;
1252
1253 // Wait for the socket to close and the subscription to be
1254 // cancelled. Perhaps overkill, but it means we know these will be
1255 // done.
1256 //
1257 // This is uglier than it needs to be since cancel can return null.
1258 var cleanupFutures = [sock.close()];
1259 var future = subscription.cancel();
1260 if (future != null) {
1261 cleanupFutures.add(future);
1262 }
1263
1264 vmSubscription = null;
1265 vmSock = null;
1266 outstandingCommands = null;
1267
1268 return Future.wait(cleanupFutures);
1269 }
1270
1271 Future debuggerQuit() {
1272 // Kill target process, if any.
1273 if (targetProcess != null) {
1274 if (!targetProcess.kill()) {
1275 print('Unable to kill process ${targetProcess.pid}');
1276 }
1277 }
1278
1279 // Restore terminal settings, close connections.
1280 return Future.wait([closeCommando(), closeVmSocket()]).then((_) {
1281 exit(0);
1282
1283 // Unreachable.
1284 return new Future.value();
1285 });
1286 }
1287
1288
1289 void parseArgs(List<String> args) {
1290 int pos = 0;
1291 settings['vm'] = Platform.executable;
1292 while (pos < args.length && args[pos].startsWith('-')) {
1293 pos++;
1294 }
1295 if (pos < args.length) {
1296 settings['vmargs'] = args.getRange(0, pos).join(' ');
1297 settings['script'] = args[pos];
1298 settings['args'] = args.getRange(pos + 1, args.length).join(' ');
1299 }
804 } 1300 }
805 1301
806 void main(List<String> args) { 1302 void main(List<String> args) {
807 if (args.length > 0) { 1303 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 1304
817 // TODO(turnidge): For now we only show full lines of output 1305 cmdo = new Commando(completer: debuggerCommandCompleter);
818 // from the debugged process. Should show each character. 1306 cmdSubscription = cmdo.commands.listen(processCommand,
819 process.stdout 1307 onError: processError,
820 .transform(UTF8.decoder) 1308 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 } 1309 }
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