diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2010-12-22 06:14:29 +0300 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2010-12-30 12:35:11 +0300 |
commit | 8d82ec21308812132a71afe45c5bc517b4a43354 (patch) | |
tree | cd5138d97432387f51332bee46b034ada1cabda8 /lib | |
parent | 0df804b9e7b509797e652a0e8657989023a01911 (diff) |
Add beginning of build-in debugger
+ test-debugger-client (which is currently broken)
Diffstat (limited to 'lib')
-rw-r--r-- | lib/_debugger.js | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/lib/_debugger.js b/lib/_debugger.js new file mode 100644 index 00000000000..9f95e896ce5 --- /dev/null +++ b/lib/_debugger.js @@ -0,0 +1,221 @@ +var net = require('net'); +var readline = require('readline'); +var inherits = require('util').inherits; + +exports.port = 5858; + +exports.start = function (pid) { + if (pid) { + process.kill(pid, "SIGUSR1"); + setTimeout(tryConnect, 100); + } else { + tryConnect(); + } +}; + +var c; + +function tryConnect() { + c = new Client(); + + process.stdout.write("connecting..."); + c.connect(exports.port, function () { + process.stdout.write("ok\r\n"); + startInterface(); + }); + +} + +// +// Parser/Serializer for V8 debugger protocol +// http://code.google.com/p/v8/wiki/DebuggerProtocol +// +// Usage: +// p = new Protocol(); +// +// p.onResponse = function (res) { +// // do stuff with response from V8 +// }; +// +// socket.setEncoding('utf8'); +// socket.on('data', function (s) { +// // Pass strings into the protocol +// p.execute(s); +// }); +// +// +function Protocol() { + this._newRes(); +} +exports.Protocol = Protocol; + + +Protocol.prototype._newRes = function(raw) { + this.res = { raw: raw || '', headers: {} }; + this.state = 'headers'; + this.reqSeq = 1; +}; + + +Protocol.prototype.execute = function(d) { + var res = this.res; + res.raw += d; + + switch (this.state) { + case 'headers': + var endHeaderIndex = res.raw.indexOf('\r\n\r\n'); + + if (endHeaderIndex < 0) break; + + var lines = res.raw.slice(0, endHeaderIndex).split('\r\n'); + for (var i = 0; i < lines.length; i++) { + var kv = lines[i].split(/: +/); + res.headers[kv[0]] = kv[1]; + } + + this.contentLength = +res.headers['Content-Length']; + this.bodyStartIndex = endHeaderIndex + 4; + + this.state = 'body'; + if (res.raw.length - this.bodyStartIndex < this.contentLength) break; + // pass thru + + case 'body': + if (res.raw.length - this.bodyStartIndex >= this.contentLength) { + res.body = + res.raw.slice(this.bodyStartIndex, + this.bodyStartIndex + this.contentLength); + // JSON parse body? + res.body = res.body.length ? JSON.parse(res.body) : {}; + + // Done! + this.onResponse(res); + + this._newRes(res.raw.slice(this.bodyStartIndex + this.contentLength)); + } + break; + + default: + throw new Error("Unknown state"); + break; + } +}; + + +Protocol.prototype.serialize = function(req) { + req.type = 'request'; + req.seq = this.reqSeq++; + var json = JSON.stringify(req); + return 'Content-Length: ' + json.length + '\r\n\r\n' + json; +}; + + + + +function Client() { + net.Stream.call(this); + var protocol = this.protocol = new Protocol(c); + this._reqCallbacks = []; + var socket = this; + + // Note that 'Protocol' requires strings instead of Buffers. + socket.setEncoding('utf8'); + socket.on('data', function(d) { + protocol.execute(d); + }); + + protocol.onResponse = this._onResponse.bind(this); +}; +inherits(Client, net.Stream); +exports.Client = Client; + + +Client.prototype._onResponse = function(res) { + console.error(res); + for (var i = 0; i < this._reqCallbacks.length; i++) { + var cb = this._reqCallbacks[i]; + if (this._reqCallbacks[i].request_seq == cb.request_seq) break; + } + + if (cb) { + this._reqCallbacks.splice(i, 1); + cb(res.body); + } else if (res.headers.Type == 'connect') { + // do nothing + } else { + console.error("unhandled res: %j", res.body); + } +}; + + +Client.prototype.req = function(req, cb) { + this.write(this.protocol.serialize(req)); + cb.request_seq = req.seq; + this._reqCallbacks.push(cb); +}; + + +Client.prototype.reqVersion = function(cb) { + this.req({ command: 'version' } , function (res) { + if (cb) cb(res.body.V8Version, res.running); + }); +}; + + +var helpMessage = "Commands: version, eval, help, quit"; + + +function startInterface() { + + var i = readline.createInterface(process.stdout); + var stdin = process.openStdin(); + stdin.addListener('data', function(chunk) { + i.write(chunk); + }); + + var prompt = '> '; + + i.setPrompt(prompt); + i.prompt(); + + i.on('SIGINT', function() { + i.close(); + }); + + i.on('line', function(cmd) { + if (cmd == 'quit') { + process.exit(0); + } else if (/^help/.test(cmd)) { + console.log(helpMessage); + i.prompt(); + + } else if ('version' == cmd) { + c.reqVersion(function (v) { + console.log(v); + i.prompt(); + }); + + } else if (/^eval/.test(cmd)) { + var req = { + command: 'evaluate', + arguments: { 'expression': cmd.slice(5) } + }; + + c.req(req, function (res) { + console.log(res); + i.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); + } + i.prompt(); + } + }); + + i.on('close', function() { + stdin.destroy(); + }); +} |