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.port = 5858;
|
||||||
|
|
||||||
exports.start = function () {
|
exports.start = function () {
|
||||||
startInterface();
|
var interface = new Interface();
|
||||||
|
|
||||||
process.on('exit', function () {
|
|
||||||
if (child) child.kill();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var child;
|
|
||||||
var c;
|
|
||||||
var term;
|
|
||||||
var args = process.argv.slice(2);
|
var args = process.argv.slice(2);
|
||||||
args.unshift('--debug-brk');
|
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
|
// Parser/Serializer for V8 debugger protocol
|
||||||
@ -185,7 +103,7 @@ var NO_FRAME = -1;
|
|||||||
|
|
||||||
function Client() {
|
function Client() {
|
||||||
net.Stream.call(this);
|
net.Stream.call(this);
|
||||||
var protocol = this.protocol = new Protocol(c);
|
var protocol = this.protocol = new Protocol(this);
|
||||||
this._reqCallbacks = [];
|
this._reqCallbacks = [];
|
||||||
var socket = this;
|
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) {
|
Client.prototype.step = function(action, count, cb) {
|
||||||
var req = {
|
var req = {
|
||||||
command: 'continue',
|
command: 'continue',
|
||||||
@ -394,202 +312,316 @@ function SourceInfo(body) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var restartQuestionPrompt = "The program being debugged has " +
|
// This class is the readline-enabled debugger interface which is invoked on
|
||||||
"been started already.\n" +
|
// "node debug"
|
||||||
"Start it from the beginning? (y or n): ";
|
function Interface() {
|
||||||
|
var self = this;
|
||||||
|
var term = this.term = readline.createInterface(process.stdout);
|
||||||
|
var child;
|
||||||
|
var client;
|
||||||
|
var term;
|
||||||
|
|
||||||
function restartQuestion (cb) {
|
process.on('exit', function () {
|
||||||
term.question(restartQuestionPrompt, function (answer) {
|
self.killChild();
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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();
|
var stdin = process.openStdin();
|
||||||
stdin.addListener('data', function(chunk) {
|
stdin.addListener('data', function(chunk) {
|
||||||
term.write(chunk);
|
term.write(chunk);
|
||||||
});
|
});
|
||||||
|
|
||||||
var prompt = 'debug> ';
|
|
||||||
|
|
||||||
term.setPrompt('debug> ');
|
term.setPrompt('debug> ');
|
||||||
term.prompt();
|
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('SIGINT', function () {
|
||||||
term.on('close', tryQuit);
|
self.tryQuit();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
term.on('close', function () {
|
||||||
|
self.tryQuit();
|
||||||
|
});
|
||||||
|
|
||||||
term.on('line', function(cmd) {
|
term.on('line', function(cmd) {
|
||||||
|
|
||||||
// trim whitespace
|
// trim whitespace
|
||||||
cmd = cmd.replace(/^\s*/, '').replace(/\s*$/, '');
|
cmd = cmd.replace(/^\s*/, '').replace(/\s*$/, '');
|
||||||
|
self.handleCommand(cmd);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (cmd == 'quit' || cmd == 'q' || cmd == 'exit') {
|
|
||||||
tryQuit();
|
|
||||||
|
|
||||||
} else if (/^r(un)?/.test(cmd)) {
|
Interface.prototype.tryQuit = function() {
|
||||||
if (child) {
|
if (this.quitTried) return;
|
||||||
restartQuestion(function (yes) {
|
this.quitTried = true;
|
||||||
if (!yes) {
|
this.killChild();
|
||||||
term.prompt();
|
this.term.close();
|
||||||
} else {
|
process.exit(0);
|
||||||
console.log("restarting...");
|
};
|
||||||
trySpawn(function () {
|
|
||||||
c.reqContinue();
|
|
||||||
});
|
Interface.prototype.handleBreak = function(r) {
|
||||||
}
|
var result = '';
|
||||||
});
|
if (r.breakpoints) {
|
||||||
} else {
|
result += 'breakpoint';
|
||||||
trySpawn(function () {
|
if (r.breakpoints.length > 1) {
|
||||||
c.reqContinue();
|
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)) {
|
this.client.currentSourceLine = r.sourceLine;
|
||||||
console.log(helpMessage);
|
this.client.currentFrame = 0;
|
||||||
term.prompt();
|
this.client.currentScript = r.script.name;
|
||||||
|
|
||||||
} else if ('version' == cmd) {
|
console.log(result);
|
||||||
if (!c) {
|
|
||||||
printNotConnected();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
c.reqVersion(function (v) {
|
|
||||||
console.log(v);
|
|
||||||
term.prompt();
|
|
||||||
});
|
|
||||||
|
|
||||||
} else if (/info +breakpoints/.test(cmd)) {
|
this.term.prompt();
|
||||||
if (!c) {
|
};
|
||||||
printNotConnected();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
c.listbreakpoints(function (res) {
|
|
||||||
console.log(res);
|
|
||||||
term.prompt();
|
|
||||||
});
|
|
||||||
|
|
||||||
} else if (/^backtrace/.test(cmd) || /^bt/.test(cmd)) {
|
|
||||||
if (!c) {
|
Interface.prototype.handleCommand = function(cmd) {
|
||||||
printNotConnected();
|
var self = this;
|
||||||
return;
|
|
||||||
}
|
var client = this.client;
|
||||||
c.reqBacktrace(function (bt) {
|
var term = this.term;
|
||||||
if (/full/.test(cmd)) {
|
|
||||||
console.log(bt);
|
if (cmd == 'quit' || cmd == 'q' || cmd == 'exit') {
|
||||||
} else if (bt.totalFrames == 0) {
|
self.tryQuit();
|
||||||
console.log('(empty stack)');
|
|
||||||
|
} else if (/^r(un)?/.test(cmd)) {
|
||||||
|
if (self.child) {
|
||||||
|
self.restartQuestion(function (yes) {
|
||||||
|
if (!yes) {
|
||||||
|
term.prompt();
|
||||||
} else {
|
} else {
|
||||||
var result = '';
|
console.log("restarting...");
|
||||||
for (j = 0; j < bt.frames.length; j++) {
|
self.trySpawn();
|
||||||
if (j != 0) result += '\n';
|
}
|
||||||
result += bt.frames[j].text;
|
});
|
||||||
}
|
} else {
|
||||||
console.log(result);
|
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();
|
term.prompt();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} else if (cmd == 'scripts' || cmd == 'scripts full') {
|
} else {
|
||||||
if (!c) {
|
if (!/^\s*$/.test(cmd)) {
|
||||||
printNotConnected();
|
// If it's not all white-space print this error message.
|
||||||
return;
|
console.log('Unknown command "%s". Try "help"', cmd);
|
||||||
}
|
}
|
||||||
printScripts();
|
term.prompt();
|
||||||
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)) {
|
var restartQuestionPrompt = "The program being debugged has " +
|
||||||
if (!c) {
|
"been started already.\n" +
|
||||||
printNotConnected();
|
"Start it from the beginning? (y or n): ";
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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 {
|
} else {
|
||||||
if (!/^\s*$/.test(cmd)) {
|
console.log("Please answer y or n.");
|
||||||
// If it's not all white-space print this error message.
|
self.restartQuestion(cb);
|
||||||
console.log('Unknown command "%s". Try "help"', cmd);
|
|
||||||
}
|
|
||||||
term.prompt();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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