| Index: tools/ddbg.dart
|
| ===================================================================
|
| --- tools/ddbg.dart (revision 30481)
|
| +++ tools/ddbg.dart (working copy)
|
| @@ -1,650 +0,0 @@
|
| -// 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:convert";
|
| -import "dart:io";
|
| -import "dart:async";
|
| -
|
| -
|
| -Map<int, Completer> outstandingCommands;
|
| -
|
| -Socket vmSock;
|
| -String vmData;
|
| -var stdinSubscription;
|
| -var vmSubscription;
|
| -int seqNum = 0;
|
| -int isolate_id = -1;
|
| -
|
| -final verbose = false;
|
| -final printMessages = false;
|
| -
|
| -// The location of the last paused event.
|
| -Map pausedLocation = null;
|
| -
|
| -
|
| -void printHelp() {
|
| - print("""
|
| - q Quit debugger shell
|
| - bt Show backtrace
|
| - r Resume execution
|
| - s Single step
|
| - so Step over
|
| - si Step into
|
| - sbp [<file>] <line> Set breakpoint
|
| - rbp <id> Remove breakpoint with given id
|
| - po <id> Print object info for given id
|
| - eval obj <id> <expr> Evaluate expr on object id
|
| - eval cls <id> <expr> Evaluate expr on class id
|
| - eval lib <id> <expr> Evaluate expr in toplevel of library id
|
| - pl <id> <idx> [<len>] Print list element/slice
|
| - pc <id> Print class info for given id
|
| - ll List loaded libraries
|
| - plib <id> Print library info for given library id
|
| - slib <id> <true|false> Set library id debuggable
|
| - pg <id> Print all global variables visible within given library id
|
| - ls <lib_id> List loaded scripts in library
|
| - gs <lib_id> <script_url> Get source text of script in library
|
| - tok <lib_id> <script_url> Get line and token table of script in library
|
| - epi <none|all|unhandled> Set exception pause info
|
| - li List ids of all isolates in the VM
|
| - i <id> Interrupt execution of given isolate id
|
| - h Print help
|
| -""");
|
| -}
|
| -
|
| -
|
| -void quitShell() {
|
| - vmSubscription.cancel();
|
| - vmSock.close();
|
| - stdinSubscription.cancel();
|
| -}
|
| -
|
| -
|
| -Future sendCmd(Map<String, dynamic> cmd) {
|
| - var completer = new Completer();
|
| - int id = cmd["id"];
|
| - outstandingCommands[id] = completer;
|
| - if (verbose) {
|
| - print("sending: '${JSON.encode(cmd)}'");
|
| - }
|
| - vmSock.write(JSON.encode(cmd));
|
| - return completer.future;
|
| -}
|
| -
|
| -void processCommand(String cmdLine) {
|
| -
|
| - void huh() {
|
| - print("'$cmdLine' not understood, try h for help");
|
| - }
|
| -
|
| - seqNum++;
|
| - var args = cmdLine.split(' ');
|
| - if (args.length == 0) {
|
| - return;
|
| - }
|
| - var command = args[0];
|
| - var simple_commands =
|
| - { 'r':'resume', 's':'stepOver', 'si':'stepInto', 'so':'stepOut'};
|
| - if (simple_commands[command] != null) {
|
| - var cmd = { "id": seqNum,
|
| - "command": simple_commands[command],
|
| - "params": { "isolateId" : isolate_id } };
|
| - sendCmd(cmd).then((result) => handleGenericResponse(result));
|
| - } else if (command == "bt") {
|
| - var cmd = { "id": seqNum,
|
| - "command": "getStackTrace",
|
| - "params": { "isolateId" : isolate_id } };
|
| - sendCmd(cmd).then((result) => handleStackTraceResponse(result));
|
| - } else if (command == "ll") {
|
| - var cmd = { "id": seqNum,
|
| - "command": "getLibraries",
|
| - "params": { "isolateId" : isolate_id } };
|
| - sendCmd(cmd).then((result) => handleGetLibraryResponse(result));
|
| - } else if (command == "sbp" && args.length >= 2) {
|
| - var url, line;
|
| - if (args.length == 2 && pausedLocation != null) {
|
| - url = pausedLocation["url"];
|
| - assert(url != null);
|
| - line = int.parse(args[1]);
|
| - } else {
|
| - url = args[1];
|
| - line = int.parse(args[2]);
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "setBreakpoint",
|
| - "params": { "isolateId" : isolate_id,
|
| - "url": url,
|
| - "line": line }};
|
| - sendCmd(cmd).then((result) => handleSetBpResponse(result));
|
| - } else if (command == "rbp" && args.length == 2) {
|
| - var cmd = { "id": seqNum,
|
| - "command": "removeBreakpoint",
|
| - "params": { "isolateId" : isolate_id,
|
| - "breakpointId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then((result) => handleGenericResponse(result));
|
| - } else if (command == "ls" && args.length == 2) {
|
| - var cmd = { "id": seqNum,
|
| - "command": "getScriptURLs",
|
| - "params": { "isolateId" : isolate_id,
|
| - "libraryId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then((result) => handleGetScriptsResponse(result));
|
| - } else if (command == "eval" && args.length > 3) {
|
| - var expr = args.getRange(3, args.length).join(" ");
|
| - var target = args[1];
|
| - if (target == "obj") {
|
| - target = "objectId";
|
| - } else if (target == "cls") {
|
| - target = "classId";
|
| - } else if (target == "lib") {
|
| - target = "libraryId";
|
| - } else {
|
| - huh();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "evaluateExpr",
|
| - "params": { "isolateId": isolate_id,
|
| - target: int.parse(args[2]),
|
| - "expression": expr } };
|
| - sendCmd(cmd).then((result) => handleEvalResponse(result));
|
| - } else if (command == "po" && args.length == 2) {
|
| - var cmd = { "id": seqNum,
|
| - "command": "getObjectProperties",
|
| - "params": { "isolateId" : isolate_id,
|
| - "objectId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then((result) => handleGetObjPropsResponse(result));
|
| - } else if (command == "pl" && args.length >= 3) {
|
| - var cmd;
|
| - if (args.length == 3) {
|
| - cmd = { "id": seqNum,
|
| - "command": "getListElements",
|
| - "params": { "isolateId" : isolate_id,
|
| - "objectId": int.parse(args[1]),
|
| - "index": int.parse(args[2]) } };
|
| - } else {
|
| - cmd = { "id": seqNum,
|
| - "command": "getListElements",
|
| - "params": { "isolateId" : isolate_id,
|
| - "objectId": int.parse(args[1]),
|
| - "index": int.parse(args[2]),
|
| - "length": int.parse(args[3]) } };
|
| - }
|
| - sendCmd(cmd).then((result) => handleGetListResponse(result));
|
| - } else if (command == "pc" && args.length == 2) {
|
| - var cmd = { "id": seqNum,
|
| - "command": "getClassProperties",
|
| - "params": { "isolateId" : isolate_id,
|
| - "classId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then((result) => handleGetClassPropsResponse(result));
|
| - } else if (command == "plib" && args.length == 2) {
|
| - var cmd = { "id": seqNum,
|
| - "command": "getLibraryProperties",
|
| - "params": {"isolateId" : isolate_id,
|
| - "libraryId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then((result) => handleGetLibraryPropsResponse(result));
|
| - } else if (command == "slib" && args.length == 3) {
|
| - var cmd = { "id": seqNum,
|
| - "command": "setLibraryProperties",
|
| - "params": {"isolateId" : isolate_id,
|
| - "libraryId": int.parse(args[1]),
|
| - "debuggingEnabled": args[2] } };
|
| - sendCmd(cmd).then((result) => handleSetLibraryPropsResponse(result));
|
| - } else if (command == "pg" && args.length == 2) {
|
| - var cmd = { "id": seqNum,
|
| - "command": "getGlobalVariables",
|
| - "params": { "isolateId" : isolate_id,
|
| - "libraryId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then((result) => handleGetGlobalVarsResponse(result));
|
| - } else if (command == "gs" && args.length == 3) {
|
| - var cmd = { "id": seqNum,
|
| - "command": "getScriptSource",
|
| - "params": { "isolateId" : isolate_id,
|
| - "libraryId": int.parse(args[1]),
|
| - "url": args[2] } };
|
| - sendCmd(cmd).then((result) => handleGetSourceResponse(result));
|
| - } else if (command == "tok" && args.length == 3) {
|
| - var cmd = { "id": seqNum,
|
| - "command": "getLineNumberTable",
|
| - "params": { "isolateId" : isolate_id,
|
| - "libraryId": int.parse(args[1]),
|
| - "url": args[2] } };
|
| - sendCmd(cmd).then((result) => handleGetLineTableResponse(result));
|
| - } else if (command == "epi" && args.length == 2) {
|
| - var cmd = { "id": seqNum,
|
| - "command": "setPauseOnException",
|
| - "params": { "isolateId" : isolate_id,
|
| - "exceptions": args[1] } };
|
| - sendCmd(cmd).then((result) => handleGenericResponse(result));
|
| - } else if (command == "li") {
|
| - var cmd = { "id": seqNum, "command": "getIsolateIds" };
|
| - sendCmd(cmd).then((result) => handleGetIsolatesResponse(result));
|
| - } else if (command == "i" && args.length == 2) {
|
| - var cmd = { "id": seqNum,
|
| - "command": "interrupt",
|
| - "params": { "isolateId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then((result) => handleGenericResponse(result));
|
| - } else if (command == "q") {
|
| - quitShell();
|
| - } else if (command == "h") {
|
| - printHelp();
|
| - } else {
|
| - huh();
|
| - }
|
| -}
|
| -
|
| -
|
| -String remoteObject(value) {
|
| - var kind = value["kind"];
|
| - var text = value["text"];
|
| - var id = value["objectId"];
|
| - if (kind == "string") {
|
| - return "(string, id $id) '$text'";
|
| - } else if (kind == "list") {
|
| - var len = value["length"];
|
| - return "(list, id $id, len $len) $text";
|
| - } else if (kind == "object") {
|
| - return "(obj, id $id) $text";
|
| - } else if (kind == "function") {
|
| - var location = value['location'] != null
|
| - ? ", file '${value['location']['url']}'"
|
| - ", token pos ${value['location']['tokenOffset']}"
|
| - : "";
|
| - var name = value['name'];
|
| - var signature = value['signature'];
|
| - return "(closure ${name}${signature} $location)";
|
| - } else {
|
| - return "$text";
|
| - }
|
| -}
|
| -
|
| -
|
| -printNamedObject(obj) {
|
| - var name = obj["name"];
|
| - var value = obj["value"];
|
| - print(" $name = ${remoteObject(value)}");
|
| -}
|
| -
|
| -
|
| -handleGetObjPropsResponse(response) {
|
| - Map props = response["result"];
|
| - int class_id = props["classId"];
|
| - if (class_id == -1) {
|
| - print(" null");
|
| - return;
|
| - }
|
| - List fields = props["fields"];
|
| - print(" class id: $class_id");
|
| - for (int i = 0; i < fields.length; i++) {
|
| - printNamedObject(fields[i]);
|
| - }
|
| -}
|
| -
|
| -handleGetListResponse(response) {
|
| - Map result = response["result"];
|
| - if (result["elements"] != null) {
|
| - // List slice.
|
| - var index = result["index"];
|
| - var length = result["length"];
|
| - List elements = result["elements"];
|
| - assert(length == elements.length);
|
| - for (int i = 0; i < length; i++) {
|
| - var kind = elements[i]["kind"];
|
| - var text = elements[i]["text"];
|
| - print(" ${index + i}: ($kind) $text");
|
| - }
|
| - } else {
|
| - // One element, a remote object.
|
| - print(result);
|
| - print(" ${remoteObject(result)}");
|
| - }
|
| -}
|
| -
|
| -
|
| -handleGetClassPropsResponse(response) {
|
| - Map props = response["result"];
|
| - assert(props["name"] != null);
|
| - int libId = props["libraryId"];
|
| - assert(libId != null);
|
| - print(" class ${props["name"]} (library id: $libId)");
|
| - List fields = props["fields"];
|
| - if (fields.length > 0) {
|
| - print(" static fields:");
|
| - for (int i = 0; i < fields.length; i++) {
|
| - printNamedObject(fields[i]);
|
| - }
|
| - }
|
| -}
|
| -
|
| -
|
| -handleGetLibraryPropsResponse(response) {
|
| - Map props = response["result"];
|
| - assert(props["url"] != null);
|
| - print(" library url: ${props["url"]}");
|
| - assert(props["debuggingEnabled"] != null);
|
| - print(" debugging enabled: ${props["debuggingEnabled"]}");
|
| - List imports = props["imports"];
|
| - assert(imports != null);
|
| - if (imports.length > 0) {
|
| - print(" imports:");
|
| - for (int i = 0; i < imports.length; i++) {
|
| - print(" id ${imports[i]["libraryId"]} prefix ${imports[i]["prefix"]}");
|
| - }
|
| - }
|
| - List globals = props["globals"];
|
| - assert(globals != null);
|
| - if (globals.length > 0) {
|
| - print(" global variables:");
|
| - for (int i = 0; i < globals.length; i++) {
|
| - printNamedObject(globals[i]);
|
| - }
|
| - }
|
| -}
|
| -
|
| -
|
| -handleSetLibraryPropsResponse(response) {
|
| - Map props = response["result"];
|
| - assert(props["debuggingEnabled"] != null);
|
| - print(" debugging enabled: ${props["debuggingEnabled"]}");
|
| -}
|
| -
|
| -
|
| -handleGetGlobalVarsResponse(response) {
|
| - List globals = response["result"]["globals"];
|
| - for (int i = 0; i < globals.length; i++) {
|
| - printNamedObject(globals[i]);
|
| - }
|
| -}
|
| -
|
| -
|
| -handleGetSourceResponse(response) {
|
| - Map result = response["result"];
|
| - String source = result["text"];
|
| - print("Source text:\n$source\n--------");
|
| -}
|
| -
|
| -
|
| -handleGetLineTableResponse(response) {
|
| - Map result = response["result"];
|
| - var info = result["lines"];
|
| - print("Line info table:\n$info");
|
| -}
|
| -
|
| -
|
| -handleGetIsolatesResponse(response) {
|
| - Map result = response["result"];
|
| - print("Isolates: ${result["isolateIds"]}");
|
| -}
|
| -
|
| -
|
| -void handleGetLibraryResponse(response) {
|
| - Map result = response["result"];
|
| - List libs = result["libraries"];
|
| - print("Loaded libraries:");
|
| - print(libs);
|
| - for (int i = 0; i < libs.length; i++) {
|
| - print(" ${libs[i]["id"]} ${libs[i]["url"]}");
|
| - }
|
| -}
|
| -
|
| -
|
| -void handleGetScriptsResponse(response) {
|
| - Map result = response["result"];
|
| - List urls = result["urls"];
|
| - print("Loaded scripts:");
|
| - for (int i = 0; i < urls.length; i++) {
|
| - print(" $i ${urls[i]}");
|
| - }
|
| -}
|
| -
|
| -
|
| -void handleEvalResponse(response) {
|
| - Map result = response["result"];
|
| - print(remoteObject(result));
|
| -}
|
| -
|
| -
|
| -void handleSetBpResponse(response) {
|
| - Map result = response["result"];
|
| - var id = result["breakpointId"];
|
| - assert(id != null);
|
| - print("Set BP $id");
|
| -}
|
| -
|
| -
|
| -void handleGenericResponse(response) {
|
| - if (response["error"] != null) {
|
| - print("Error: ${response["error"]}");
|
| - }
|
| -}
|
| -
|
| -
|
| -void handleStackTraceResponse(response) {
|
| - Map result = response["result"];
|
| - List callFrames = result["callFrames"];
|
| - assert(callFrames != null);
|
| - printStackTrace(callFrames);
|
| -}
|
| -
|
| -
|
| -void printStackFrame(frame_num, Map frame) {
|
| - var fname = frame["functionName"];
|
| - var libId = frame["location"]["libraryId"];
|
| - var url = frame["location"]["url"];
|
| - var toff = frame["location"]["tokenOffset"];
|
| - print("$frame_num $fname (url: $url token: $toff lib: $libId)");
|
| - List locals = frame["locals"];
|
| - for (int i = 0; i < locals.length; i++) {
|
| - printNamedObject(locals[i]);
|
| - }
|
| -}
|
| -
|
| -
|
| -void printStackTrace(List frames) {
|
| - for (int i = 0; i < frames.length; i++) {
|
| - printStackFrame(i, frames[i]);
|
| - }
|
| -}
|
| -
|
| -
|
| -void handlePausedEvent(msg) {
|
| - assert(msg["params"] != null);
|
| - var reason = msg["params"]["reason"];
|
| - isolate_id = msg["params"]["isolateId"];
|
| - assert(isolate_id != null);
|
| - pausedLocation = msg["params"]["location"];
|
| - assert(pausedLocation != null);
|
| - if (reason == "breakpoint") {
|
| - print("Isolate $isolate_id paused on breakpoint");
|
| - print("location: $pausedLocation");
|
| - } else if (reason == "interrupted") {
|
| - print("Isolate $isolate_id paused due to an interrupt");
|
| - } else {
|
| - assert(reason == "exception");
|
| - var excObj = msg["params"]["exception"];
|
| - print("Isolate $isolate_id paused on exception");
|
| - print(remoteObject(excObj));
|
| - }
|
| -}
|
| -
|
| -
|
| -void processVmMessage(String jsonString) {
|
| - var msg = JSON.decode(jsonString);
|
| - if (msg == null) {
|
| - return;
|
| - }
|
| - var event = msg["event"];
|
| - if (event == "paused") {
|
| - handlePausedEvent(msg);
|
| - return;
|
| - }
|
| - if (event == "breakpointResolved") {
|
| - Map params = msg["params"];
|
| - assert(params != null);
|
| - print("BP ${params["breakpointId"]} resolved and "
|
| - "set at line ${params["line"]}.");
|
| - return;
|
| - }
|
| - if (event == "isolate") {
|
| - Map params = msg["params"];
|
| - assert(params != null);
|
| - print("Isolate ${params["id"]} has been ${params["reason"]}.");
|
| - 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);
|
| - }
|
| - }
|
| -}
|
| -
|
| -bool haveGarbageVmData() {
|
| - if (vmData == null || vmData.length == 0) return false;
|
| - var i = 0, char = " ";
|
| - while (i < vmData.length) {
|
| - char = vmData[i];
|
| - if (char != " " && char != "\n" && char != "\r" && char != "\t") break;
|
| - i++;
|
| - }
|
| - if (i >= vmData.length) {
|
| - return false;
|
| - } else {
|
| - return char != "{";
|
| - }
|
| -}
|
| -
|
| -
|
| -void processVmData(String data) {
|
| - if (vmData == null || vmData.length == 0) {
|
| - vmData = data;
|
| - } else {
|
| - vmData = vmData + data;
|
| - }
|
| - if (haveGarbageVmData()) {
|
| - print("Error: have garbage data from VM: '$vmData'");
|
| - return;
|
| - }
|
| - int msg_len = jsonObjectLength(vmData);
|
| - if (printMessages && msg_len == 0) {
|
| - print("have partial or illegal json message"
|
| - " of ${vmData.length} chars:\n'$vmData'");
|
| - return;
|
| - }
|
| - while (msg_len > 0 && msg_len <= vmData.length) {
|
| - if (msg_len == vmData.length) {
|
| - if (printMessages) { print("have one full message:\n$vmData"); }
|
| - processVmMessage(vmData);
|
| - vmData = null;
|
| - return;
|
| - }
|
| - if (printMessages) { print("at least one message: '$vmData'"); }
|
| - var msg = vmData.substring(0, msg_len);
|
| - if (printMessages) { print("first message: $msg"); }
|
| - vmData = vmData.substring(msg_len);
|
| - if (haveGarbageVmData()) {
|
| - print("Error: garbage data after previous message: '$vmData'");
|
| - print("Previous message was: '$msg'");
|
| - return;
|
| - }
|
| - processVmMessage(msg);
|
| - msg_len = jsonObjectLength(vmData);
|
| - }
|
| - if (printMessages) { print("leftover vm data '$vmData'"); }
|
| -}
|
| -
|
| -/**
|
| - * Skip past a JSON object value.
|
| - * The object value must start with '{' and continues to the
|
| - * matching '}'. No attempt is made to otherwise validate the contents
|
| - * as JSON. If it is invalid, a later [parseJson] will fail.
|
| - */
|
| -int jsonObjectLength(String string) {
|
| - int skipWhitespace(int index) {
|
| - while (index < string.length) {
|
| - String char = string[index];
|
| - if (char != " " && char != "\n" && char != "\r" && char != "\t") break;
|
| - index++;
|
| - }
|
| - return index;
|
| - }
|
| - int skipString(int index) {
|
| - assert(string[index - 1] == '"');
|
| - while (index < string.length) {
|
| - String char = string[index];
|
| - if (char == '"') return index + 1;
|
| - if (char == r'\') index++;
|
| - if (index == string.length) return index;
|
| - index++;
|
| - }
|
| - return index;
|
| - }
|
| - int index = 0;
|
| - index = skipWhitespace(index);
|
| - // Bail out if the first non-whitespace character isn't '{'.
|
| - if (index == string.length || string[index] != '{') return 0;
|
| - int nesting = 0;
|
| - while (index < string.length) {
|
| - String char = string[index++];
|
| - if (char == '{') {
|
| - nesting++;
|
| - } else if (char == '}') {
|
| - nesting--;
|
| - if (nesting == 0) return index;
|
| - } else if (char == '"') {
|
| - // Strings can contain braces. Skip their content.
|
| - index = skipString(index);
|
| - }
|
| - }
|
| - return 0;
|
| -}
|
| -
|
| -
|
| -void debuggerMain() {
|
| - outstandingCommands = new Map<int, Completer>();
|
| - Socket.connect("127.0.0.1", 5858).then((s) {
|
| - vmSock = s;
|
| - vmSock.setOption(SocketOption.TCP_NODELAY, true);
|
| - var stringStream = vmSock.transform(UTF8.decoder);
|
| - vmSubscription = stringStream.listen(
|
| - (String data) {
|
| - processVmData(data);
|
| - },
|
| - onDone: () {
|
| - print("VM debugger connection closed");
|
| - quitShell();
|
| - },
|
| - onError: (err) {
|
| - print("Error in debug connection: $err");
|
| - // TODO(floitsch): do we want to print the stack trace?
|
| - quitShell();
|
| - });
|
| - stdinSubscription = stdin.transform(UTF8.decoder)
|
| - .transform(new LineSplitter())
|
| - .listen((String line) => processCommand(line));
|
| - });
|
| -}
|
| -
|
| -void main(List<String> arguments) {
|
| - if (arguments.length > 0) {
|
| - arguments = <String>['--debug', '--verbose_debug']..addAll(arguments);
|
| - Process.start(Platform.executable, arguments).then((Process process) {
|
| - process.stdin.close();
|
| - process.exitCode.then((int exitCode) {
|
| - print('${arguments.join(" ")} exited with $exitCode');
|
| - });
|
| - debuggerMain();
|
| - });
|
| - } else {
|
| - debuggerMain();
|
| - }
|
| -}
|
|
|