Wrap up debugger in one class
just for better readablity
This commit is contained in:
parent
109f845e49
commit
d4859a55bc
532
lib/_debugger.js
532
lib/_debugger.js
@ -6,96 +6,14 @@ var spawn = require('child_process').spawn;
|
||||
exports.port = 5858;
|
||||
|
||||
exports.start = function () {
|
||||
startInterface();
|
||||
|
||||
process.on('exit', function () {
|
||||
if (child) child.kill();
|
||||
});
|
||||
var interface = new Interface();
|
||||
};
|
||||
|
||||
var child;
|
||||
var c;
|
||||
var term;
|
||||
|
||||
var args = process.argv.slice(2);
|
||||
args.unshift('--debug-brk');
|
||||
|
||||
|
||||
function tryConnect(cb) {
|
||||
c = new Client();
|
||||
|
||||
process.stdout.write("connecting...");
|
||||
c.connect(exports.port);
|
||||
c.once('ready', function () {
|
||||
process.stdout.write("ok\r\n");
|
||||
if (cb) cb();
|
||||
});
|
||||
|
||||
c.on('close', function () {
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
c.on('unhandledResponse', function (res) {
|
||||
console.log("\r\nunhandled res:");
|
||||
console.log(res);
|
||||
term.prompt();
|
||||
});
|
||||
|
||||
c.on('break', function (res) {
|
||||
var result = '';
|
||||
if (res.body.breakpoints) {
|
||||
result += 'breakpoint';
|
||||
if (res.body.breakpoints.length > 1) {
|
||||
result += 's';
|
||||
}
|
||||
result += ' #';
|
||||
for (var i = 0; i < res.body.breakpoints.length; i++) {
|
||||
if (i > 0) {
|
||||
result += ', #';
|
||||
}
|
||||
result += res.body.breakpoints[i];
|
||||
}
|
||||
} else {
|
||||
result += 'break';
|
||||
}
|
||||
result += ' in ';
|
||||
result += res.body.invocationText;
|
||||
result += ', ';
|
||||
result += SourceInfo(res.body);
|
||||
result += '\n';
|
||||
result += SourceUnderline(res.body.sourceLineText, res.body.sourceColumn);
|
||||
|
||||
c.currentSourceLine = res.body.sourceLine;
|
||||
c.currentFrame = 0;
|
||||
c.currentScript = res.body.script.name;
|
||||
|
||||
console.log(result);
|
||||
|
||||
term.prompt();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function killChild() {
|
||||
if (c) {
|
||||
c.destroy();
|
||||
}
|
||||
|
||||
if (child) {
|
||||
child.kill();
|
||||
child = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function trySpawn(cb) {
|
||||
killChild();
|
||||
|
||||
child = spawn(process.execPath, args, { customFds: [0, 1, 2] });
|
||||
|
||||
setTimeout(function () {
|
||||
tryConnect(cb);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
//
|
||||
// Parser/Serializer for V8 debugger protocol
|
||||
@ -185,7 +103,7 @@ var NO_FRAME = -1;
|
||||
|
||||
function Client() {
|
||||
net.Stream.call(this);
|
||||
var protocol = this.protocol = new Protocol(c);
|
||||
var protocol = this.protocol = new Protocol(this);
|
||||
this._reqCallbacks = [];
|
||||
var socket = this;
|
||||
|
||||
@ -340,7 +258,7 @@ Client.prototype.listbreakpoints = function(cb) {
|
||||
});
|
||||
};
|
||||
|
||||
// c.next(1, cb);
|
||||
// client.next(1, cb);
|
||||
Client.prototype.step = function(action, count, cb) {
|
||||
var req = {
|
||||
command: 'continue',
|
||||
@ -394,202 +312,316 @@ function SourceInfo(body) {
|
||||
}
|
||||
|
||||
|
||||
var restartQuestionPrompt = "The program being debugged has " +
|
||||
"been started already.\n" +
|
||||
"Start it from the beginning? (y or n): ";
|
||||
// This class is the readline-enabled debugger interface which is invoked on
|
||||
// "node debug"
|
||||
function Interface() {
|
||||
var self = this;
|
||||
var term = this.term = readline.createInterface(process.stdout);
|
||||
var child;
|
||||
var client;
|
||||
var term;
|
||||
|
||||
function restartQuestion (cb) {
|
||||
term.question(restartQuestionPrompt, function (answer) {
|
||||
if (/^y(es)?$/i.test(answer)) {
|
||||
cb(true);
|
||||
} else if (/^n(o)?$/i.test(answer)) {
|
||||
cb(false);
|
||||
} else {
|
||||
console.log("Please answer y or n.");
|
||||
restartQuestion(cb);
|
||||
}
|
||||
process.on('exit', function () {
|
||||
self.killChild();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function printScripts () {
|
||||
var text = '';
|
||||
for (var id in c.scripts) {
|
||||
var script = c.scripts[id];
|
||||
if (typeof script == 'object' && script.name) {
|
||||
text += script.name == c.currentScript ? '* ' : ' ';
|
||||
text += script.name + '\n';
|
||||
}
|
||||
}
|
||||
process.stdout.write(text);
|
||||
}
|
||||
|
||||
|
||||
function printNotConnected () {
|
||||
console.log("Program not running. Try 'run'.");
|
||||
term.prompt();
|
||||
}
|
||||
|
||||
|
||||
function startInterface() {
|
||||
|
||||
term = readline.createInterface(process.stdout);
|
||||
|
||||
var stdin = process.openStdin();
|
||||
stdin.addListener('data', function(chunk) {
|
||||
term.write(chunk);
|
||||
});
|
||||
|
||||
var prompt = 'debug> ';
|
||||
|
||||
term.setPrompt('debug> ');
|
||||
term.prompt();
|
||||
|
||||
var quitTried = false;
|
||||
this.quitTried = false;
|
||||
|
||||
function tryQuit() {
|
||||
if (quitTried) return;
|
||||
quitTried = true;
|
||||
killChild();
|
||||
term.close();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
term.on('SIGINT', tryQuit);
|
||||
term.on('close', tryQuit);
|
||||
term.on('SIGINT', function () {
|
||||
self.tryQuit();
|
||||
});
|
||||
|
||||
|
||||
term.on('close', function () {
|
||||
self.tryQuit();
|
||||
});
|
||||
|
||||
term.on('line', function(cmd) {
|
||||
|
||||
// trim whitespace
|
||||
cmd = cmd.replace(/^\s*/, '').replace(/\s*$/, '');
|
||||
self.handleCommand(cmd);
|
||||
});
|
||||
}
|
||||
|
||||
if (cmd == 'quit' || cmd == 'q' || cmd == 'exit') {
|
||||
tryQuit();
|
||||
|
||||
} else if (/^r(un)?/.test(cmd)) {
|
||||
if (child) {
|
||||
restartQuestion(function (yes) {
|
||||
if (!yes) {
|
||||
term.prompt();
|
||||
} else {
|
||||
console.log("restarting...");
|
||||
trySpawn(function () {
|
||||
c.reqContinue();
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
trySpawn(function () {
|
||||
c.reqContinue();
|
||||
});
|
||||
Interface.prototype.tryQuit = function() {
|
||||
if (this.quitTried) return;
|
||||
this.quitTried = true;
|
||||
this.killChild();
|
||||
this.term.close();
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
|
||||
Interface.prototype.handleBreak = function(r) {
|
||||
var result = '';
|
||||
if (r.breakpoints) {
|
||||
result += 'breakpoint';
|
||||
if (r.breakpoints.length > 1) {
|
||||
result += 's';
|
||||
}
|
||||
result += ' #';
|
||||
for (var i = 0; i < r.breakpoints.length; i++) {
|
||||
if (i > 0) {
|
||||
result += ', #';
|
||||
}
|
||||
result += r.breakpoints[i];
|
||||
}
|
||||
} else {
|
||||
result += 'break';
|
||||
}
|
||||
result += ' in ';
|
||||
result += r.invocationText;
|
||||
result += ', ';
|
||||
result += SourceInfo(r);
|
||||
result += '\n';
|
||||
result += SourceUnderline(r.sourceLineText, r.sourceColumn);
|
||||
|
||||
} else if (/^help/.test(cmd)) {
|
||||
console.log(helpMessage);
|
||||
term.prompt();
|
||||
this.client.currentSourceLine = r.sourceLine;
|
||||
this.client.currentFrame = 0;
|
||||
this.client.currentScript = r.script.name;
|
||||
|
||||
} else if ('version' == cmd) {
|
||||
if (!c) {
|
||||
printNotConnected();
|
||||
return;
|
||||
}
|
||||
c.reqVersion(function (v) {
|
||||
console.log(v);
|
||||
term.prompt();
|
||||
});
|
||||
console.log(result);
|
||||
|
||||
} else if (/info +breakpoints/.test(cmd)) {
|
||||
if (!c) {
|
||||
printNotConnected();
|
||||
return;
|
||||
}
|
||||
c.listbreakpoints(function (res) {
|
||||
console.log(res);
|
||||
term.prompt();
|
||||
});
|
||||
this.term.prompt();
|
||||
};
|
||||
|
||||
} else if (/^backtrace/.test(cmd) || /^bt/.test(cmd)) {
|
||||
if (!c) {
|
||||
printNotConnected();
|
||||
return;
|
||||
}
|
||||
c.reqBacktrace(function (bt) {
|
||||
if (/full/.test(cmd)) {
|
||||
console.log(bt);
|
||||
} else if (bt.totalFrames == 0) {
|
||||
console.log('(empty stack)');
|
||||
|
||||
Interface.prototype.handleCommand = function(cmd) {
|
||||
var self = this;
|
||||
|
||||
var client = this.client;
|
||||
var term = this.term;
|
||||
|
||||
if (cmd == 'quit' || cmd == 'q' || cmd == 'exit') {
|
||||
self.tryQuit();
|
||||
|
||||
} else if (/^r(un)?/.test(cmd)) {
|
||||
if (self.child) {
|
||||
self.restartQuestion(function (yes) {
|
||||
if (!yes) {
|
||||
term.prompt();
|
||||
} else {
|
||||
var result = '';
|
||||
for (j = 0; j < bt.frames.length; j++) {
|
||||
if (j != 0) result += '\n';
|
||||
result += bt.frames[j].text;
|
||||
}
|
||||
console.log(result);
|
||||
console.log("restarting...");
|
||||
self.trySpawn();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
self.trySpawn();
|
||||
}
|
||||
|
||||
} else if (/^help/.test(cmd)) {
|
||||
console.log(helpMessage);
|
||||
term.prompt();
|
||||
|
||||
} else if ('version' == cmd) {
|
||||
if (!client) {
|
||||
self.printNotConnected();
|
||||
return;
|
||||
}
|
||||
client.reqVersion(function (v) {
|
||||
console.log(v);
|
||||
term.prompt();
|
||||
});
|
||||
|
||||
} else if (/info +breakpoints/.test(cmd)) {
|
||||
if (!client) {
|
||||
self.printNotConnected();
|
||||
return;
|
||||
}
|
||||
client.listbreakpoints(function (res) {
|
||||
console.log(res);
|
||||
term.prompt();
|
||||
});
|
||||
|
||||
} else if (/^backtrace/.test(cmd) || /^bt/.test(cmd)) {
|
||||
if (!client) {
|
||||
self.printNotConnected();
|
||||
return;
|
||||
}
|
||||
client.reqBacktrace(function (bt) {
|
||||
if (/full/.test(cmd)) {
|
||||
console.log(bt);
|
||||
} else if (bt.totalFrames == 0) {
|
||||
console.log('(empty stack)');
|
||||
} else {
|
||||
var result = '';
|
||||
for (j = 0; j < bt.frames.length; j++) {
|
||||
if (j != 0) result += '\n';
|
||||
result += bt.frames[j].text;
|
||||
}
|
||||
console.log(result);
|
||||
}
|
||||
term.prompt();
|
||||
});
|
||||
|
||||
} else if (cmd == 'scripts' || cmd == 'scripts full') {
|
||||
if (!client) {
|
||||
self.printNotConnected();
|
||||
return;
|
||||
}
|
||||
self.printScripts();
|
||||
term.prompt();
|
||||
|
||||
} else if (/^c(ontinue)?/.test(cmd)) {
|
||||
if (!client) {
|
||||
self.printNotConnected();
|
||||
return;
|
||||
}
|
||||
client.reqContinue(function (res) {
|
||||
// Wait for break point. (disable raw mode?)
|
||||
});
|
||||
|
||||
} else if (/^next/.test(cmd) || /^n/.test(cmd)) {
|
||||
if (!client) {
|
||||
self.printNotConnected();
|
||||
return;
|
||||
}
|
||||
client.step('next', 1, function (res) {
|
||||
// Wait for break point. (disable raw mode?)
|
||||
});
|
||||
|
||||
} else if (/^step/.test(cmd) || /^s/.test(cmd)) {
|
||||
if (!client) {
|
||||
self.printNotConnected();
|
||||
return;
|
||||
}
|
||||
client.step('in', 1, function (res) {
|
||||
// Wait for break point. (disable raw mode?)
|
||||
});
|
||||
|
||||
} else if (/^print/.test(cmd) || /^p/.test(cmd)) {
|
||||
if (!client) {
|
||||
self.printNotConnected();
|
||||
return;
|
||||
}
|
||||
var i = cmd.indexOf(' ');
|
||||
if (i < 0) {
|
||||
console.log("print [expression]");
|
||||
term.prompt();
|
||||
} else {
|
||||
cmd = cmd.slice(i);
|
||||
client.reqEval(cmd, function (res) {
|
||||
if (res) {
|
||||
console.log(res.text);
|
||||
} else {
|
||||
console.log(res);
|
||||
}
|
||||
term.prompt();
|
||||
});
|
||||
}
|
||||
|
||||
} else if (cmd == 'scripts' || cmd == 'scripts full') {
|
||||
if (!c) {
|
||||
printNotConnected();
|
||||
return;
|
||||
}
|
||||
printScripts();
|
||||
term.prompt();
|
||||
} else {
|
||||
if (!/^\s*$/.test(cmd)) {
|
||||
// If it's not all white-space print this error message.
|
||||
console.log('Unknown command "%s". Try "help"', cmd);
|
||||
}
|
||||
term.prompt();
|
||||
}
|
||||
};
|
||||
|
||||
} else if (/^continue/.test(cmd) || /^c/.test(cmd)) {
|
||||
if (!c) {
|
||||
printNotConnected();
|
||||
return;
|
||||
}
|
||||
c.reqContinue(function (res) {
|
||||
// Wait for break point. (disable raw mode?)
|
||||
});
|
||||
|
||||
} else if (/^next/.test(cmd) || /^n/.test(cmd)) {
|
||||
if (!c) {
|
||||
printNotConnected();
|
||||
return;
|
||||
}
|
||||
c.step('next', 1, function (res) {
|
||||
// Wait for break point. (disable raw mode?)
|
||||
});
|
||||
|
||||
} else if (/^step/.test(cmd) || /^s/.test(cmd)) {
|
||||
if (!c) {
|
||||
printNotConnected();
|
||||
return;
|
||||
}
|
||||
c.step('in', 1, function (res) {
|
||||
// Wait for break point. (disable raw mode?)
|
||||
});
|
||||
|
||||
} else if (/^print/.test(cmd) || /^p/.test(cmd)) {
|
||||
if (!c) {
|
||||
printNotConnected();
|
||||
return;
|
||||
}
|
||||
var i = cmd.indexOf(' ');
|
||||
if (i < 0) {
|
||||
console.log("print [expression]");
|
||||
term.prompt();
|
||||
} else {
|
||||
cmd = cmd.slice(i);
|
||||
c.reqEval(cmd, function (res) {
|
||||
if (res) {
|
||||
console.log(res.text);
|
||||
} else {
|
||||
console.log(res);
|
||||
}
|
||||
term.prompt();
|
||||
});
|
||||
}
|
||||
var restartQuestionPrompt = "The program being debugged has " +
|
||||
"been started already.\n" +
|
||||
"Start it from the beginning? (y or n): ";
|
||||
|
||||
Interface.prototype.restartQuestion = function(cb) {
|
||||
var self = this;
|
||||
this.term.question(restartQuestionPrompt, function (answer) {
|
||||
if (/^y(es)?$/i.test(answer)) {
|
||||
cb(true);
|
||||
} else if (/^n(o)?$/i.test(answer)) {
|
||||
cb(false);
|
||||
} else {
|
||||
if (!/^\s*$/.test(cmd)) {
|
||||
// If it's not all white-space print this error message.
|
||||
console.log('Unknown command "%s". Try "help"', cmd);
|
||||
}
|
||||
term.prompt();
|
||||
console.log("Please answer y or n.");
|
||||
self.restartQuestion(cb);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Interface.prototype.killChild = function() {
|
||||
if (this.client) {
|
||||
this.client.destroy();
|
||||
this.client = null;
|
||||
}
|
||||
|
||||
if (this.child) {
|
||||
this.child.kill();
|
||||
this.child = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Interface.prototype.trySpawn = function(cb) {
|
||||
this.killChild();
|
||||
|
||||
this.child = spawn(process.execPath, args, { customFds: [0, 1, 2] });
|
||||
|
||||
var self = this;
|
||||
setTimeout(function () {
|
||||
process.stdout.write("connecting...");
|
||||
var client = self.client = new Client();
|
||||
client.connect(exports.port);
|
||||
|
||||
client.once('ready', function () {
|
||||
process.stdout.write("ok\r\n");
|
||||
|
||||
// since we did debug-brk, we're hitting a break point immediately
|
||||
// continue before anything else.
|
||||
client.reqContinue();
|
||||
|
||||
if (cb) cb();
|
||||
});
|
||||
|
||||
client.on('close', function () {
|
||||
console.log("\nprogram terminated");
|
||||
self.client = null;
|
||||
self.killChild();
|
||||
self.term.prompt();
|
||||
});
|
||||
|
||||
client.on('unhandledResponse', function (res) {
|
||||
console.log("\r\nunhandled res:");
|
||||
console.log(res);
|
||||
self.term.prompt();
|
||||
});
|
||||
|
||||
client.on('break', function (res) {
|
||||
self.handleBreak(res.body);
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
|
||||
Interface.prototype.printNotConnected = function() {
|
||||
console.log("Program not running. Try 'run'.");
|
||||
this.term.prompt();
|
||||
};
|
||||
|
||||
|
||||
Interface.prototype.printScripts = function() {
|
||||
var client = this.client;
|
||||
var text = '';
|
||||
for (var id in client.scripts) {
|
||||
var script = client.scripts[id];
|
||||
if (typeof script == 'object' && script.name) {
|
||||
text += script.name == client.currentScript ? '* ' : ' ';
|
||||
text += script.name + '\n';
|
||||
}
|
||||
}
|
||||
process.stdout.write(text);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user