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

Side by Side Diff: tools/ddbg.dart

Issue 69343017: Add support for command-line editing to ddbg. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 1 month 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/bin/ddbg.dart » ('j') | tools/ddbg/lib/commando.dart » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 // Simple interactive debugger shell that connects to the Dart VM's debugger
6 // connection port.
7
8 import "dart:convert";
9 import "dart:io";
10 import "dart:async";
11
12
13 Map<int, Completer> outstandingCommands;
14
15 Socket vmSock;
16 String vmData;
17 var stdinSubscription;
18 var vmSubscription;
19 int seqNum = 0;
20 int isolate_id = -1;
21
22 final verbose = false;
23 final printMessages = false;
24
25 // The location of the last paused event.
26 Map pausedLocation = null;
27
28
29 void printHelp() {
30 print("""
31 q Quit debugger shell
32 bt Show backtrace
33 r Resume execution
34 s Single step
35 so Step over
36 si Step into
37 sbp [<file>] <line> Set breakpoint
38 rbp <id> Remove breakpoint with given id
39 po <id> Print object info for given id
40 eval obj <id> <expr> Evaluate expr on object id
41 eval cls <id> <expr> Evaluate expr on class id
42 eval lib <id> <expr> Evaluate expr in toplevel of library id
43 pl <id> <idx> [<len>] Print list element/slice
44 pc <id> Print class info for given id
45 ll List loaded libraries
46 plib <id> Print library info for given library id
47 slib <id> <true|false> Set library id debuggable
48 pg <id> Print all global variables visible within given library id
49 ls <lib_id> List loaded scripts in library
50 gs <lib_id> <script_url> Get source text of script in library
51 tok <lib_id> <script_url> Get line and token table of script in library
52 epi <none|all|unhandled> Set exception pause info
53 li List ids of all isolates in the VM
54 i <id> Interrupt execution of given isolate id
55 h Print help
56 """);
57 }
58
59
60 void quitShell() {
61 vmSubscription.cancel();
62 vmSock.close();
63 stdinSubscription.cancel();
64 }
65
66
67 Future sendCmd(Map<String, dynamic> cmd) {
68 var completer = new Completer();
69 int id = cmd["id"];
70 outstandingCommands[id] = completer;
71 if (verbose) {
72 print("sending: '${JSON.encode(cmd)}'");
73 }
74 vmSock.write(JSON.encode(cmd));
75 return completer.future;
76 }
77
78 void processCommand(String cmdLine) {
79
80 void huh() {
81 print("'$cmdLine' not understood, try h for help");
82 }
83
84 seqNum++;
85 var args = cmdLine.split(' ');
86 if (args.length == 0) {
87 return;
88 }
89 var command = args[0];
90 var simple_commands =
91 { 'r':'resume', 's':'stepOver', 'si':'stepInto', 'so':'stepOut'};
92 if (simple_commands[command] != null) {
93 var cmd = { "id": seqNum,
94 "command": simple_commands[command],
95 "params": { "isolateId" : isolate_id } };
96 sendCmd(cmd).then((result) => handleGenericResponse(result));
97 } else if (command == "bt") {
98 var cmd = { "id": seqNum,
99 "command": "getStackTrace",
100 "params": { "isolateId" : isolate_id } };
101 sendCmd(cmd).then((result) => handleStackTraceResponse(result));
102 } else if (command == "ll") {
103 var cmd = { "id": seqNum,
104 "command": "getLibraries",
105 "params": { "isolateId" : isolate_id } };
106 sendCmd(cmd).then((result) => handleGetLibraryResponse(result));
107 } else if (command == "sbp" && args.length >= 2) {
108 var url, line;
109 if (args.length == 2 && pausedLocation != null) {
110 url = pausedLocation["url"];
111 assert(url != null);
112 line = int.parse(args[1]);
113 } else {
114 url = args[1];
115 line = int.parse(args[2]);
116 }
117 var cmd = { "id": seqNum,
118 "command": "setBreakpoint",
119 "params": { "isolateId" : isolate_id,
120 "url": url,
121 "line": line }};
122 sendCmd(cmd).then((result) => handleSetBpResponse(result));
123 } else if (command == "rbp" && args.length == 2) {
124 var cmd = { "id": seqNum,
125 "command": "removeBreakpoint",
126 "params": { "isolateId" : isolate_id,
127 "breakpointId": int.parse(args[1]) } };
128 sendCmd(cmd).then((result) => handleGenericResponse(result));
129 } else if (command == "ls" && args.length == 2) {
130 var cmd = { "id": seqNum,
131 "command": "getScriptURLs",
132 "params": { "isolateId" : isolate_id,
133 "libraryId": int.parse(args[1]) } };
134 sendCmd(cmd).then((result) => handleGetScriptsResponse(result));
135 } else if (command == "eval" && args.length > 3) {
136 var expr = args.getRange(3, args.length).join(" ");
137 var target = args[1];
138 if (target == "obj") {
139 target = "objectId";
140 } else if (target == "cls") {
141 target = "classId";
142 } else if (target == "lib") {
143 target = "libraryId";
144 } else {
145 huh();
146 return;
147 }
148 var cmd = { "id": seqNum,
149 "command": "evaluateExpr",
150 "params": { "isolateId": isolate_id,
151 target: int.parse(args[2]),
152 "expression": expr } };
153 sendCmd(cmd).then((result) => handleEvalResponse(result));
154 } else if (command == "po" && args.length == 2) {
155 var cmd = { "id": seqNum,
156 "command": "getObjectProperties",
157 "params": { "isolateId" : isolate_id,
158 "objectId": int.parse(args[1]) } };
159 sendCmd(cmd).then((result) => handleGetObjPropsResponse(result));
160 } else if (command == "pl" && args.length >= 3) {
161 var cmd;
162 if (args.length == 3) {
163 cmd = { "id": seqNum,
164 "command": "getListElements",
165 "params": { "isolateId" : isolate_id,
166 "objectId": int.parse(args[1]),
167 "index": int.parse(args[2]) } };
168 } else {
169 cmd = { "id": seqNum,
170 "command": "getListElements",
171 "params": { "isolateId" : isolate_id,
172 "objectId": int.parse(args[1]),
173 "index": int.parse(args[2]),
174 "length": int.parse(args[3]) } };
175 }
176 sendCmd(cmd).then((result) => handleGetListResponse(result));
177 } else if (command == "pc" && args.length == 2) {
178 var cmd = { "id": seqNum,
179 "command": "getClassProperties",
180 "params": { "isolateId" : isolate_id,
181 "classId": int.parse(args[1]) } };
182 sendCmd(cmd).then((result) => handleGetClassPropsResponse(result));
183 } else if (command == "plib" && args.length == 2) {
184 var cmd = { "id": seqNum,
185 "command": "getLibraryProperties",
186 "params": {"isolateId" : isolate_id,
187 "libraryId": int.parse(args[1]) } };
188 sendCmd(cmd).then((result) => handleGetLibraryPropsResponse(result));
189 } else if (command == "slib" && args.length == 3) {
190 var cmd = { "id": seqNum,
191 "command": "setLibraryProperties",
192 "params": {"isolateId" : isolate_id,
193 "libraryId": int.parse(args[1]),
194 "debuggingEnabled": args[2] } };
195 sendCmd(cmd).then((result) => handleSetLibraryPropsResponse(result));
196 } else if (command == "pg" && args.length == 2) {
197 var cmd = { "id": seqNum,
198 "command": "getGlobalVariables",
199 "params": { "isolateId" : isolate_id,
200 "libraryId": int.parse(args[1]) } };
201 sendCmd(cmd).then((result) => handleGetGlobalVarsResponse(result));
202 } else if (command == "gs" && args.length == 3) {
203 var cmd = { "id": seqNum,
204 "command": "getScriptSource",
205 "params": { "isolateId" : isolate_id,
206 "libraryId": int.parse(args[1]),
207 "url": args[2] } };
208 sendCmd(cmd).then((result) => handleGetSourceResponse(result));
209 } else if (command == "tok" && args.length == 3) {
210 var cmd = { "id": seqNum,
211 "command": "getLineNumberTable",
212 "params": { "isolateId" : isolate_id,
213 "libraryId": int.parse(args[1]),
214 "url": args[2] } };
215 sendCmd(cmd).then((result) => handleGetLineTableResponse(result));
216 } else if (command == "epi" && args.length == 2) {
217 var cmd = { "id": seqNum,
218 "command": "setPauseOnException",
219 "params": { "isolateId" : isolate_id,
220 "exceptions": args[1] } };
221 sendCmd(cmd).then((result) => handleGenericResponse(result));
222 } else if (command == "li") {
223 var cmd = { "id": seqNum, "command": "getIsolateIds" };
224 sendCmd(cmd).then((result) => handleGetIsolatesResponse(result));
225 } else if (command == "i" && args.length == 2) {
226 var cmd = { "id": seqNum,
227 "command": "interrupt",
228 "params": { "isolateId": int.parse(args[1]) } };
229 sendCmd(cmd).then((result) => handleGenericResponse(result));
230 } else if (command == "q") {
231 quitShell();
232 } else if (command == "h") {
233 printHelp();
234 } else {
235 huh();
236 }
237 }
238
239
240 String remoteObject(value) {
241 var kind = value["kind"];
242 var text = value["text"];
243 var id = value["objectId"];
244 if (kind == "string") {
245 return "(string, id $id) '$text'";
246 } else if (kind == "list") {
247 var len = value["length"];
248 return "(list, id $id, len $len) $text";
249 } else if (kind == "object") {
250 return "(obj, id $id) $text";
251 } else {
252 return "$text";
253 }
254 }
255
256
257 printNamedObject(obj) {
258 var name = obj["name"];
259 var value = obj["value"];
260 print(" $name = ${remoteObject(value)}");
261 }
262
263
264 handleGetObjPropsResponse(response) {
265 Map props = response["result"];
266 int class_id = props["classId"];
267 if (class_id == -1) {
268 print(" null");
269 return;
270 }
271 List fields = props["fields"];
272 print(" class id: $class_id");
273 for (int i = 0; i < fields.length; i++) {
274 printNamedObject(fields[i]);
275 }
276 }
277
278 handleGetListResponse(response) {
279 Map result = response["result"];
280 if (result["elements"] != null) {
281 // List slice.
282 var index = result["index"];
283 var length = result["length"];
284 List elements = result["elements"];
285 assert(length == elements.length);
286 for (int i = 0; i < length; i++) {
287 var kind = elements[i]["kind"];
288 var text = elements[i]["text"];
289 print(" ${index + i}: ($kind) $text");
290 }
291 } else {
292 // One element, a remote object.
293 print(result);
294 print(" ${remoteObject(result)}");
295 }
296 }
297
298
299 handleGetClassPropsResponse(response) {
300 Map props = response["result"];
301 assert(props["name"] != null);
302 int libId = props["libraryId"];
303 assert(libId != null);
304 print(" class ${props["name"]} (library id: $libId)");
305 List fields = props["fields"];
306 if (fields.length > 0) {
307 print(" static fields:");
308 for (int i = 0; i < fields.length; i++) {
309 printNamedObject(fields[i]);
310 }
311 }
312 }
313
314
315 handleGetLibraryPropsResponse(response) {
316 Map props = response["result"];
317 assert(props["url"] != null);
318 print(" library url: ${props["url"]}");
319 assert(props["debuggingEnabled"] != null);
320 print(" debugging enabled: ${props["debuggingEnabled"]}");
321 List imports = props["imports"];
322 assert(imports != null);
323 if (imports.length > 0) {
324 print(" imports:");
325 for (int i = 0; i < imports.length; i++) {
326 print(" id ${imports[i]["libraryId"]} prefix ${imports[i]["prefix"]}");
327 }
328 }
329 List globals = props["globals"];
330 assert(globals != null);
331 if (globals.length > 0) {
332 print(" global variables:");
333 for (int i = 0; i < globals.length; i++) {
334 printNamedObject(globals[i]);
335 }
336 }
337 }
338
339
340 handleSetLibraryPropsResponse(response) {
341 Map props = response["result"];
342 assert(props["debuggingEnabled"] != null);
343 print(" debugging enabled: ${props["debuggingEnabled"]}");
344 }
345
346
347 handleGetGlobalVarsResponse(response) {
348 List globals = response["result"]["globals"];
349 for (int i = 0; i < globals.length; i++) {
350 printNamedObject(globals[i]);
351 }
352 }
353
354
355 handleGetSourceResponse(response) {
356 Map result = response["result"];
357 String source = result["text"];
358 print("Source text:\n$source\n--------");
359 }
360
361
362 handleGetLineTableResponse(response) {
363 Map result = response["result"];
364 var info = result["lines"];
365 print("Line info table:\n$info");
366 }
367
368
369 handleGetIsolatesResponse(response) {
370 Map result = response["result"];
371 print("Isolates: ${result["isolateIds"]}");
372 }
373
374
375 void handleGetLibraryResponse(response) {
376 Map result = response["result"];
377 List libs = result["libraries"];
378 print("Loaded libraries:");
379 print(libs);
380 for (int i = 0; i < libs.length; i++) {
381 print(" ${libs[i]["id"]} ${libs[i]["url"]}");
382 }
383 }
384
385
386 void handleGetScriptsResponse(response) {
387 Map result = response["result"];
388 List urls = result["urls"];
389 print("Loaded scripts:");
390 for (int i = 0; i < urls.length; i++) {
391 print(" $i ${urls[i]}");
392 }
393 }
394
395
396 void handleEvalResponse(response) {
397 Map result = response["result"];
398 print(remoteObject(result));
399 }
400
401
402 void handleSetBpResponse(response) {
403 Map result = response["result"];
404 var id = result["breakpointId"];
405 assert(id != null);
406 print("Set BP $id");
407 }
408
409
410 void handleGenericResponse(response) {
411 if (response["error"] != null) {
412 print("Error: ${response["error"]}");
413 }
414 }
415
416
417 void handleStackTraceResponse(response) {
418 Map result = response["result"];
419 List callFrames = result["callFrames"];
420 assert(callFrames != null);
421 printStackTrace(callFrames);
422 }
423
424
425 void printStackFrame(frame_num, Map frame) {
426 var fname = frame["functionName"];
427 var libId = frame["location"]["libraryId"];
428 var url = frame["location"]["url"];
429 var toff = frame["location"]["tokenOffset"];
430 print("$frame_num $fname (url: $url token: $toff lib: $libId)");
431 List locals = frame["locals"];
432 for (int i = 0; i < locals.length; i++) {
433 printNamedObject(locals[i]);
434 }
435 }
436
437
438 void printStackTrace(List frames) {
439 for (int i = 0; i < frames.length; i++) {
440 printStackFrame(i, frames[i]);
441 }
442 }
443
444
445 void handlePausedEvent(msg) {
446 assert(msg["params"] != null);
447 var reason = msg["params"]["reason"];
448 isolate_id = msg["params"]["isolateId"];
449 assert(isolate_id != null);
450 pausedLocation = msg["params"]["location"];
451 assert(pausedLocation != null);
452 if (reason == "breakpoint") {
453 print("Isolate $isolate_id paused on breakpoint");
454 print("location: $pausedLocation");
455 } else if (reason == "interrupted") {
456 print("Isolate $isolate_id paused due to an interrupt");
457 } else {
458 assert(reason == "exception");
459 var excObj = msg["params"]["exception"];
460 print("Isolate $isolate_id paused on exception");
461 print(remoteObject(excObj));
462 }
463 }
464
465
466 void processVmMessage(String jsonString) {
467 var msg = JSON.decode(jsonString);
468 if (msg == null) {
469 return;
470 }
471 var event = msg["event"];
472 if (event == "paused") {
473 handlePausedEvent(msg);
474 return;
475 }
476 if (event == "breakpointResolved") {
477 Map params = msg["params"];
478 assert(params != null);
479 print("BP ${params["breakpointId"]} resolved and "
480 "set at line ${params["line"]}.");
481 return;
482 }
483 if (event == "isolate") {
484 Map params = msg["params"];
485 assert(params != null);
486 print("Isolate ${params["id"]} has been ${params["reason"]}.");
487 return;
488 }
489 if (msg["id"] != null) {
490 var id = msg["id"];
491 if (outstandingCommands.containsKey(id)) {
492 if (msg["error"] != null) {
493 print("VM says: ${msg["error"]}");
494 } else {
495 var completer = outstandingCommands[id];
496 completer.complete(msg);
497 }
498 outstandingCommands.remove(id);
499 }
500 }
501 }
502
503 bool haveGarbageVmData() {
504 if (vmData == null || vmData.length == 0) return false;
505 var i = 0, char = " ";
506 while (i < vmData.length) {
507 char = vmData[i];
508 if (char != " " && char != "\n" && char != "\r" && char != "\t") break;
509 i++;
510 }
511 if (i >= vmData.length) {
512 return false;
513 } else {
514 return char != "{";
515 }
516 }
517
518
519 void processVmData(String data) {
520 if (vmData == null || vmData.length == 0) {
521 vmData = data;
522 } else {
523 vmData = vmData + data;
524 }
525 if (haveGarbageVmData()) {
526 print("Error: have garbage data from VM: '$vmData'");
527 return;
528 }
529 int msg_len = jsonObjectLength(vmData);
530 if (printMessages && msg_len == 0) {
531 print("have partial or illegal json message"
532 " of ${vmData.length} chars:\n'$vmData'");
533 return;
534 }
535 while (msg_len > 0 && msg_len <= vmData.length) {
536 if (msg_len == vmData.length) {
537 if (printMessages) { print("have one full message:\n$vmData"); }
538 processVmMessage(vmData);
539 vmData = null;
540 return;
541 }
542 if (printMessages) { print("at least one message: '$vmData'"); }
543 var msg = vmData.substring(0, msg_len);
544 if (printMessages) { print("first message: $msg"); }
545 vmData = vmData.substring(msg_len);
546 if (haveGarbageVmData()) {
547 print("Error: garbage data after previous message: '$vmData'");
548 print("Previous message was: '$msg'");
549 return;
550 }
551 processVmMessage(msg);
552 msg_len = jsonObjectLength(vmData);
553 }
554 if (printMessages) { print("leftover vm data '$vmData'"); }
555 }
556
557 /**
558 * Skip past a JSON object value.
559 * The object value must start with '{' and continues to the
560 * matching '}'. No attempt is made to otherwise validate the contents
561 * as JSON. If it is invalid, a later [parseJson] will fail.
562 */
563 int jsonObjectLength(String string) {
564 int skipWhitespace(int index) {
565 while (index < string.length) {
566 String char = string[index];
567 if (char != " " && char != "\n" && char != "\r" && char != "\t") break;
568 index++;
569 }
570 return index;
571 }
572 int skipString(int index) {
573 assert(string[index - 1] == '"');
574 while (index < string.length) {
575 String char = string[index];
576 if (char == '"') return index + 1;
577 if (char == r'\') index++;
578 if (index == string.length) return index;
579 index++;
580 }
581 return index;
582 }
583 int index = 0;
584 index = skipWhitespace(index);
585 // Bail out if the first non-whitespace character isn't '{'.
586 if (index == string.length || string[index] != '{') return 0;
587 int nesting = 0;
588 while (index < string.length) {
589 String char = string[index++];
590 if (char == '{') {
591 nesting++;
592 } else if (char == '}') {
593 nesting--;
594 if (nesting == 0) return index;
595 } else if (char == '"') {
596 // Strings can contain braces. Skip their content.
597 index = skipString(index);
598 }
599 }
600 return 0;
601 }
602
603
604 void debuggerMain() {
605 outstandingCommands = new Map<int, Completer>();
606 Socket.connect("127.0.0.1", 5858).then((s) {
607 vmSock = s;
608 vmSock.setOption(SocketOption.TCP_NODELAY, true);
609 var stringStream = vmSock.transform(UTF8.decoder);
610 vmSubscription = stringStream.listen(
611 (String data) {
612 processVmData(data);
613 },
614 onDone: () {
615 print("VM debugger connection closed");
616 quitShell();
617 },
618 onError: (err) {
619 print("Error in debug connection: $err");
620 // TODO(floitsch): do we want to print the stack trace?
621 quitShell();
622 });
623 stdinSubscription = stdin.transform(UTF8.decoder)
624 .transform(new LineSplitter())
625 .listen((String line) => processCommand(line));
626 });
627 }
628
629 void main(List<String> arguments) {
630 if (arguments.length > 0) {
631 arguments = <String>['--debug', '--verbose_debug']..addAll(arguments);
632 Process.start(Platform.executable, arguments).then((Process process) {
633 process.stdin.close();
634 process.exitCode.then((int exitCode) {
635 print('${arguments.join(" ")} exited with $exitCode');
636 });
637 debuggerMain();
638 });
639 } else {
640 debuggerMain();
641 }
642 }
OLDNEW
« no previous file with comments | « no previous file | tools/ddbg/bin/ddbg.dart » ('j') | tools/ddbg/lib/commando.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698