diff --git a/src/node.cc b/src/node.cc index c6efa89f2bf..ab24990029a 100644 --- a/src/node.cc +++ b/src/node.cc @@ -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 process_l = SetupProcessObject(argc, argv); diff --git a/src/node_isolate.cc b/src/node_isolate.cc index 47e5b66e3ca..ede131faf01 100644 --- a/src/node_isolate.cc +++ b/src/node_isolate.cc @@ -20,6 +20,7 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include +#include #include #include #include @@ -31,11 +32,16 @@ #include +#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 CreateIsolate(const Arguments& args) { } isolate->argv_[isolate->argc_] = NULL; + // If options object was provided + if (args.Length() > 1) { + Local options = args[1].As(); + Local opt_debug = options->Get(String::New("debug")); + Local 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 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 t = FunctionTemplate::New(IsolateDebugger::New); + isolate_debugger_constructor = Persistent::New(t); + + t->InstanceTemplate()->SetInternalFieldCount(1); + t->SetClassName(String::NewSymbol("IsolateDebugger")); + + NODE_SET_PROTOTYPE_METHOD(t, "write", IsolateDebugger::Write); +} + + +IsolateDebugger::IsolateDebugger(Handle init) { + debuggee_ = NULL; + initialized_ = false; + host_ = Isolate::GetCurrent(); + host_loop_ = host_->GetLoop(); + init_callback_fn_ = Persistent::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(this); + + msg_channel_ = new Channel( + 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 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(c->data); + + d->host_->Enter(); + HandleScope scope; + + Handle 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 IsolateDebugger::New(const Arguments& args) { + HandleScope scope; + + IsolateDebugger* d = new IsolateDebugger(args[0]); + d->Wrap(args.Holder()); + + return args.This(); +} + + +IsolateDebugger* IsolateDebugger::New(Handle init) { + HandleScope scope; + + Handle argv[1] = { init }; + Handle i = isolate_debugger_constructor->GetFunction()->NewInstance( + 1, + argv + ); + + return ObjectWrap::Unwrap(i); +} + + +Handle IsolateDebugger::Write(const Arguments& args) { + HandleScope scope; + + if (args.Length() != 1) { + return ThrowException(String::New( + "IsolateDebugger::Write requires one argument" + )); + } + + IsolateDebugger* d = ObjectWrap::Unwrap(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 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(); } diff --git a/src/node_isolate.h b/src/node_isolate.h index ccfc8343332..667ee24ad1a 100644 --- a/src/node_isolate.h +++ b/src/node_isolate.h @@ -23,8 +23,10 @@ #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 @@ -42,9 +44,15 @@ namespace node { +template + +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 New(const v8::Arguments& args); + static IsolateDebugger* New(v8::Handle init); + + static v8::Handle Write(const v8::Arguments& args); + + static void DebugMessageHandler(const v8::Debug::Message& message); + static void MessageCallback(IsolateDebuggerMessage* msg, void*); + + IsolateDebugger(v8::Handle init); + ~IsolateDebugger(); + +protected: + Isolate* host_; + uv_loop_t* host_loop_; + + uv_async_t init_callback_; + v8::Persistent init_callback_fn_; + + bool initialized_; + Isolate* debuggee_; + v8::Isolate* debuggee_v8_; + + struct debug_msg_s { + uint16_t* value; + int len; + + IsolateDebugger* d; + }; + + Channel* msg_channel_; +}; + } // namespace node #endif // SRC_NODE_ISOLATE_H_ diff --git a/src/node_vars.h b/src/node_vars.h index 4ec628497c2..f50938ab984 100644 --- a/src/node_vars.h +++ b/src/node_vars.h @@ -171,6 +171,9 @@ struct globals { v8::Persistent wrapped_context_constructor; v8::Persistent wrapped_script_constructor; + // node_isolate.cc + v8::Persistent isolate_debugger_constructor; + // node_signal_watcher.cc v8::Persistent callback_symbol; v8::Persistent signal_watcher_constructor_template; diff --git a/test/simple/test-isolates.js b/test/simple/test-isolates.js index 8524f6fb6ab..d06628f70be 100644 --- a/test/simple/test-isolates.js +++ b/test/simple/test-isolates.js @@ -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");