Browse Source

Rather have the debugger be parent process

v0.7.4-release
Ryan Dahl 14 years ago
parent
commit
90e55c3357
  1. 127
      lib/_debugger.js
  2. 143
      src/node.cc
  3. 3
      src/node.js

127
lib/_debugger.js

@ -1,30 +1,84 @@
var net = require('net');
var readline = require('readline');
var inherits = require('util').inherits;
var spawn = require('child_process').spawn;
exports.port = 5858;
exports.start = function (pid) {
if (pid) {
process.kill(pid, "SIGUSR1");
setTimeout(tryConnect, 100);
} else {
tryConnect();
}
exports.start = function () {
startInterface();
};
var c;
var term;
function trySpawn(cb) {
var args = process.argv.slice(2);
args.unshift('--debug-brk');
console.log(args);
function tryConnect() {
var child = spawn(process.execPath, args, {
customFds: [0, 1, 2]
});
setTimeout(function () {
tryConnect(cb);
}, 1000);
}
function tryConnect(cb) {
c = new Client();
process.stdout.write("connecting...");
c.connect(exports.port);
c.on('ready', function () {
c.once('ready', function () {
process.stdout.write("ok\r\n");
startInterface();
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();
});
}
//
@ -146,6 +200,8 @@ Client.prototype._onResponse = function(res) {
this.emit('ready');
} else if (res.body && res.body.event == 'break') {
this.emit('break', res.body);
} else if (res.body && res.body.event == 'afterCompile') {
this.emit('afterCompile', res.body);
} else if (cb) {
this._reqCallbacks.splice(i, 1);
cb(res.body);
@ -290,11 +346,10 @@ function SourceInfo(body) {
function startInterface() {
var term = readline.createInterface(process.stdout);
term = readline.createInterface(process.stdout);
var stdin = process.openStdin();
stdin.addListener('data', function(chunk) {
term.write(chunk);
@ -323,13 +378,14 @@ function startInterface() {
term.on('SIGINT', tryQuit);
term.on('close', tryQuit);
c.on('close', function () {
process.exit(0);
});
term.on('line', function(cmd) {
if (cmd == 'quit' || cmd == 'q' || cmd == 'exit') {
tryQuit();
} else if (/^r(un)?/.test(cmd)) {
trySpawn();
} else if (/^help/.test(cmd)) {
console.log(helpMessage);
term.prompt();
@ -419,43 +475,4 @@ function startInterface() {
term.prompt();
}
});
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();
});
}

143
src/node.cc

@ -17,11 +17,6 @@
#include <pwd.h> /* getpwnam() */
#include <grp.h> /* getgrnam() */
// waitpid
#include <sys/types.h>
#include <sys/wait.h>
#include "platform.h"
#include <node_buffer.h>
@ -92,7 +87,6 @@ static ev_idle tick_spinner;
static bool need_tick_cb;
static Persistent<String> tick_callback_sym;
static ev_async enable_debug;
static ev_async eio_want_poll_notifier;
static ev_async eio_done_poll_notifier;
static ev_idle eio_poller;
@ -1868,41 +1862,6 @@ static void SignalExit(int signal) {
}
static void EnableDebugSignalHandler(int signal) {
// can't do much here, marshal this back into the main thread where we'll
// enable the debugger.
ev_async_send(EV_DEFAULT_UC_ &enable_debug);
}
static void EnableDebug(bool wait_connect) {
// Start the debug thread and it's associated TCP server on port 5858.
bool r = Debug::EnableAgent("node " NODE_VERSION, debug_port);
if (wait_connect) {
// Set up an empty handler so v8 will not continue until a debugger
// attaches. This is the same behavior as Debug::EnableAgent(_,_,true)
// except we don't break at the beginning of the script.
// see Debugger::StartAgent in debug.cc of v8/src
Debug::SetMessageHandler2(node::DebugBreakMessageHandler);
}
// Crappy check that everything went well. FIXME
assert(r);
// Print out some information.
fprintf(stderr, "debugger listening on port %d\r\n", debug_port);
}
static void EnableDebug2(EV_P_ ev_async *watcher, int revents) {
assert(watcher == &enable_debug);
assert(revents == EV_ASYNC);
EnableDebug(false);
}
static int RegisterSignalHandler(int signal, void (*handler)(int)) {
struct sigaction sa;
@ -1913,57 +1872,6 @@ static int RegisterSignalHandler(int signal, void (*handler)(int)) {
}
static bool debugger_slave_running = false;
static void HandleDebugEvent(DebugEvent event,
Handle<Object> exec_state,
Handle<Object> event_data,
Handle<Value> data) {
HandleScope scope;
if (debugger_slave_running) return;
if (event != Break) {
return;
}
// Then we take one of two actions
// 1. Inspect the environ variable NODE_DEBUG_PROG; if it is not empty //
// then start it. (TODO)
// 2. Start the built-in debugger.
size_t size = 2*PATH_MAX;
char node_path[size];
OS::GetExecutablePath(node_path, &size);
int pid = vfork();
if (pid == -1) {
perror("vfork()");
return;
}
if (pid == 0) {
// Child process
char *argv[] = { node_path, "debug", NULL };
execvp(node_path, argv);
perror("execvp()");
_exit(127);
}
debugger_slave_running = true;
// We've hit some debugger event. First we will enable the debugger agent.
EnableDebug(true);
// TODO probably need to waitpid here or something to avoid zombies.
// int status;
// waitpid(pid, &status, 0);
//Debug::DebugBreak();
}
int Start(int argc, char *argv[]) {
// Hack aroung with the argv pointer. Used for process.title = "blah".
argv = node::Platform::SetupArgs(argc, argv);
@ -2059,32 +1967,37 @@ int Start(int argc, char *argv[]) {
V8::SetFatalErrorHandler(node::OnFatalError);
// Initialize the async watcher for receiving messages from the debug
// thread and marshal it into the main thread. DebugMessageCallback()
// is called from the main thread to execute a random bit of javascript
// - which will give V8 control so it can handle whatever new message
// had been received on the debug thread.
ev_async_init(&node::debug_watcher, node::DebugMessageCallback);
ev_set_priority(&node::debug_watcher, EV_MAXPRI);
// Set the callback DebugMessageDispatch which is called from the debug
// thread.
Debug::SetDebugMessageDispatchHandler(node::DebugMessageDispatch);
// Start the async watcher.
ev_async_start(EV_DEFAULT_UC_ &node::debug_watcher);
// unref it so that we exit the event loop despite it being active.
ev_unref(EV_DEFAULT_UC);
// If the --debug flag was specified then initialize the debug thread.
if (node::use_debug_agent) {
// XXX: only use if debug flag enabled?
Debug::SetDebugEventListener(HandleDebugEvent);
} else {
RegisterSignalHandler(SIGUSR1, EnableDebugSignalHandler);
ev_async_init(&enable_debug, EnableDebug2);
ev_async_start(EV_DEFAULT_UC_ &enable_debug);
// Initialize the async watcher for receiving messages from the debug
// thread and marshal it into the main thread. DebugMessageCallback()
// is called from the main thread to execute a random bit of javascript
// - which will give V8 control so it can handle whatever new message
// had been received on the debug thread.
ev_async_init(&node::debug_watcher, node::DebugMessageCallback);
ev_set_priority(&node::debug_watcher, EV_MAXPRI);
// Set the callback DebugMessageDispatch which is called from the debug
// thread.
Debug::SetDebugMessageDispatchHandler(node::DebugMessageDispatch);
// Start the async watcher.
ev_async_start(EV_DEFAULT_UC_ &node::debug_watcher);
// unref it so that we exit the event loop despite it being active.
ev_unref(EV_DEFAULT_UC);
// Start the debug thread and it's associated TCP server on port 5858.
bool r = Debug::EnableAgent("node " NODE_VERSION, node::debug_port);
if (node::debug_wait_connect) {
// Set up an empty handler so v8 will not continue until a debugger
// attaches. This is the same behavior as Debug::EnableAgent(_,_,true)
// except we don't break at the beginning of the script.
// see Debugger::StartAgent in debug.cc of v8/src
Debug::SetMessageHandler2(node::DebugBreakMessageHandler);
}
// Crappy check that everything went well. FIXME
assert(r);
// Print out some information.
printf("debugger listening on port %d\n", node::debug_port);
}
// Create the one and only Context.

3
src/node.js

@ -539,8 +539,7 @@
if (process.argv[1] == 'debug') {
// Start the debugger agent
var d = requireNative('_debugger');
var pid = process.argv[2];
d.start(pid);
d.start();
} else {
// Load module

Loading…
Cancel
Save