| Index: tools/ddbg.dart
|
| ===================================================================
|
| --- tools/ddbg.dart (revision 30608)
|
| +++ tools/ddbg.dart (working copy)
|
| @@ -8,6 +8,7 @@
|
| import "dart:convert";
|
| import "dart:io";
|
| import "dart:async";
|
| +import "dart:math";
|
|
|
| import "ddbg/lib/commando.dart";
|
|
|
| @@ -26,11 +27,14 @@
|
|
|
| Socket vmSock;
|
| String vmData;
|
| +var cmdSubscription;
|
| Commando cmdo;
|
| var vmSubscription;
|
| int seqNum = 0;
|
|
|
| -Process targetProcess;
|
| +bool isDebugging = false;
|
| +Process targetProcess = null;
|
| +bool suppressNextExitCode = false;
|
|
|
| final verbose = false;
|
| final printMessages = false;
|
| @@ -38,39 +42,8 @@
|
| TargetIsolate currentIsolate;
|
| TargetIsolate mainIsolate;
|
|
|
| +int debugPort = 5858;
|
|
|
| -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
|
| - sci <id> Set current target isolate
|
| - i <id> Interrupt execution of given isolate id
|
| - h Print help
|
| -""");
|
| -}
|
| -
|
| -
|
| String formatLocation(Map location) {
|
| if (location == null) return "";
|
| var fileName = location["url"].split("/").last;
|
| @@ -78,13 +51,6 @@
|
| }
|
|
|
|
|
| -void quitShell() {
|
| - vmSubscription.cancel();
|
| - vmSock.close();
|
| - cmdo.done();
|
| -}
|
| -
|
| -
|
| Future sendCmd(Map<String, dynamic> cmd) {
|
| var completer = new Completer();
|
| int id = cmd["id"];
|
| @@ -113,6 +79,411 @@
|
| 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();
|
| +
|
| +// Generates a string of 'count' spaces.
|
| +String _spaces(int count) {
|
| + return new List.filled(count, ' ').join('');
|
| +}
|
| +
|
| +// 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() ];
|
| +
|
| +
|
| +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) {
|
| + const tabStop = 12;
|
| + var spaces = _spaces(max(1, (tabStop - command.name.length)));
|
| + print(' ${command.name}${spaces}${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 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 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)) {
|
| @@ -123,13 +494,13 @@
|
| };
|
| }
|
|
|
| -
|
| void processCommand(String cmdLine) {
|
|
|
| void huh() {
|
| - print("'$cmdLine' not understood, try h for help");
|
| + print("'$cmdLine' not understood, try 'help' for help.");
|
| }
|
|
|
| + cmdo.hide();
|
| seqNum++;
|
| cmdLine = cmdLine.trim();
|
| var args = cmdLine.split(' ');
|
| @@ -137,6 +508,7 @@
|
| return;
|
| }
|
| var command = args[0];
|
| +
|
| var resume_commands =
|
| { 'r':'resume', 's':'stepOver', 'si':'stepInto', 'so':'stepOut'};
|
| if (resume_commands[command] != null) {
|
| @@ -144,19 +516,16 @@
|
| var cmd = { "id": seqNum,
|
| "command": resume_commands[command],
|
| "params": { "isolateId" : currentIsolate.id } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleResumedResponse));
|
| } else if (command == "bt") {
|
| var cmd = { "id": seqNum,
|
| "command": "getStackTrace",
|
| "params": { "isolateId" : currentIsolate.id } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleStackTraceResponse));
|
| } else if (command == "ll") {
|
| var cmd = { "id": seqNum,
|
| "command": "getLibraries",
|
| "params": { "isolateId" : currentIsolate.id } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleGetLibraryResponse));
|
| } else if (command == "sbp" && args.length >= 2) {
|
| var url, line;
|
| @@ -173,21 +542,18 @@
|
| "params": { "isolateId" : currentIsolate.id,
|
| "url": url,
|
| "line": line }};
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleSetBpResponse));
|
| } else if (command == "rbp" && args.length == 2) {
|
| var cmd = { "id": seqNum,
|
| "command": "removeBreakpoint",
|
| "params": { "isolateId" : currentIsolate.id,
|
| "breakpointId": int.parse(args[1]) } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleGenericResponse));
|
| } else if (command == "ls" && args.length == 2) {
|
| var cmd = { "id": seqNum,
|
| "command": "getScriptURLs",
|
| "params": { "isolateId" : currentIsolate.id,
|
| "libraryId": int.parse(args[1]) } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleGetScriptsResponse));
|
| } else if (command == "eval" && args.length > 3) {
|
| var expr = args.getRange(3, args.length).join(" ");
|
| @@ -207,14 +573,12 @@
|
| "params": { "isolateId": currentIsolate.id,
|
| target: int.parse(args[2]),
|
| "expression": expr } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleEvalResponse));
|
| } else if (command == "po" && args.length == 2) {
|
| var cmd = { "id": seqNum,
|
| "command": "getObjectProperties",
|
| "params": { "isolateId" : currentIsolate.id,
|
| "objectId": int.parse(args[1]) } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleGetObjPropsResponse));
|
| } else if (command == "pl" && args.length >= 3) {
|
| var cmd;
|
| @@ -232,21 +596,18 @@
|
| "index": int.parse(args[2]),
|
| "length": int.parse(args[3]) } };
|
| }
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleGetListResponse));
|
| } else if (command == "pc" && args.length == 2) {
|
| var cmd = { "id": seqNum,
|
| "command": "getClassProperties",
|
| "params": { "isolateId" : currentIsolate.id,
|
| "classId": int.parse(args[1]) } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleGetClassPropsResponse));
|
| } else if (command == "plib" && args.length == 2) {
|
| var cmd = { "id": seqNum,
|
| "command": "getLibraryProperties",
|
| "params": {"isolateId" : currentIsolate.id,
|
| "libraryId": int.parse(args[1]) } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleGetLibraryPropsResponse));
|
| } else if (command == "slib" && args.length == 3) {
|
| var cmd = { "id": seqNum,
|
| @@ -254,14 +615,12 @@
|
| "params": {"isolateId" : currentIsolate.id,
|
| "libraryId": int.parse(args[1]),
|
| "debuggingEnabled": args[2] } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleSetLibraryPropsResponse));
|
| } else if (command == "pg" && args.length == 2) {
|
| var cmd = { "id": seqNum,
|
| "command": "getGlobalVariables",
|
| "params": { "isolateId" : currentIsolate.id,
|
| "libraryId": int.parse(args[1]) } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleGetGlobalVarsResponse));
|
| } else if (command == "gs" && args.length == 3) {
|
| var cmd = { "id": seqNum,
|
| @@ -269,7 +628,6 @@
|
| "params": { "isolateId" : currentIsolate.id,
|
| "libraryId": int.parse(args[1]),
|
| "url": args[2] } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleGetSourceResponse));
|
| } else if (command == "tok" && args.length == 3) {
|
| var cmd = { "id": seqNum,
|
| @@ -277,18 +635,15 @@
|
| "params": { "isolateId" : currentIsolate.id,
|
| "libraryId": int.parse(args[1]),
|
| "url": args[2] } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleGetLineTableResponse));
|
| } else if (command == "epi" && args.length == 2) {
|
| var cmd = { "id": seqNum,
|
| "command": "setPauseOnException",
|
| "params": { "isolateId" : currentIsolate.id,
|
| "exceptions": args[1] } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleGenericResponse));
|
| } else if (command == "li") {
|
| var cmd = { "id": seqNum, "command": "getIsolateIds" };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleGetIsolatesResponse));
|
| } else if (command == "sci" && args.length == 2) {
|
| var id = int.parse(args[1]);
|
| @@ -298,22 +653,46 @@
|
| } 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]) } };
|
| - cmdo.hide();
|
| sendCmd(cmd).then(showPromptAfter(handleGenericResponse));
|
| - } else if (command == "q") {
|
| - quitShell();
|
| - } else if (command == "h") {
|
| - printHelp();
|
| + } else if (command.length == 0) {
|
| + huh();
|
| + cmdo.show();
|
| } else {
|
| - huh();
|
| + // 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("Ambigous 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"];
|
| @@ -762,85 +1141,169 @@
|
| // we hardcode the list here.
|
| //
|
| // TODO(turnidge): Implement completion for arguments as well.
|
| - List<String> allCommands = ['q', 'bt', 'r', 's', 'so', 'si', 'sbp', 'rbp',
|
| + List<String> oldCommands = ['bt', 'r', 's', 'so', 'si', 'sbp', 'rbp',
|
| 'po', 'eval', 'pl', 'pc', 'll', 'plib', 'slib',
|
| - 'pg', 'ls', 'gs', 'tok', 'epi', 'li', 'i', 'h'];
|
| + 'pg', 'ls', 'gs', 'tok', 'epi', 'li', 'i' ];
|
|
|
| // Completion of first word in the command.
|
| if (commandParts.length == 1) {
|
| String prefix = commandParts.last;
|
| - for (String command in allCommands) {
|
| + 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;
|
| }
|
|
|
| -void debuggerMain() {
|
| +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>();
|
| - 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: () {
|
| + vmSubscription = stringStream.listen(
|
| + (String data) {
|
| + processVmData(data);
|
| + },
|
| + onDone: () {
|
| + cmdo.hide();
|
| + if (verbose) {
|
| 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();
|
| - });
|
| - cmdo = new Commando(stdin, stdout, processCommand,
|
| - completer : debuggerCommandCompleter);
|
| - });
|
| + }
|
| + 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();
|
| + });
|
| + });
|
| }
|
|
|
| -void main(List<String> args) {
|
| - if (args.length > 0) {
|
| - if (verbose) {
|
| - args = <String>['--debug', '--verbose_debug']..addAll(args);
|
| - } else {
|
| - args = <String>['--debug']..addAll(args);
|
| +
|
| +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);
|
| +}
|
| +
|
| +Future debuggerQuit() {
|
| + // Kill target process, if any.
|
| + if (targetProcess != null) {
|
| + if (!targetProcess.kill()) {
|
| + print('Unable to kill process ${targetProcess.pid}');
|
| }
|
| - Process.start(Platform.executable, args).then((Process process) {
|
| - 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) {
|
| - // Hide/show command prompt across asynchronous output.
|
| - if (cmdo != null) {
|
| - cmdo.hide();
|
| - }
|
| - print("$line");
|
| - if (cmdo != null) {
|
| - cmdo.show();
|
| - }
|
| - });
|
| + // Restore terminal settings, close connections.
|
| + return Future.wait([closeCommando(), closeVmSocket()]).then((_) {
|
| + exit(0);
|
|
|
| - process.exitCode.then((int exitCode) {
|
| - if (exitCode == 0) {
|
| - print('Program exited normally.');
|
| - } else {
|
| - print('Program exited with code $exitCode.');
|
| - }
|
| - });
|
| + // Unreachable.
|
| + return new Future.value();
|
| + });
|
| +}
|
|
|
| - debuggerMain();
|
| - });
|
| - } else {
|
| - debuggerMain();
|
| +
|
| +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) {
|
| + parseArgs(args);
|
| +
|
| + cmdo = new Commando(completer: debuggerCommandCompleter);
|
| + cmdSubscription = cmdo.commands.listen(processCommand,
|
| + onError: processError,
|
| + onDone: processDone);
|
| +}
|
|
|