Index: tools/ddbg.dart |
=================================================================== |
--- tools/ddbg.dart (revision 0) |
+++ tools/ddbg.dart (revision 0) |
@@ -0,0 +1,195 @@ |
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+// Simple interactive debugger shell that connects to the Dart VM's debugger |
+// connection port. |
+ |
+#import("dart:io"); |
+#import("dart:json"); |
+ |
+Map<int, Completer> outstandingCommands; |
+ |
+Socket vmSock; |
+OutputStream vmStream; |
+int seqNum = 0; |
+ |
+bool verbose = false; |
+ |
+ |
+void printHelp() { |
+ print(""" |
+ q Quit debugger shell |
+ r Resume execution |
+ s Single step |
+ so Step over |
+ si Step into |
+ ll List loaded libraries |
+ ls <libname> List loaded scripts in library |
+"""); |
+} |
+ |
+ |
+void quitShell() { |
+ vmStream.close(); |
+ vmSock.close(); |
+ stdin.close(); |
+} |
+ |
+ |
+Future sendCmd(Map<String, Dynamic> cmd) { |
+ var completer = new Completer(); |
+ assert(cmd["id"] != null); |
+ var id = cmd["id"]; |
+ outstandingCommands[id] = completer; |
+ if (verbose) { |
+ print("sending: '${JSON.stringify(cmd)}'"); |
+ } |
+ vmStream.writeString(JSON.stringify(cmd)); |
+ return completer.future; |
+} |
+ |
+ |
+void processCommand(String cmdLine) { |
+ seqNum++; |
+ var args = cmdLine.split(' '); |
+ if (args.length == 0) { |
+ return; |
+ } |
+ var cmd = args[0]; |
+ if (cmd == "r") { |
+ var cmd = { "id": seqNum, "command": "resume" }; |
+ sendCmd(cmd).then((result) => handleGenericResponse(result)); |
+ } else if (cmd == "s") { |
+ var cmd = { "id": seqNum, "command": "stepOver" }; |
+ sendCmd(cmd).then((result) => handleGenericResponse(result)); |
+ } else if (cmd == "si") { |
+ var cmd = { "id": seqNum, "command": "stepInto" }; |
+ sendCmd(cmd).then((result) => handleGenericResponse(result)); |
+ } else if (cmd == "so") { |
+ var cmd = { "id": seqNum, "command": "stepOut" }; |
+ sendCmd(cmd).then((result) => handleGenericResponse(result)); |
+ } else if (cmd == "ll") { |
+ var cmd = { "id": seqNum, "command": "getLibraryURLs" }; |
+ sendCmd(cmd).then((result) => handleGetLibraryResponse(result)); |
+ } else if (cmd == "ls") { |
+ if (args.length < 2) { |
+ return; |
+ } |
+ var cmd = { "id": seqNum, "command": "getScriptURLs", |
+ "param": { "library": args[1] }}; |
+ sendCmd(cmd).then((result) => handleGetScriptsResponse(result)); |
+ } else if (cmd == "q") { |
+ quitShell(); |
+ } else if (cmd == "h") { |
+ printHelp(); |
+ } else { |
+ print("Error: $cmd not understood"); |
+ } |
+} |
+ |
+ |
+void handleGetLibraryResponse(response) { |
+ var result = response["result"]; |
+ assert(result != null); |
+ var urls = result["urls"]; |
+ assert(urls != null); |
+ assert(urls is List); |
+ print("Loaded isolates:"); |
+ for (int i = 0; i < urls.length; i++) { |
+ print(" $i ${urls[i]}"); |
+ } |
+} |
+ |
+ |
+void handleGetScriptsResponse(response) { |
+ var result = response["result"]; |
+ assert(result != null); |
+ var urls = result["urls"]; |
+ assert(urls != null); |
+ assert(urls is List); |
+ print("Loaded scripts:"); |
+ for (int i = 0; i < urls.length; i++) { |
+ print(" $i ${urls[i]}"); |
+ } |
+} |
+ |
+ |
+void handleGenericResponse(response) { |
+ if (response["error"] != null) { |
+ print("Error: ${response["error"]}"); |
+ } |
+} |
+ |
+ |
+void printStackTrace(var trace) { |
+ if (trace == null) { |
+ return; |
+ } |
+ var frames = trace["callFrames"]; |
+ if (frames is !List) { |
+ print("unexpected type for frames parameter $frames"); |
+ return; |
+ } |
+ for (int i = 0; i < frames.length; i++) { |
+ var frame = frames[i]; |
+ var fname = frame["functionName"]; |
+ var url = frame["location"]["url"]; |
+ var line = frame["location"]["lineNumber"]; |
+ print("$i $fname ($url:$line)"); |
+ } |
+} |
+ |
+ |
+// TODO(hausner): Need to handle the case where we receive only a partial |
+// message from the debugger, e.g. when the message is too big to fit in |
+// one network packet. |
+void processVmData(String s) { |
+ if (verbose) print("vm: $s"); |
+ var msg = JSON.parse(s); |
+ if (msg == null) { |
+ return; |
+ } |
+ var event = msg["event"]; |
+ if (event == "paused") { |
+ print("VM paused, stack trace:"); |
+ printStackTrace(msg["params"]); |
+ return; |
+ } |
+ if (msg["id"] != null) { |
+ var id = msg["id"]; |
+ if (outstandingCommands.containsKey(id)) { |
+ if (msg["error"] != null) { |
+ print("VM says: ${msg["error"]}"); |
+ } else { |
+ var completer = outstandingCommands[id]; |
+ completer.complete(msg); |
+ } |
+ outstandingCommands.remove(id); |
+ } |
+ } |
+} |
+ |
+ |
+void main() { |
+ outstandingCommands = new Map<int, Completer>(); |
+ vmSock = new Socket("127.0.0.1", 5858); |
+ vmStream = new SocketOutputStream(vmSock); |
+ var stdinStream = new StringInputStream(stdin); |
+ stdinStream.onLine = () { |
+ processCommand(stdinStream.readLine()); |
+ }; |
+ var vmInStream = new SocketInputStream(vmSock); |
+ vmInStream.onData = () { |
+ var s = new String.fromCharCodes(vmInStream.read()); |
+ processVmData(s); |
+ }; |
+ vmInStream.onError = (err) { |
+ print("Error in debug connection: $err"); |
+ quitShell(); |
+ }; |
+ vmInStream.onClosed = () { |
+ print("VM debugger connection closed"); |
+ quitShell(); |
+ }; |
+} |