diff --git a/lib/_debugger.js b/lib/_debugger.js index 2ae26ec100..382afd18cd 100644 --- a/lib/_debugger.js +++ b/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(); - }); } diff --git a/src/node.cc b/src/node.cc index c349afb8dd..9d8405571f 100644 --- a/src/node.cc +++ b/src/node.cc @@ -17,11 +17,6 @@ #include /* getpwnam() */ #include /* getgrnam() */ -// waitpid -#include -#include - - #include "platform.h" #include @@ -92,7 +87,6 @@ static ev_idle tick_spinner; static bool need_tick_cb; static Persistent 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 exec_state, - Handle event_data, - Handle 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. diff --git a/src/node.js b/src/node.js index 4717e65d47..b1645eeac7 100644 --- a/src/node.js +++ b/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