Rather have the debugger be parent process
authorRyan Dahl <ry@tinyclouds.org>
Thu, 30 Dec 2010 06:07:38 +0000 (22:07 -0800)
committerRyan Dahl <ry@tinyclouds.org>
Thu, 30 Dec 2010 09:35:13 +0000 (01:35 -0800)
lib/_debugger.js
src/node.cc
src/node.js

index 2ae26ec..382afd1 100644 (file)
@@ -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();
-  });
 }
index c349afb..9d84055 100644 (file)
 #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.
index 4717e65..b1645ee 100644 (file)
     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