| Index: tools/ddbg.dart
|
| diff --git a/tools/ddbg.dart b/tools/ddbg.dart
|
| deleted file mode 100644
|
| index f14a0170a93f82bf13f71bdfb1c1c99a6211697c..0000000000000000000000000000000000000000
|
| --- a/tools/ddbg.dart
|
| +++ /dev/null
|
| @@ -1,1496 +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";
|
| -import "dart:math";
|
| -
|
| -import "ddbg/lib/commando.dart";
|
| -
|
| -class TargetScript {
|
| - // The text of a script.
|
| - String source = null;
|
| -
|
| - // A mapping from line number to source text.
|
| - List<String> lineToSource = null;
|
| -
|
| - // A mapping from token offset to line number.
|
| - Map<int,int> tokenToLine = null;
|
| -}
|
| -
|
| -const UnknownLocation = const {};
|
| -
|
| -class TargetIsolate {
|
| - int id;
|
| - // The location of the last paused event.
|
| - Map pausedLocation = null;
|
| -
|
| - TargetIsolate(this.id);
|
| - bool get isPaused => pausedLocation != null;
|
| - String get pausedUrl => pausedLocation != null ? pausedLocation["url"] : null;
|
| -
|
| - Map<String, TargetScript> scripts = {};
|
| -}
|
| -
|
| -Map<int, TargetIsolate> targetIsolates= new Map<int, TargetIsolate>();
|
| -
|
| -Map<int, Completer> outstandingCommands;
|
| -
|
| -Socket vmSock;
|
| -String vmData;
|
| -var cmdSubscription;
|
| -Commando cmdo;
|
| -var vmSubscription;
|
| -int seqNum = 0;
|
| -
|
| -bool isDebugging = false;
|
| -Process targetProcess = null;
|
| -bool suppressNextExitCode = false;
|
| -
|
| -final verbose = false;
|
| -final printMessages = false;
|
| -
|
| -TargetIsolate currentIsolate;
|
| -TargetIsolate mainIsolate;
|
| -
|
| -int debugPort = 5858;
|
| -
|
| -String formatLocation(Map location) {
|
| - if (location == null) return "";
|
| - var fileName = location["url"].split("/").last;
|
| - return "file: $fileName lib: ${location['libraryId']} token: ${location['tokenOffset']}";
|
| -}
|
| -
|
| -
|
| -Future sendCmd(Map<String, dynamic> cmd) {
|
| - var completer = new Completer.sync();
|
| - int id = cmd["id"];
|
| - outstandingCommands[id] = completer;
|
| - if (verbose) {
|
| - print("sending: '${JSON.encode(cmd)}'");
|
| - }
|
| - vmSock.write(JSON.encode(cmd));
|
| - return completer.future;
|
| -}
|
| -
|
| -
|
| -bool checkCurrentIsolate() {
|
| - if (vmSock == null) {
|
| - print("There is no active script. Try 'help run'.");
|
| - return false;
|
| - }
|
| - if (currentIsolate == null) {
|
| - print('There is no current isolate.');
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -
|
| -void setCurrentIsolate(TargetIsolate isolate) {
|
| - if (isolate != currentIsolate) {
|
| - currentIsolate = isolate;
|
| - if (mainIsolate == null) {
|
| - print("Main isolate is ${isolate.id}");
|
| - mainIsolate = isolate;
|
| - }
|
| - print("Current isolate is now ${isolate.id}");
|
| - }
|
| -}
|
| -
|
| -
|
| -bool checkPaused() {
|
| - if (!checkCurrentIsolate()) return false;
|
| - if (currentIsolate.isPaused) return true;
|
| - print("Current isolate must be paused");
|
| - return false;
|
| -}
|
| -
|
| -// These settings are allowed in the 'set' and 'show' debugger commands.
|
| -var validSettings = ['vm', 'vmargs', 'script', 'args'];
|
| -
|
| -// The current values for all settings.
|
| -var settings = new Map();
|
| -
|
| -String _leftJustify(text, int width) {
|
| - StringBuffer buffer = new StringBuffer();
|
| - buffer.write(text);
|
| - while (buffer.length < width) {
|
| - buffer.write(' ');
|
| - }
|
| - return buffer.toString();
|
| -}
|
| -
|
| -// TODO(turnidge): Move all commands here.
|
| -List<Command> commandList =
|
| - [ new HelpCommand(),
|
| - new QuitCommand(),
|
| - new RunCommand(),
|
| - new KillCommand(),
|
| - new ConnectCommand(),
|
| - new DisconnectCommand(),
|
| - new SetCommand(),
|
| - new ShowCommand() ];
|
| -
|
| -
|
| -List<Command> matchCommand(String commandName, bool exactMatchWins) {
|
| - List matches = [];
|
| - for (var command in commandList) {
|
| - if (command.name.startsWith(commandName)) {
|
| - if (exactMatchWins && command.name == commandName) {
|
| - // Exact match
|
| - return [command];
|
| - } else {
|
| - matches.add(command);
|
| - }
|
| - }
|
| - }
|
| - return matches;
|
| -}
|
| -
|
| -abstract class Command {
|
| - String get name;
|
| - Future run(List<String> args);
|
| -}
|
| -
|
| -class HelpCommand extends Command {
|
| - final name = 'help';
|
| - final helpShort = 'Show a list of debugger commands';
|
| - final helpLong ="""
|
| -Show a list of debugger commands or get more information about a
|
| -particular command.
|
| -
|
| -Usage:
|
| - help
|
| - help <command>
|
| -""";
|
| -
|
| - Future run(List<String> args) {
|
| - if (args.length == 1) {
|
| - print("Debugger commands:\n");
|
| - for (var command in commandList) {
|
| - print(' ${_leftJustify(command.name, 11)} ${command.helpShort}');
|
| - }
|
| -
|
| - // TODO(turnidge): Convert all commands to use the Command class.
|
| - print("""
|
| - 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 fr <n> <expr> Evaluate expr on stack frame index n
|
| - 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
|
| - sci <id> Set current target isolate
|
| - i <id> Interrupt execution of given isolate id
|
| -""");
|
| -
|
| - print("For more information about a particular command, type:\n\n"
|
| - " help <command>\n");
|
| -
|
| - print("Commands may be abbreviated: e.g. type 'h' for 'help.\n");
|
| - } else if (args.length == 2) {
|
| - var commandName = args[1];
|
| - var matches = matchCommand(commandName, true);
|
| - if (matches.length == 0) {
|
| - print("Command '$commandName' not recognized. "
|
| - "Try 'help' for a list of commands.");
|
| - } else {
|
| - for (var command in matches) {
|
| - print("---- ${command.name} ----\n${command.helpLong}");
|
| - }
|
| - }
|
| - } else {
|
| - print("Command '$command' not recognized. "
|
| - "Try 'help' for a list of commands.");
|
| - }
|
| -
|
| - return new Future.value();
|
| - }
|
| -}
|
| -
|
| -
|
| -class QuitCommand extends Command {
|
| - final name = 'quit';
|
| - final helpShort = 'Quit the debugger.';
|
| - final helpLong ="""
|
| -Quit the debugger.
|
| -
|
| -Usage:
|
| - quit
|
| -""";
|
| -
|
| - Future run(List<String> args) {
|
| - if (args.length > 1) {
|
| - print("Unexpected arguments to $name command.");
|
| - return new Future.value();
|
| - }
|
| - return debuggerQuit();
|
| - }
|
| -}
|
| -
|
| -class SetCommand extends Command {
|
| - final name = 'set';
|
| - final helpShort = 'Change the value of a debugger setting.';
|
| - final helpLong ="""
|
| -Change the value of a debugger setting.
|
| -
|
| -Usage:
|
| - set <setting> <value>
|
| -
|
| -Valid settings are:
|
| - ${validSettings.join('\n ')}.
|
| -
|
| -See also 'help show'.
|
| -""";
|
| -
|
| - Future run(List<String> args) {
|
| - if (args.length < 3 || !validSettings.contains(args[1])) {
|
| - print("Undefined $name command. Try 'help $name'.");
|
| - return new Future.value();
|
| - }
|
| - var option = args[1];
|
| - var value = args.getRange(2, args.length).join(' ');
|
| - settings[option] = value;
|
| - return new Future.value();
|
| - }
|
| -}
|
| -
|
| -class ShowCommand extends Command {
|
| - final name = 'show';
|
| - final helpShort = 'Show the current value of a debugger setting.';
|
| - final helpLong ="""
|
| -Show the current value of a debugger setting.
|
| -
|
| -Usage:
|
| - show
|
| - show <setting>
|
| -
|
| -If no <setting> is specified, all current settings are shown.
|
| -
|
| -Valid settings are:
|
| - ${validSettings.join('\n ')}.
|
| -
|
| -See also 'help set'.
|
| -""";
|
| -
|
| - Future run(List<String> args) {
|
| - if (args.length == 1) {
|
| - for (var option in validSettings) {
|
| - var value = settings[option];
|
| - print("$option = '$value'");
|
| - }
|
| - } else if (args.length == 2 && validSettings.contains(args[1])) {
|
| - var option = args[1];
|
| - var value = settings[option];
|
| - if (value == null) {
|
| - print('$option has not been set.');
|
| - } else {
|
| - print("$option = '$value'");
|
| - }
|
| - return new Future.value();
|
| - } else {
|
| - print("Undefined $name command. Try 'help $name'.");
|
| - }
|
| - return new Future.value();
|
| - }
|
| -}
|
| -
|
| -class RunCommand extends Command {
|
| - final name = 'run';
|
| - final helpShort = "Run the currrent script.";
|
| - final helpLong ="""
|
| -Runs the current script.
|
| -
|
| -Usage:
|
| - run
|
| - run <args>
|
| -
|
| -The current script will be run on the current vm. The 'vm' and
|
| -'vmargs' settings are used to specify the current vm and vm arguments.
|
| -The 'script' and 'args' settings are used to specify the current
|
| -script and script arguments.
|
| -
|
| -For more information on settings type 'help show' or 'help set'.
|
| -
|
| -If <args> are provided to the run command, it is the same as typing
|
| -'set args <args>' followed by 'run'.
|
| -""";
|
| -
|
| - Future run(List<String> cmdArgs) {
|
| - if (isDebugging) {
|
| - // TODO(turnidge): Implement modal y/n dialog to stop running script.
|
| - print("There is already a running dart process. "
|
| - "Try 'kill'.");
|
| - return new Future.value();
|
| - }
|
| - assert(targetProcess == null);
|
| - if (settings['script'] == null) {
|
| - print("There is no script specified. "
|
| - "Use 'set script' to set the current script.");
|
| - return new Future.value();
|
| - }
|
| - if (cmdArgs.length > 1) {
|
| - settings['args'] = cmdArgs.getRange(1, cmdArgs.length);
|
| - }
|
| -
|
| - // Build the process arguments.
|
| - var processArgs = ['--debug:$debugPort'];
|
| - if (verbose) {
|
| - processArgs.add('--verbose_debug');
|
| - }
|
| - if (settings['vmargs'] != null) {
|
| - processArgs.addAll(settings['vmargs'].split(' '));
|
| - }
|
| - processArgs.add(settings['script']);
|
| - if (settings['args'] != null) {
|
| - processArgs.addAll(settings['args'].split(' '));
|
| - }
|
| - String vm = settings['vm'];
|
| -
|
| - isDebugging = true;
|
| - cmdo.hide();
|
| - return Process.start(vm, processArgs).then((process) {
|
| - print("Started process ${process.pid} '$vm ${processArgs.join(' ')}'");
|
| - targetProcess = process;
|
| - process.stdin.close();
|
| -
|
| - // TODO(turnidge): For now we only show full lines of output
|
| - // from the debugged process. Should show each character.
|
| - process.stdout
|
| - .transform(UTF8.decoder)
|
| - .transform(new LineSplitter())
|
| - .listen((String line) {
|
| - cmdo.hide();
|
| - // TODO(turnidge): Escape output in any way?
|
| - print(line);
|
| - cmdo.show();
|
| - });
|
| -
|
| - process.stderr
|
| - .transform(UTF8.decoder)
|
| - .transform(new LineSplitter())
|
| - .listen((String line) {
|
| - cmdo.hide();
|
| - print(line);
|
| - cmdo.show();
|
| - });
|
| -
|
| - process.exitCode.then((int exitCode) {
|
| - cmdo.hide();
|
| - if (suppressNextExitCode) {
|
| - suppressNextExitCode = false;
|
| - } else {
|
| - if (exitCode == 0) {
|
| - print('Process exited normally.');
|
| - } else {
|
| - print('Process exited with code $exitCode.');
|
| - }
|
| - }
|
| - targetProcess = null;
|
| - cmdo.show();
|
| - });
|
| -
|
| - // Wait for the vm to open the debugging port.
|
| - return openVmSocket(0);
|
| - });
|
| - }
|
| -}
|
| -
|
| -class KillCommand extends Command {
|
| - final name = 'kill';
|
| - final helpShort = 'Kill the currently executing script.';
|
| - final helpLong ="""
|
| -Kill the currently executing script.
|
| -
|
| -Usage:
|
| - kill
|
| -""";
|
| -
|
| - Future run(List<String> cmdArgs) {
|
| - if (!isDebugging) {
|
| - print('There is no running script.');
|
| - return new Future.value();
|
| - }
|
| - if (targetProcess == null) {
|
| - print("The active dart process was not started with 'run'. "
|
| - "Try 'disconnect' instead.");
|
| - return new Future.value();
|
| - }
|
| - assert(targetProcess != null);
|
| - bool result = targetProcess.kill();
|
| - if (result) {
|
| - print('Process killed.');
|
| - suppressNextExitCode = true;
|
| - } else {
|
| - print('Unable to kill process ${targetProcess.pid}');
|
| - }
|
| - return new Future.value();
|
| - }
|
| -}
|
| -
|
| -class ConnectCommand extends Command {
|
| - final name = 'connect';
|
| - final helpShort = "Connect to a running dart script.";
|
| - final helpLong ="""
|
| -Connect to a running dart script.
|
| -
|
| -Usage:
|
| - connect
|
| - connect <port>
|
| -
|
| -The debugger will connect to a dart script which has already been
|
| -started with the --debug option. If no port is provided, the debugger
|
| -will attempt to connect on the default debugger port.
|
| -""";
|
| -
|
| - Future run(List<String> cmdArgs) {
|
| - if (cmdArgs.length > 2) {
|
| - print("Too many arguments to 'connect'.");
|
| - }
|
| - if (isDebugging) {
|
| - // TODO(turnidge): Implement modal y/n dialog to stop running script.
|
| - print("There is already a running dart process. "
|
| - "Try 'kill'.");
|
| - return new Future.value();
|
| - }
|
| - assert(targetProcess == null);
|
| - if (cmdArgs.length == 2) {
|
| - debugPort = int.parse(cmdArgs[1]);
|
| - }
|
| -
|
| - isDebugging = true;
|
| - cmdo.hide();
|
| - return openVmSocket(0);
|
| - }
|
| -}
|
| -
|
| -class DisconnectCommand extends Command {
|
| - final name = 'disconnect';
|
| - final helpShort = "Disconnect from a running dart script.";
|
| - final helpLong ="""
|
| -Disconnect from a running dart script.
|
| -
|
| -Usage:
|
| - disconnect
|
| -
|
| -The debugger will disconnect from a dart script's debugging port. The
|
| -script must have been connected to earlier with the 'connect' command.
|
| -""";
|
| -
|
| - Future run(List<String> cmdArgs) {
|
| - if (cmdArgs.length > 1) {
|
| - print("Too many arguments to 'disconnect'.");
|
| - }
|
| - if (!isDebugging) {
|
| - // TODO(turnidge): Implement modal y/n dialog to stop running script.
|
| - print("There is no active dart process. "
|
| - "Try 'connect'.");
|
| - return new Future.value();
|
| - }
|
| - if (targetProcess != null) {
|
| - print("The active dart process was started with 'run'. "
|
| - "Try 'kill'.");
|
| - }
|
| -
|
| - cmdo.hide();
|
| - return closeVmSocket();
|
| - }
|
| -}
|
| -
|
| -typedef void HandlerType(Map response);
|
| -
|
| -HandlerType showPromptAfter(void handler(Map response)) {
|
| - return (response) {
|
| - handler(response);
|
| - cmdo.show();
|
| - };
|
| -}
|
| -
|
| -void processCommand(String cmdLine) {
|
| - void huh() {
|
| - print("'$cmdLine' not understood, try 'help' for help.");
|
| - }
|
| -
|
| - cmdo.hide();
|
| - seqNum++;
|
| - cmdLine = cmdLine.trim();
|
| - var args = cmdLine.split(' ');
|
| - if (args.length == 0) {
|
| - return;
|
| - }
|
| - var command = args[0];
|
| -
|
| - var resume_commands =
|
| - { 'r':'resume', 's':'stepOver', 'si':'stepInto', 'so':'stepOut'};
|
| - if (resume_commands[command] != null) {
|
| - if (!checkPaused()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": resume_commands[command],
|
| - "params": { "isolateId" : currentIsolate.id } };
|
| - sendCmd(cmd).then(showPromptAfter(handleResumedResponse));
|
| - } else if (command == "bt") {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "getStackTrace",
|
| - "params": { "isolateId" : currentIsolate.id } };
|
| - sendCmd(cmd).then(showPromptAfter(handleStackTraceResponse));
|
| - } else if (command == "ll") {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "getLibraries",
|
| - "params": { "isolateId" : currentIsolate.id } };
|
| - sendCmd(cmd).then(showPromptAfter(handleGetLibraryResponse));
|
| - } else if (command == "sbp" && args.length >= 2) {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var url, line;
|
| - if (args.length == 2 && currentIsolate.pausedUrl != null) {
|
| - url = currentIsolate.pausedUrl;
|
| - line = int.parse(args[1]);
|
| - } else {
|
| - url = args[1];
|
| - line = int.parse(args[2]);
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "setBreakpoint",
|
| - "params": { "isolateId" : currentIsolate.id,
|
| - "url": url,
|
| - "line": line }};
|
| - sendCmd(cmd).then(showPromptAfter(handleSetBpResponse));
|
| - } else if (command == "rbp" && args.length == 2) {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "removeBreakpoint",
|
| - "params": { "isolateId" : currentIsolate.id,
|
| - "breakpointId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then(showPromptAfter(handleGenericResponse));
|
| - } else if (command == "ls" && args.length == 2) {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "getScriptURLs",
|
| - "params": { "isolateId" : currentIsolate.id,
|
| - "libraryId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then(showPromptAfter(handleGetScriptsResponse));
|
| - } else if (command == "eval" && args.length > 3) {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - 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 if (target == "fr") {
|
| - target = "frameId";
|
| - } else {
|
| - huh();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "evaluateExpr",
|
| - "params": { "isolateId": currentIsolate.id,
|
| - target: int.parse(args[2]),
|
| - "expression": expr } };
|
| - sendCmd(cmd).then(showPromptAfter(handleEvalResponse));
|
| - } else if (command == "po" && args.length == 2) {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "getObjectProperties",
|
| - "params": { "isolateId" : currentIsolate.id,
|
| - "objectId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then(showPromptAfter(handleGetObjPropsResponse));
|
| - } else if (command == "pl" && args.length >= 3) {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd;
|
| - if (args.length == 3) {
|
| - cmd = { "id": seqNum,
|
| - "command": "getListElements",
|
| - "params": { "isolateId" : currentIsolate.id,
|
| - "objectId": int.parse(args[1]),
|
| - "index": int.parse(args[2]) } };
|
| - } else {
|
| - cmd = { "id": seqNum,
|
| - "command": "getListElements",
|
| - "params": { "isolateId" : currentIsolate.id,
|
| - "objectId": int.parse(args[1]),
|
| - "index": int.parse(args[2]),
|
| - "length": int.parse(args[3]) } };
|
| - }
|
| - sendCmd(cmd).then(showPromptAfter(handleGetListResponse));
|
| - } else if (command == "pc" && args.length == 2) {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "getClassProperties",
|
| - "params": { "isolateId" : currentIsolate.id,
|
| - "classId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then(showPromptAfter(handleGetClassPropsResponse));
|
| - } else if (command == "plib" && args.length == 2) {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "getLibraryProperties",
|
| - "params": {"isolateId" : currentIsolate.id,
|
| - "libraryId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then(showPromptAfter(handleGetLibraryPropsResponse));
|
| - } else if (command == "slib" && args.length == 3) {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "setLibraryProperties",
|
| - "params": {"isolateId" : currentIsolate.id,
|
| - "libraryId": int.parse(args[1]),
|
| - "debuggingEnabled": args[2] } };
|
| - sendCmd(cmd).then(showPromptAfter(handleSetLibraryPropsResponse));
|
| - } else if (command == "pg" && args.length == 2) {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "getGlobalVariables",
|
| - "params": { "isolateId" : currentIsolate.id,
|
| - "libraryId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then(showPromptAfter(handleGetGlobalVarsResponse));
|
| - } else if (command == "gs" && args.length == 3) {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "getScriptSource",
|
| - "params": { "isolateId" : currentIsolate.id,
|
| - "libraryId": int.parse(args[1]),
|
| - "url": args[2] } };
|
| - sendCmd(cmd).then(showPromptAfter(handleGetSourceResponse));
|
| - } else if (command == "tok" && args.length == 3) {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "getLineNumberTable",
|
| - "params": { "isolateId" : currentIsolate.id,
|
| - "libraryId": int.parse(args[1]),
|
| - "url": args[2] } };
|
| - sendCmd(cmd).then(showPromptAfter(handleGetLineTableResponse));
|
| - } else if (command == "epi" && args.length == 2) {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum,
|
| - "command": "setPauseOnException",
|
| - "params": { "isolateId" : currentIsolate.id,
|
| - "exceptions": args[1] } };
|
| - sendCmd(cmd).then(showPromptAfter(handleGenericResponse));
|
| - } else if (command == "li") {
|
| - if (!checkCurrentIsolate()) {
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - var cmd = { "id": seqNum, "command": "getIsolateIds" };
|
| - sendCmd(cmd).then(showPromptAfter(handleGetIsolatesResponse));
|
| - } else if (command == "sci" && args.length == 2) {
|
| - var id = int.parse(args[1]);
|
| - if (targetIsolates[id] != null) {
|
| - setCurrentIsolate(targetIsolates[id]);
|
| - } else {
|
| - print("$id is not a valid isolate id");
|
| - }
|
| - cmdo.show();
|
| - } else if (command == "i" && args.length == 2) {
|
| - var cmd = { "id": seqNum,
|
| - "command": "interrupt",
|
| - "params": { "isolateId": int.parse(args[1]) } };
|
| - sendCmd(cmd).then(showPromptAfter(handleGenericResponse));
|
| - } else if (command.length == 0) {
|
| - huh();
|
| - cmdo.show();
|
| - } else {
|
| - // TODO(turnidge): Use this for all commands.
|
| - var matches = matchCommand(command, true);
|
| - if (matches.length == 0) {
|
| - huh();
|
| - cmdo.show();
|
| - } else if (matches.length == 1) {
|
| - matches[0].run(args).then((_) {
|
| - cmdo.show();
|
| - });
|
| - } else {
|
| - var matchNames = matches.map((handler) => handler.name);
|
| - print("Ambiguous command '$command' : ${matchNames.toList()}");
|
| - cmdo.show();
|
| - }
|
| - }
|
| -}
|
| -
|
| -
|
| -void processError(error, trace) {
|
| - cmdo.hide();
|
| - print("\nInternal error:\n$error\n$trace");
|
| - cmdo.show();
|
| -}
|
| -
|
| -
|
| -void processDone() {
|
| - debuggerQuit();
|
| -}
|
| -
|
| -
|
| -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 = formatLocation(value['location']);
|
| - 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(Map 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(Map 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(Map 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(Map 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(Map response) {
|
| - Map props = response["result"];
|
| - assert(props["debuggingEnabled"] != null);
|
| - print(" debugging enabled: ${props["debuggingEnabled"]}");
|
| -}
|
| -
|
| -
|
| -handleGetGlobalVarsResponse(Map response) {
|
| - List globals = response["result"]["globals"];
|
| - for (int i = 0; i < globals.length; i++) {
|
| - printNamedObject(globals[i]);
|
| - }
|
| -}
|
| -
|
| -
|
| -handleGetSourceResponse(Map response) {
|
| - Map result = response["result"];
|
| - String source = result["text"];
|
| - print("Source text:\n$source\n--------");
|
| -}
|
| -
|
| -
|
| -handleGetLineTableResponse(Map response) {
|
| - Map result = response["result"];
|
| - var info = result["lines"];
|
| - print("Line info table:\n$info");
|
| -}
|
| -
|
| -
|
| -void handleGetIsolatesResponse(Map response) {
|
| - Map result = response["result"];
|
| - List ids = result["isolateIds"];
|
| - assert(ids != null);
|
| - print("List of isolates:");
|
| - for (int id in ids) {
|
| - TargetIsolate isolate = targetIsolates[id];
|
| - var state = (isolate != null) ? "running" : "<unknown isolate>";
|
| - if (isolate != null && isolate.isPaused) {
|
| - var loc = formatLocation(isolate.pausedLocation);
|
| - state = "paused at $loc";
|
| - }
|
| - var marker = " ";
|
| - if (currentIsolate != null && id == currentIsolate.id) {
|
| - marker = "*";
|
| - }
|
| - print("$marker $id $state");
|
| - }
|
| -}
|
| -
|
| -
|
| -void handleGetLibraryResponse(Map 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(Map 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(Map response) {
|
| - Map result = response["result"];
|
| - print(remoteObject(result));
|
| -}
|
| -
|
| -
|
| -void handleSetBpResponse(Map response) {
|
| - Map result = response["result"];
|
| - var id = result["breakpointId"];
|
| - assert(id != null);
|
| - print("Set BP $id");
|
| -}
|
| -
|
| -
|
| -void handleGenericResponse(Map response) {
|
| - if (response["error"] != null) {
|
| - print("Error: ${response["error"]}");
|
| - }
|
| -}
|
| -
|
| -void handleResumedResponse(Map response) {
|
| - if (response["error"] != null) {
|
| - print("Error: ${response["error"]}");
|
| - return;
|
| - }
|
| - assert(currentIsolate != null);
|
| - currentIsolate.pausedLocation = null;
|
| -}
|
| -
|
| -
|
| -void handleStackTraceResponse(Map 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 loc = formatLocation(frame["location"]);
|
| - print("#${_leftJustify(frame_num,2)} $fname at $loc");
|
| - 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]);
|
| - }
|
| -}
|
| -
|
| -
|
| -Map<int, int> parseLineNumberTable(List<List<int>> table) {
|
| - Map tokenToLine = {};
|
| - for (var line in table) {
|
| - // Each entry begins with a line number...
|
| - var lineNumber = line[0];
|
| - for (var pos = 1; pos < line.length; pos += 2) {
|
| - // ...and is followed by (token offset, col number) pairs.
|
| - // We ignore the column numbers.
|
| - var tokenOffset = line[pos];
|
| - tokenToLine[tokenOffset] = lineNumber;
|
| - }
|
| - }
|
| - return tokenToLine;
|
| -}
|
| -
|
| -
|
| -Future<TargetScript> getTargetScript(Map location) {
|
| - var isolate = targetIsolates[currentIsolate.id];
|
| - var url = location['url'];
|
| - var script = isolate.scripts[url];
|
| - if (script != null) {
|
| - return new Future.value(script);
|
| - }
|
| - script = new TargetScript();
|
| -
|
| - // Ask the vm for the source and line number table.
|
| - var sourceCmd = {
|
| - "id": seqNum++,
|
| - "command": "getScriptSource",
|
| - "params": { "isolateId": currentIsolate.id,
|
| - "libraryId": location['libraryId'],
|
| - "url": url } };
|
| -
|
| - var lineNumberCmd = {
|
| - "id": seqNum++,
|
| - "command": "getLineNumberTable",
|
| - "params": { "isolateId": currentIsolate.id,
|
| - "libraryId": location['libraryId'],
|
| - "url": url } };
|
| -
|
| - // Send the source command
|
| - var sourceResponse = sendCmd(sourceCmd).then((response) {
|
| - Map result = response["result"];
|
| - script.source = result['text'];
|
| - // Line numbers are 1-based so add a dummy for line 0.
|
| - script.lineToSource = [''];
|
| - script.lineToSource.addAll(script.source.split('\n'));
|
| - });
|
| -
|
| - // Send the line numbers command
|
| - var lineNumberResponse = sendCmd(lineNumberCmd).then((response) {
|
| - Map result = response["result"];
|
| - script.tokenToLine = parseLineNumberTable(result['lines']);
|
| - });
|
| -
|
| - return Future.wait([sourceResponse, lineNumberResponse]).then((_) {
|
| - // When both commands complete, cache the result.
|
| - isolate.scripts[url] = script;
|
| - return script;
|
| - });
|
| -}
|
| -
|
| -
|
| -Future printLocation(String label, Map location) {
|
| - // Figure out the line number.
|
| - return getTargetScript(location).then((script) {
|
| - var lineNumber = script.tokenToLine[location['tokenOffset']];
|
| - var text = script.lineToSource[lineNumber];
|
| - if (label != null) {
|
| - var fileName = location['url'].split("/").last;
|
| - print("$label \n"
|
| - " at $fileName:$lineNumber");
|
| - }
|
| - print("${_leftJustify(lineNumber, 8)}$text");
|
| - });
|
| -}
|
| -
|
| -
|
| -Future handlePausedEvent(msg) {
|
| - assert(msg["params"] != null);
|
| - var reason = msg["params"]["reason"];
|
| - int isolateId = msg["params"]["isolateId"];
|
| - assert(isolateId != null);
|
| - var isolate = targetIsolates[isolateId];
|
| - assert(isolate != null);
|
| - assert(!isolate.isPaused);
|
| - var location = msg["params"]["location"];;
|
| - setCurrentIsolate(isolate);
|
| - isolate.pausedLocation = (location == null) ? UnknownLocation : location;
|
| - if (reason == "breakpoint") {
|
| - assert(location != null);
|
| - var bpId = (msg["params"]["breakpointId"]);
|
| - var label = (bpId != null) ? "Breakpoint $bpId" : null;
|
| - return printLocation(label, location);
|
| - } else if (reason == "interrupted") {
|
| - assert(location != null);
|
| - return printLocation("Interrupted", location);
|
| - } else {
|
| - assert(reason == "exception");
|
| - var excObj = msg["params"]["exception"];
|
| - print("Isolate $isolateId paused on exception");
|
| - print(remoteObject(excObj));
|
| - return new Future.value();
|
| - }
|
| -}
|
| -
|
| -void handleIsolateEvent(msg) {
|
| - Map params = msg["params"];
|
| - assert(params != null);
|
| - var isolateId = params["id"];
|
| - var reason = params["reason"];
|
| - if (reason == "created") {
|
| - print("Isolate $isolateId has been created.");
|
| - assert(targetIsolates[isolateId] == null);
|
| - targetIsolates[isolateId] = new TargetIsolate(isolateId);
|
| - } else {
|
| - assert(reason == "shutdown");
|
| - var isolate = targetIsolates.remove(isolateId);
|
| - assert(isolate != null);
|
| - if (isolate == mainIsolate) {
|
| - mainIsolate = null;
|
| - print("Main isolate ${isolate.id} has terminated.");
|
| - } else {
|
| - print("Isolate ${isolate.id} has terminated.");
|
| - }
|
| - if (isolate == currentIsolate) {
|
| - currentIsolate = mainIsolate;
|
| - if (currentIsolate == null && !targetIsolates.isEmpty) {
|
| - currentIsolate = targetIsolates.values.first;
|
| - }
|
| - if (currentIsolate != null) {
|
| - print("Setting current isolate to ${currentIsolate.id}.");
|
| - } else {
|
| - print("All isolates have terminated.");
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -void processVmMessage(String jsonString) {
|
| - var msg = JSON.decode(jsonString);
|
| - if (msg == null) {
|
| - return;
|
| - }
|
| - var event = msg["event"];
|
| - if (event == "isolate") {
|
| - cmdo.hide();
|
| - handleIsolateEvent(msg);
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - if (event == "paused") {
|
| - cmdo.hide();
|
| - handlePausedEvent(msg).then((_) {
|
| - cmdo.show();
|
| - });
|
| - return;
|
| - }
|
| - if (event == "breakpointResolved") {
|
| - Map params = msg["params"];
|
| - assert(params != null);
|
| - var isolateId = params["isolateId"];
|
| - var location = formatLocation(params["location"]);
|
| - cmdo.hide();
|
| - print("Breakpoint ${params["breakpointId"]} resolved in isolate $isolateId"
|
| - " at $location.");
|
| - cmdo.show();
|
| - return;
|
| - }
|
| - if (msg["id"] != null) {
|
| - var id = msg["id"];
|
| - if (outstandingCommands.containsKey(id)) {
|
| - var completer = outstandingCommands.remove(id);
|
| - if (msg["error"] != null) {
|
| - print("VM says: ${msg["error"]}");
|
| - // TODO(turnidge): Rework how hide/show happens. For now we
|
| - // show here explicitly.
|
| - cmdo.show();
|
| - } else {
|
| - completer.complete(msg);
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -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;
|
| -}
|
| -
|
| -List<String> debuggerCommandCompleter(List<String> commandParts) {
|
| - List<String> completions = new List<String>();
|
| -
|
| - // TODO(turnidge): Have a global command table and use it to for
|
| - // help messages, command completion, and command dispatching. For now
|
| - // we hardcode the list here.
|
| - //
|
| - // TODO(turnidge): Implement completion for arguments as well.
|
| - List<String> oldCommands = ['bt', 'r', 's', 'so', 'si', 'sbp', 'rbp',
|
| - 'po', 'eval', 'pl', 'pc', 'll', 'plib', 'slib',
|
| - 'pg', 'ls', 'gs', 'tok', 'epi', 'li', 'i' ];
|
| -
|
| - // Completion of first word in the command.
|
| - if (commandParts.length == 1) {
|
| - String prefix = commandParts.last;
|
| - for (var command in oldCommands) {
|
| - if (command.startsWith(prefix)) {
|
| - completions.add(command);
|
| - }
|
| - }
|
| - for (var command in commandList) {
|
| - if (command.name.startsWith(prefix)) {
|
| - completions.add(command.name);
|
| - }
|
| - }
|
| - }
|
| -
|
| - return completions;
|
| -}
|
| -
|
| -Future closeCommando() {
|
| - var subscription = cmdSubscription;
|
| - cmdSubscription = null;
|
| - cmdo = null;
|
| -
|
| - var future = subscription.cancel();
|
| - if (future != null) {
|
| - return future;
|
| - } else {
|
| - return new Future.value();
|
| - }
|
| -}
|
| -
|
| -
|
| -Future openVmSocket(int attempt) {
|
| - return Socket.connect("127.0.0.1", debugPort).then(
|
| - setupVmSocket,
|
| - onError: (e) {
|
| - // We were unable to connect to the debugger's port. Try again.
|
| - retryOpenVmSocket(e, attempt);
|
| - });
|
| -}
|
| -
|
| -
|
| -void setupVmSocket(Socket s) {
|
| - vmSock = s;
|
| - vmSock.setOption(SocketOption.TCP_NODELAY, true);
|
| - var stringStream = vmSock.transform(UTF8.decoder);
|
| - outstandingCommands = new Map<int, Completer>();
|
| - vmSubscription = stringStream.listen(
|
| - (String data) {
|
| - processVmData(data);
|
| - },
|
| - onDone: () {
|
| - cmdo.hide();
|
| - if (verbose) {
|
| - print("VM debugger connection closed");
|
| - }
|
| - closeVmSocket().then((_) {
|
| - cmdo.show();
|
| - });
|
| - },
|
| - onError: (err) {
|
| - cmdo.hide();
|
| - // TODO(floitsch): do we want to print the stack trace?
|
| - print("Error in debug connection: $err");
|
| -
|
| - // TODO(turnidge): Kill the debugged process here?
|
| - closeVmSocket().then((_) {
|
| - cmdo.show();
|
| - });
|
| - });
|
| -}
|
| -
|
| -
|
| -Future retryOpenVmSocket(error, int attempt) {
|
| - var delay;
|
| - if (attempt < 10) {
|
| - delay = new Duration(milliseconds:10);
|
| - } else if (attempt < 20) {
|
| - delay = new Duration(seconds:1);
|
| - } else {
|
| - // Too many retries. Give up.
|
| - //
|
| - // TODO(turnidge): Kill the debugged process here?
|
| - print('Timed out waiting for debugger to start.\nError: $e');
|
| - return closeVmSocket();
|
| - }
|
| - // Wait and retry.
|
| - return new Future.delayed(delay, () {
|
| - openVmSocket(attempt + 1);
|
| - });
|
| -}
|
| -
|
| -
|
| -Future closeVmSocket() {
|
| - if (vmSubscription == null) {
|
| - // Already closed, nothing to do.
|
| - assert(vmSock == null);
|
| - return new Future.value();
|
| - }
|
| -
|
| - isDebugging = false;
|
| - var subscription = vmSubscription;
|
| - var sock = vmSock;
|
| -
|
| - // Wait for the socket to close and the subscription to be
|
| - // cancelled. Perhaps overkill, but it means we know these will be
|
| - // done.
|
| - //
|
| - // This is uglier than it needs to be since cancel can return null.
|
| - var cleanupFutures = [sock.close()];
|
| - var future = subscription.cancel();
|
| - if (future != null) {
|
| - cleanupFutures.add(future);
|
| - }
|
| -
|
| - vmSubscription = null;
|
| - vmSock = null;
|
| - outstandingCommands = null;
|
| - return Future.wait(cleanupFutures);
|
| -}
|
| -
|
| -void debuggerError(self, parent, zone, error, StackTrace trace) {
|
| - print('\n--------\nExiting due to unexpected error:\n'
|
| - ' $error\n$trace\n');
|
| - debuggerQuit();
|
| -}
|
| -
|
| -Future debuggerQuit() {
|
| - // Kill target process, if any.
|
| - if (targetProcess != null) {
|
| - if (!targetProcess.kill()) {
|
| - print('Unable to kill process ${targetProcess.pid}');
|
| - }
|
| - }
|
| -
|
| - // Restore terminal settings, close connections.
|
| - return Future.wait([closeCommando(), closeVmSocket()]).then((_) {
|
| - exit(0);
|
| -
|
| - // Unreachable.
|
| - return new Future.value();
|
| - });
|
| -}
|
| -
|
| -
|
| -void parseArgs(List<String> args) {
|
| - int pos = 0;
|
| - settings['vm'] = Platform.executable;
|
| - while (pos < args.length && args[pos].startsWith('-')) {
|
| - pos++;
|
| - }
|
| - if (pos < args.length) {
|
| - settings['vmargs'] = args.getRange(0, pos).join(' ');
|
| - settings['script'] = args[pos];
|
| - settings['args'] = args.getRange(pos + 1, args.length).join(' ');
|
| - }
|
| -}
|
| -
|
| -void main(List<String> args) {
|
| - // Setup a zone which will exit the debugger cleanly on any uncaught
|
| - // exception.
|
| - var zone = Zone.ROOT.fork(specification:new ZoneSpecification(
|
| - handleUncaughtError: debuggerError));
|
| -
|
| - zone.run(() {
|
| - parseArgs(args);
|
| - cmdo = new Commando(completer: debuggerCommandCompleter);
|
| - cmdSubscription = cmdo.commands.listen(processCommand,
|
| - onError: processError,
|
| - onDone: processDone);
|
| - });
|
| -}
|
|
|