| Index: tools/ddbg.dart
|
| ===================================================================
|
| --- tools/ddbg.dart (revision 30871)
|
| +++ tools/ddbg.dart (working copy)
|
| @@ -12,6 +12,18 @@
|
|
|
| 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;
|
| +}
|
| +
|
| +
|
| class TargetIsolate {
|
| int id;
|
| // The location of the last paused event.
|
| @@ -19,6 +31,8 @@
|
|
|
| TargetIsolate(this.id);
|
| bool get isPaused => pausedLocation != null;
|
| +
|
| + Map<String, TargetScript> scripts = {};
|
| }
|
|
|
| Map<int, TargetIsolate> targetIsolates= new Map<int, TargetIsolate>();
|
| @@ -33,6 +47,7 @@
|
| int seqNum = 0;
|
|
|
| bool isDebugging = false;
|
| +bool stepMode = false;
|
| Process targetProcess = null;
|
| bool suppressNextExitCode = false;
|
|
|
| @@ -85,9 +100,13 @@
|
| // 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('');
|
| +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.
|
| @@ -138,9 +157,7 @@
|
| 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}');
|
| + print(' ${_leftJustify(command.name, 11)} ${command.helpShort}');
|
| }
|
|
|
| // TODO(turnidge): Convert all commands to use the Command class.
|
| @@ -336,7 +353,7 @@
|
|
|
| isDebugging = true;
|
| cmdo.hide();
|
| - return Process.start(vm, processArgs).then((Process process) {
|
| + return Process.start(vm, processArgs).then((process) {
|
| print("Started process ${process.pid} '$vm ${processArgs.join(' ')}'");
|
| targetProcess = process;
|
| process.stdin.close();
|
| @@ -512,6 +529,8 @@
|
| { 'r':'resume', 's':'stepOver', 'si':'stepInto', 'so':'stepOut'};
|
| if (resume_commands[command] != null) {
|
| if (!checkPaused()) return;
|
| + // TODO(turnidge): step mode isn't quite right yet.
|
| + stepMode = (command != 'r');
|
| var cmd = { "id": seqNum,
|
| "command": resume_commands[command],
|
| "params": { "isolateId" : currentIsolate.id } };
|
| @@ -909,7 +928,7 @@
|
| void printStackFrame(frame_num, Map frame) {
|
| var fname = frame["functionName"];
|
| var loc = formatLocation(frame["location"]);
|
| - print("$frame_num $fname ($loc)");
|
| + print("#${_leftJustify(frame_num,2)} $fname at $loc");
|
| List locals = frame["locals"];
|
| for (int i = 0; i < locals.length; i++) {
|
| printNamedObject(locals[i]);
|
| @@ -924,7 +943,85 @@
|
| }
|
|
|
|
|
| -void handlePausedEvent(msg) {
|
| +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);
|
| + }
|
| +
|
| + // 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']);
|
| + });
|
| +
|
| + script = new TargetScript();
|
| + 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"];
|
| @@ -936,16 +1033,16 @@
|
| assert(location != null);
|
| isolate.pausedLocation = location;
|
| if (reason == "breakpoint") {
|
| - print("Isolate $isolateId paused on breakpoint");
|
| - print("location: ${formatLocation(location)}");
|
| + return printLocation((stepMode ? null : "Breakpoint"), location);
|
| } else if (reason == "interrupted") {
|
| - print("Isolate $isolateId paused due to an interrupt");
|
| - print("location: ${formatLocation(location)}");
|
| + stepMode = false;
|
| + 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();
|
| }
|
| }
|
|
|
| @@ -1001,8 +1098,9 @@
|
| }
|
| if (event == "paused") {
|
| cmdo.hide();
|
| - handlePausedEvent(msg);
|
| - cmdo.show();
|
| + handlePausedEvent(msg).then((_) {
|
| + cmdo.show();
|
| + });
|
| return;
|
| }
|
| if (event == "breakpointResolved") {
|
| @@ -1011,7 +1109,7 @@
|
| var isolateId = params["isolateId"];
|
| var location = formatLocation(params["location"]);
|
| cmdo.hide();
|
| - print("BP ${params["breakpointId"]} resolved in isolate $isolateId"
|
| + print("Breakpoint ${params["breakpointId"]} resolved in isolate $isolateId"
|
| " at $location.");
|
| cmdo.show();
|
| return;
|
| @@ -1267,6 +1365,12 @@
|
| 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) {
|
| @@ -1299,10 +1403,16 @@
|
| }
|
|
|
| void main(List<String> args) {
|
| - parseArgs(args);
|
| + // Setup a zone which will exit the debugger cleanly on any uncaught
|
| + // exception.
|
| + var zone = Zone.ROOT.fork(specification:new ZoneSpecification(
|
| + handleUncaughtError: debuggerError));
|
|
|
| - cmdo = new Commando(completer: debuggerCommandCompleter);
|
| - cmdSubscription = cmdo.commands.listen(processCommand,
|
| - onError: processError,
|
| - onDone: processDone);
|
| + zone.run(() {
|
| + parseArgs(args);
|
| + cmdo = new Commando(completer: debuggerCommandCompleter);
|
| + cmdSubscription = cmdo.commands.listen(processCommand,
|
| + onError: processError,
|
| + onDone: processDone);
|
| + });
|
| }
|
|
|