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

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 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/bin/ddbg.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 if (kind == "function") {
252 var location = value['location'] != null
253 ? ", file '${value['location']['url']}'"
254 ", token pos ${value['location']['tokenOffset']}"
255 : "";
256 var name = value['name'];
257 var signature = value['signature'];
258 return "(closure ${name}${signature} $location)";
259 } else {
260 return "$text";
261 }
262 }
263
264
265 printNamedObject(obj) {
266 var name = obj["name"];
267 var value = obj["value"];
268 print(" $name = ${remoteObject(value)}");
269 }
270
271
272 handleGetObjPropsResponse(response) {
273 Map props = response["result"];
274 int class_id = props["classId"];
275 if (class_id == -1) {
276 print(" null");
277 return;
278 }
279 List fields = props["fields"];
280 print(" class id: $class_id");
281 for (int i = 0; i < fields.length; i++) {
282 printNamedObject(fields[i]);
283 }
284 }
285
286 handleGetListResponse(response) {
287 Map result = response["result"];
288 if (result["elements"] != null) {
289 // List slice.
290 var index = result["index"];
291 var length = result["length"];
292 List elements = result["elements"];
293 assert(length == elements.length);
294 for (int i = 0; i < length; i++) {
295 var kind = elements[i]["kind"];
296 var text = elements[i]["text"];
297 print(" ${index + i}: ($kind) $text");
298 }
299 } else {
300 // One element, a remote object.
301 print(result);
302 print(" ${remoteObject(result)}");
303 }
304 }
305
306
307 handleGetClassPropsResponse(response) {
308 Map props = response["result"];
309 assert(props["name"] != null);
310 int libId = props["libraryId"];
311 assert(libId != null);
312 print(" class ${props["name"]} (library id: $libId)");
313 List fields = props["fields"];
314 if (fields.length > 0) {
315 print(" static fields:");
316 for (int i = 0; i < fields.length; i++) {
317 printNamedObject(fields[i]);
318 }
319 }
320 }
321
322
323 handleGetLibraryPropsResponse(response) {
324 Map props = response["result"];
325 assert(props["url"] != null);
326 print(" library url: ${props["url"]}");
327 assert(props["debuggingEnabled"] != null);
328 print(" debugging enabled: ${props["debuggingEnabled"]}");
329 List imports = props["imports"];
330 assert(imports != null);
331 if (imports.length > 0) {
332 print(" imports:");
333 for (int i = 0; i < imports.length; i++) {
334 print(" id ${imports[i]["libraryId"]} prefix ${imports[i]["prefix"]}");
335 }
336 }
337 List globals = props["globals"];
338 assert(globals != null);
339 if (globals.length > 0) {
340 print(" global variables:");
341 for (int i = 0; i < globals.length; i++) {
342 printNamedObject(globals[i]);
343 }
344 }
345 }
346
347
348 handleSetLibraryPropsResponse(response) {
349 Map props = response["result"];
350 assert(props["debuggingEnabled"] != null);
351 print(" debugging enabled: ${props["debuggingEnabled"]}");
352 }
353
354
355 handleGetGlobalVarsResponse(response) {
356 List globals = response["result"]["globals"];
357 for (int i = 0; i < globals.length; i++) {
358 printNamedObject(globals[i]);
359 }
360 }
361
362
363 handleGetSourceResponse(response) {
364 Map result = response["result"];
365 String source = result["text"];
366 print("Source text:\n$source\n--------");
367 }
368
369
370 handleGetLineTableResponse(response) {
371 Map result = response["result"];
372 var info = result["lines"];
373 print("Line info table:\n$info");
374 }
375
376
377 handleGetIsolatesResponse(response) {
378 Map result = response["result"];
379 print("Isolates: ${result["isolateIds"]}");
380 }
381
382
383 void handleGetLibraryResponse(response) {
384 Map result = response["result"];
385 List libs = result["libraries"];
386 print("Loaded libraries:");
387 print(libs);
388 for (int i = 0; i < libs.length; i++) {
389 print(" ${libs[i]["id"]} ${libs[i]["url"]}");
390 }
391 }
392
393
394 void handleGetScriptsResponse(response) {
395 Map result = response["result"];
396 List urls = result["urls"];
397 print("Loaded scripts:");
398 for (int i = 0; i < urls.length; i++) {
399 print(" $i ${urls[i]}");
400 }
401 }
402
403
404 void handleEvalResponse(response) {
405 Map result = response["result"];
406 print(remoteObject(result));
407 }
408
409
410 void handleSetBpResponse(response) {
411 Map result = response["result"];
412 var id = result["breakpointId"];
413 assert(id != null);
414 print("Set BP $id");
415 }
416
417
418 void handleGenericResponse(response) {
419 if (response["error"] != null) {
420 print("Error: ${response["error"]}");
421 }
422 }
423
424
425 void handleStackTraceResponse(response) {
426 Map result = response["result"];
427 List callFrames = result["callFrames"];
428 assert(callFrames != null);
429 printStackTrace(callFrames);
430 }
431
432
433 void printStackFrame(frame_num, Map frame) {
434 var fname = frame["functionName"];
435 var libId = frame["location"]["libraryId"];
436 var url = frame["location"]["url"];
437 var toff = frame["location"]["tokenOffset"];
438 print("$frame_num $fname (url: $url token: $toff lib: $libId)");
439 List locals = frame["locals"];
440 for (int i = 0; i < locals.length; i++) {
441 printNamedObject(locals[i]);
442 }
443 }
444
445
446 void printStackTrace(List frames) {
447 for (int i = 0; i < frames.length; i++) {
448 printStackFrame(i, frames[i]);
449 }
450 }
451
452
453 void handlePausedEvent(msg) {
454 assert(msg["params"] != null);
455 var reason = msg["params"]["reason"];
456 isolate_id = msg["params"]["isolateId"];
457 assert(isolate_id != null);
458 pausedLocation = msg["params"]["location"];
459 assert(pausedLocation != null);
460 if (reason == "breakpoint") {
461 print("Isolate $isolate_id paused on breakpoint");
462 print("location: $pausedLocation");
463 } else if (reason == "interrupted") {
464 print("Isolate $isolate_id paused due to an interrupt");
465 } else {
466 assert(reason == "exception");
467 var excObj = msg["params"]["exception"];
468 print("Isolate $isolate_id paused on exception");
469 print(remoteObject(excObj));
470 }
471 }
472
473
474 void processVmMessage(String jsonString) {
475 var msg = JSON.decode(jsonString);
476 if (msg == null) {
477 return;
478 }
479 var event = msg["event"];
480 if (event == "paused") {
481 handlePausedEvent(msg);
482 return;
483 }
484 if (event == "breakpointResolved") {
485 Map params = msg["params"];
486 assert(params != null);
487 print("BP ${params["breakpointId"]} resolved and "
488 "set at line ${params["line"]}.");
489 return;
490 }
491 if (event == "isolate") {
492 Map params = msg["params"];
493 assert(params != null);
494 print("Isolate ${params["id"]} has been ${params["reason"]}.");
495 return;
496 }
497 if (msg["id"] != null) {
498 var id = msg["id"];
499 if (outstandingCommands.containsKey(id)) {
500 if (msg["error"] != null) {
501 print("VM says: ${msg["error"]}");
502 } else {
503 var completer = outstandingCommands[id];
504 completer.complete(msg);
505 }
506 outstandingCommands.remove(id);
507 }
508 }
509 }
510
511 bool haveGarbageVmData() {
512 if (vmData == null || vmData.length == 0) return false;
513 var i = 0, char = " ";
514 while (i < vmData.length) {
515 char = vmData[i];
516 if (char != " " && char != "\n" && char != "\r" && char != "\t") break;
517 i++;
518 }
519 if (i >= vmData.length) {
520 return false;
521 } else {
522 return char != "{";
523 }
524 }
525
526
527 void processVmData(String data) {
528 if (vmData == null || vmData.length == 0) {
529 vmData = data;
530 } else {
531 vmData = vmData + data;
532 }
533 if (haveGarbageVmData()) {
534 print("Error: have garbage data from VM: '$vmData'");
535 return;
536 }
537 int msg_len = jsonObjectLength(vmData);
538 if (printMessages && msg_len == 0) {
539 print("have partial or illegal json message"
540 " of ${vmData.length} chars:\n'$vmData'");
541 return;
542 }
543 while (msg_len > 0 && msg_len <= vmData.length) {
544 if (msg_len == vmData.length) {
545 if (printMessages) { print("have one full message:\n$vmData"); }
546 processVmMessage(vmData);
547 vmData = null;
548 return;
549 }
550 if (printMessages) { print("at least one message: '$vmData'"); }
551 var msg = vmData.substring(0, msg_len);
552 if (printMessages) { print("first message: $msg"); }
553 vmData = vmData.substring(msg_len);
554 if (haveGarbageVmData()) {
555 print("Error: garbage data after previous message: '$vmData'");
556 print("Previous message was: '$msg'");
557 return;
558 }
559 processVmMessage(msg);
560 msg_len = jsonObjectLength(vmData);
561 }
562 if (printMessages) { print("leftover vm data '$vmData'"); }
563 }
564
565 /**
566 * Skip past a JSON object value.
567 * The object value must start with '{' and continues to the
568 * matching '}'. No attempt is made to otherwise validate the contents
569 * as JSON. If it is invalid, a later [parseJson] will fail.
570 */
571 int jsonObjectLength(String string) {
572 int skipWhitespace(int index) {
573 while (index < string.length) {
574 String char = string[index];
575 if (char != " " && char != "\n" && char != "\r" && char != "\t") break;
576 index++;
577 }
578 return index;
579 }
580 int skipString(int index) {
581 assert(string[index - 1] == '"');
582 while (index < string.length) {
583 String char = string[index];
584 if (char == '"') return index + 1;
585 if (char == r'\') index++;
586 if (index == string.length) return index;
587 index++;
588 }
589 return index;
590 }
591 int index = 0;
592 index = skipWhitespace(index);
593 // Bail out if the first non-whitespace character isn't '{'.
594 if (index == string.length || string[index] != '{') return 0;
595 int nesting = 0;
596 while (index < string.length) {
597 String char = string[index++];
598 if (char == '{') {
599 nesting++;
600 } else if (char == '}') {
601 nesting--;
602 if (nesting == 0) return index;
603 } else if (char == '"') {
604 // Strings can contain braces. Skip their content.
605 index = skipString(index);
606 }
607 }
608 return 0;
609 }
610
611
612 void debuggerMain() {
613 outstandingCommands = new Map<int, Completer>();
614 Socket.connect("127.0.0.1", 5858).then((s) {
615 vmSock = s;
616 vmSock.setOption(SocketOption.TCP_NODELAY, true);
617 var stringStream = vmSock.transform(UTF8.decoder);
618 vmSubscription = stringStream.listen(
619 (String data) {
620 processVmData(data);
621 },
622 onDone: () {
623 print("VM debugger connection closed");
624 quitShell();
625 },
626 onError: (err) {
627 print("Error in debug connection: $err");
628 // TODO(floitsch): do we want to print the stack trace?
629 quitShell();
630 });
631 stdinSubscription = stdin.transform(UTF8.decoder)
632 .transform(new LineSplitter())
633 .listen((String line) => processCommand(line));
634 });
635 }
636
637 void main(List<String> arguments) {
638 if (arguments.length > 0) {
639 arguments = <String>['--debug', '--verbose_debug']..addAll(arguments);
640 Process.start(Platform.executable, arguments).then((Process process) {
641 process.stdin.close();
642 process.exitCode.then((int exitCode) {
643 print('${arguments.join(" ")} exited with $exitCode');
644 });
645 debuggerMain();
646 });
647 } else {
648 debuggerMain();
649 }
650 }
OLDNEW
« no previous file with comments | « no previous file | tools/ddbg/bin/ddbg.dart » ('j') | tools/ddbg/bin/ddbg.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698