IsolateDebugger C++
authorFedor Indutny <fedor.indutny@gmail.com>
Fri, 6 Jan 2012 18:27:55 +0000 (00:27 +0600)
committerBen Noordhuis <info@bnoordhuis.nl>
Tue, 10 Jan 2012 01:28:42 +0000 (02:28 +0100)
src/node.cc
src/node_isolate.cc
src/node_isolate.h
src/node_vars.h
test/simple/test-isolates.js

index c6efa89..ab24990 100644 (file)
@@ -2624,15 +2624,20 @@ void StartThread(node::Isolate* isolate,
   // even when we need it to access it from another (debugger) thread.
   node_isolate = v8::Isolate::GetCurrent();
 
-  // If the --debug flag was specified then initialize the debug thread.
-  if (use_debug_agent) {
-    EnableDebug(debug_wait_connect);
-  } else {
+  // Only main isolate is allowed to run a debug agent and listen for signals
+  if (isolate->id_ == 1) {
+    // If the --debug flag was specified then initialize the debug thread.
+    if (use_debug_agent) {
+      EnableDebug(debug_wait_connect);
+    } else {
 #ifdef _WIN32
-    RegisterDebugSignalHandler();
+      RegisterDebugSignalHandler();
 #else // Posix
-    RegisterSignalHandler(SIGUSR1, EnableDebugSignalHandler);
+      RegisterSignalHandler(SIGUSR1, EnableDebugSignalHandler);
 #endif // __POSIX__
+    }
+  } else if (isolate->debug_state != Isolate::kNone) {
+    isolate->debugger_instance->Init();
   }
 
   Handle<Object> process_l = SetupProcessObject(argc, argv);
index 47e5b66..ede131f 100644 (file)
@@ -20,6 +20,7 @@
 // USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 #include <v8.h>
+#include <v8-debug.h>
 #include <node.h>
 #include <node_buffer.h>
 #include <node_isolate.h>
 #include <assert.h>
 
 
+#define isolate_debugger_constructor NODE_VAR(isolate_debugger_constructor)
+
+
 namespace node {
 
 using v8::Arguments;
 using v8::Array;
+using v8::Context;
 using v8::False;
+using v8::Function;
 using v8::FunctionTemplate;
 using v8::Handle;
 using v8::HandleScope;
@@ -47,6 +53,7 @@ using v8::ObjectTemplate;
 using v8::Persistent;
 using v8::String;
 using v8::True;
+using v8::Undefined;
 using v8::Value;
 using v8::Undefined;
 
@@ -252,6 +259,9 @@ Isolate::Isolate() {
     loop_ = uv_loop_new();
   }
 
+  debug_state = kNone;
+  debugger_instance = NULL;
+
   ngx_queue_init(&at_exit_callbacks_);
 
   v8_isolate_ = v8::Isolate::New();
@@ -290,7 +300,7 @@ void Isolate::Enter() {
   v8_isolate_->Enter();
 
   if (v8_context_.IsEmpty()) {
-    v8_context_ = v8::Context::New();
+    v8_context_ = Context::New();
   }
   v8_context_->Enter();
 
@@ -476,6 +486,22 @@ static Handle<Value> CreateIsolate(const Arguments& args) {
   }
   isolate->argv_[isolate->argc_] = NULL;
 
+  // If options object was provided
+  if (args.Length() > 1) {
+    Local<Object> options = args[1].As<Object>();
+    Local<Value> opt_debug = options->Get(String::New("debug"));
+    Local<Value> opt_debug_brk = options->Get(String::New("debugBrk"));
+
+    // Handle .debug = true case
+    if (opt_debug->IsFunction()) {
+      isolate->debug_state = opt_debug_brk->IsTrue() ?
+          Isolate::kDebugBrk
+          :
+          Isolate::kDebug;
+      isolate->debugger_instance = IsolateDebugger::New(opt_debug);
+    }
+  }
+
   if (uv_thread_create(&isolate->tid_, RunIsolate, isolate))
     return Null(); // wrap is collected by the GC
   else
@@ -493,6 +519,186 @@ void InitIsolates(Handle<Object> target) {
   HandleScope scope;
   NODE_SET_METHOD(target, "create", CreateIsolate);
   NODE_SET_METHOD(target, "count", CountIsolate);
+
+  IsolateDebugger::Initialize();
+}
+
+
+class IsolateDebuggerMessage {
+ public:
+  IsolateDebugger* d_;
+  uint16_t* value_;
+  int len_;
+
+  IsolateDebuggerMessage(IsolateDebugger* d, uint16_t* value, int len) {
+    d_ = d;
+    value_ = new uint16_t[len];
+    len_ = len;
+    memcpy(value_, value, len * sizeof(value_[0]));
+  }
+
+  ~IsolateDebuggerMessage() {
+    delete[] value_;
+  }
+};
+
+
+void IsolateDebugger::Initialize() {
+  HandleScope scope;
+
+  Local<FunctionTemplate> t = FunctionTemplate::New(IsolateDebugger::New);
+  isolate_debugger_constructor = Persistent<FunctionTemplate>::New(t);
+
+  t->InstanceTemplate()->SetInternalFieldCount(1);
+  t->SetClassName(String::NewSymbol("IsolateDebugger"));
+
+  NODE_SET_PROTOTYPE_METHOD(t, "write", IsolateDebugger::Write);
+}
+
+
+IsolateDebugger::IsolateDebugger(Handle<Value> init) {
+  debuggee_ = NULL;
+  initialized_ = false;
+  host_ = Isolate::GetCurrent();
+  host_loop_ = host_->GetLoop();
+  init_callback_fn_ = Persistent<Value>::New(init);
+
+  // Init async handle to invoke js callback once
+  // debugger will be initialized
+  uv_async_init(host_loop_,
+                &init_callback_,
+                IsolateDebugger::InitCallback);
+  init_callback_.data = reinterpret_cast<void*>(this);
+
+  msg_channel_ = new Channel<IsolateDebuggerMessage*>(
+      host_loop_, MessageCallback, NULL);
+}
+
+
+IsolateDebugger::~IsolateDebugger() {
+  init_callback_fn_.Clear();
+  init_callback_fn_.Dispose();
+  delete msg_channel_;
+}
+
+
+void IsolateDebugger::Init(void) {
+  HandleScope scope;
+
+  Isolate* isolate = Isolate::GetCurrent();
+
+  debuggee_ = isolate;
+  debuggee_v8_ = isolate->GetV8Isolate();
+  v8::Debug::SetMessageHandler2(IsolateDebugger::DebugMessageHandler);
+
+  // Expose v8debug for isolate
+
+  if (isolate->debug_state == Isolate::kDebugBrk) {
+    Local<Context> debugContext = v8::Debug::GetDebugContext();
+
+    debugContext->SetSecurityToken(
+        isolate->GetV8Context()->GetSecurityToken()
+    );
+    isolate->GetV8Context()->Global()->Set(
+        String::New("v8debug"),
+        debugContext->Global()
+    );
+  }
+
+  initialized_ = true;
+
+  uv_async_send(&init_callback_);
+}
+
+
+void IsolateDebugger::InitCallback(uv_async_t* c, int status) {
+  assert(c->data != NULL);
+
+  IsolateDebugger* d = reinterpret_cast<IsolateDebugger*>(c->data);
+
+  d->host_->Enter();
+  HandleScope scope;
+
+  Handle<Value> argv[1] = { d->handle_ };
+  Function::Cast(*d->init_callback_fn_)->Call(d->handle_, 1, argv);
+
+  d->host_->Exit();
+
+  // Unreference loop
+  uv_unref(d->host_loop_);
+}
+
+
+Handle<Value> IsolateDebugger::New(const Arguments& args) {
+  HandleScope scope;
+
+  IsolateDebugger* d = new IsolateDebugger(args[0]);
+  d->Wrap(args.Holder());
+
+  return args.This();
+}
+
+
+IsolateDebugger* IsolateDebugger::New(Handle<Value> init) {
+  HandleScope scope;
+
+  Handle<Value> argv[1] = { init };
+  Handle<Object> i = isolate_debugger_constructor->GetFunction()->NewInstance(
+      1,
+      argv
+  );
+
+  return ObjectWrap::Unwrap<IsolateDebugger>(i);
+}
+
+
+Handle<Value> IsolateDebugger::Write(const Arguments& args) {
+  HandleScope scope;
+
+  if (args.Length() != 1) {
+    return ThrowException(String::New(
+        "IsolateDebugger::Write requires one argument"
+    ));
+  }
+
+  IsolateDebugger* d = ObjectWrap::Unwrap<IsolateDebugger>(args.This());
+  assert(d->initialized_);
+
+  String::Value v(args[0]->ToString());
+  v8::Debug::SendCommand(*v,
+                         v.length(),
+                         NULL,
+                         d->debuggee_v8_);
+
+  return Undefined();
+}
+
+
+void IsolateDebugger::DebugMessageHandler(const v8::Debug::Message& message) {
+  IsolateDebugger* d = Isolate::GetCurrent()->debugger_instance;
+
+  String::Value v(message.GetJSON());
+  d->msg_channel_->Send(new IsolateDebuggerMessage(d, *v, v.length()));
+}
+
+
+void IsolateDebugger::MessageCallback(IsolateDebuggerMessage* msg, void*) {
+  assert(msg != NULL);
+
+  IsolateDebugger *d = msg->d_;
+  // Enter parent isolate context
+  d->host_->Enter();
+  HandleScope scope;
+
+  // debugger.onmessage should be a function!
+  Handle<Value> argv[] = { String::New(msg->value_, msg->len_) };
+  MakeCallback(d->handle_, "onmessage", ARRAY_SIZE(argv), argv);
+
+  // Free memory allocated for message
+  delete msg;
+
+  // And leave isolate
+  d->host_->Exit();
 }
 
 
index ccfc834..667ee24 100644 (file)
 #define SRC_NODE_ISOLATE_H_
 
 #include "v8.h"
+#include "v8-debug.h"
 #include "uv.h"
 #include "node_vars.h"
+#include "node_object_wrap.h"
 #include "ngx-queue.h"
 
 #ifdef NDEBUG
 
 namespace node {
 
+template <class T>
+
+class Channel;
+
 class IsolateWrap;
 class IsolateChannel;
 class IsolateMessage;
+class IsolateDebugger;
+class IsolateDebuggerMessage;
 
 class Isolate {
 public:
@@ -52,6 +60,13 @@ public:
   int argc_;
   uv_thread_t tid_;
 
+  enum {
+    kNone,
+    kDebug,
+    kDebugBrk
+  } debug_state;
+  IsolateDebugger* debugger_instance;
+
   // Call this before instantiating any Isolate
   static void Initialize();
   static int Count();
@@ -125,6 +140,44 @@ private:
   bool globals_init_;
 };
 
+class IsolateDebugger : ObjectWrap {
+public:
+  static void Initialize();
+  void Init();
+  static void InitCallback(uv_async_t* c, int status);
+
+  static v8::Handle<v8::Value> New(const v8::Arguments& args);
+  static IsolateDebugger* New(v8::Handle<v8::Value> init);
+
+  static v8::Handle<v8::Value> Write(const v8::Arguments& args);
+
+  static void DebugMessageHandler(const v8::Debug::Message& message);
+  static void MessageCallback(IsolateDebuggerMessage* msg, void*);
+
+  IsolateDebugger(v8::Handle<v8::Value> init);
+  ~IsolateDebugger();
+
+protected:
+  Isolate* host_;
+  uv_loop_t* host_loop_;
+
+  uv_async_t init_callback_;
+  v8::Persistent<v8::Value> init_callback_fn_;
+
+  bool initialized_;
+  Isolate* debuggee_;
+  v8::Isolate* debuggee_v8_;
+
+  struct debug_msg_s {
+    uint16_t* value;
+    int len;
+
+    IsolateDebugger* d;
+  };
+
+  Channel<IsolateDebuggerMessage*>* msg_channel_;
+};
+
 } // namespace node
 
 #endif // SRC_NODE_ISOLATE_H_
index 4ec6284..f50938a 100644 (file)
@@ -171,6 +171,9 @@ struct globals {
   v8::Persistent<v8::FunctionTemplate> wrapped_context_constructor;
   v8::Persistent<v8::FunctionTemplate> wrapped_script_constructor;
 
+  // node_isolate.cc
+  v8::Persistent<v8::FunctionTemplate> isolate_debugger_constructor;
+
   // node_signal_watcher.cc
   v8::Persistent<v8::String> callback_symbol;
   v8::Persistent<v8::FunctionTemplate> signal_watcher_constructor_template;
index 8524f6f..d06628f 100644 (file)
@@ -5,7 +5,20 @@ var isolates = process.binding('isolates');
 console.log("count: %d", isolates.count());
 
 if (process.tid === 1) {
-  var isolate = isolates.create(process.argv);
+  var isolate = isolates.create(process.argv, {
+    debug: function init(d) {
+      d.onmessage = function(data) {
+        data = JSON.parse(data);
+        if (data.event === 'break') {
+          d.write(JSON.stringify({
+            type: 'request',
+            seq: 1,
+            command: 'continue'
+          }));
+        }
+      };
+    }
+  });
 
   isolate.onmessage = function() {
     console.error("onmessage");