IsolateDebugger C++
This commit is contained in:
parent
44e7033279
commit
99679c6430
@ -2624,6 +2624,8 @@ void StartThread(node::Isolate* isolate,
|
|||||||
// even when we need it to access it from another (debugger) thread.
|
// even when we need it to access it from another (debugger) thread.
|
||||||
node_isolate = v8::Isolate::GetCurrent();
|
node_isolate = v8::Isolate::GetCurrent();
|
||||||
|
|
||||||
|
// 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 the --debug flag was specified then initialize the debug thread.
|
||||||
if (use_debug_agent) {
|
if (use_debug_agent) {
|
||||||
EnableDebug(debug_wait_connect);
|
EnableDebug(debug_wait_connect);
|
||||||
@ -2634,6 +2636,9 @@ void StartThread(node::Isolate* isolate,
|
|||||||
RegisterSignalHandler(SIGUSR1, EnableDebugSignalHandler);
|
RegisterSignalHandler(SIGUSR1, EnableDebugSignalHandler);
|
||||||
#endif // __POSIX__
|
#endif // __POSIX__
|
||||||
}
|
}
|
||||||
|
} else if (isolate->debug_state != Isolate::kNone) {
|
||||||
|
isolate->debugger_instance->Init();
|
||||||
|
}
|
||||||
|
|
||||||
Handle<Object> process_l = SetupProcessObject(argc, argv);
|
Handle<Object> process_l = SetupProcessObject(argc, argv);
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
#include <v8.h>
|
#include <v8.h>
|
||||||
|
#include <v8-debug.h>
|
||||||
#include <node.h>
|
#include <node.h>
|
||||||
#include <node_buffer.h>
|
#include <node_buffer.h>
|
||||||
#include <node_isolate.h>
|
#include <node_isolate.h>
|
||||||
@ -31,11 +32,16 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define isolate_debugger_constructor NODE_VAR(isolate_debugger_constructor)
|
||||||
|
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
using v8::Arguments;
|
using v8::Arguments;
|
||||||
using v8::Array;
|
using v8::Array;
|
||||||
|
using v8::Context;
|
||||||
using v8::False;
|
using v8::False;
|
||||||
|
using v8::Function;
|
||||||
using v8::FunctionTemplate;
|
using v8::FunctionTemplate;
|
||||||
using v8::Handle;
|
using v8::Handle;
|
||||||
using v8::HandleScope;
|
using v8::HandleScope;
|
||||||
@ -47,6 +53,7 @@ using v8::ObjectTemplate;
|
|||||||
using v8::Persistent;
|
using v8::Persistent;
|
||||||
using v8::String;
|
using v8::String;
|
||||||
using v8::True;
|
using v8::True;
|
||||||
|
using v8::Undefined;
|
||||||
using v8::Value;
|
using v8::Value;
|
||||||
using v8::Undefined;
|
using v8::Undefined;
|
||||||
|
|
||||||
@ -252,6 +259,9 @@ Isolate::Isolate() {
|
|||||||
loop_ = uv_loop_new();
|
loop_ = uv_loop_new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug_state = kNone;
|
||||||
|
debugger_instance = NULL;
|
||||||
|
|
||||||
ngx_queue_init(&at_exit_callbacks_);
|
ngx_queue_init(&at_exit_callbacks_);
|
||||||
|
|
||||||
v8_isolate_ = v8::Isolate::New();
|
v8_isolate_ = v8::Isolate::New();
|
||||||
@ -290,7 +300,7 @@ void Isolate::Enter() {
|
|||||||
v8_isolate_->Enter();
|
v8_isolate_->Enter();
|
||||||
|
|
||||||
if (v8_context_.IsEmpty()) {
|
if (v8_context_.IsEmpty()) {
|
||||||
v8_context_ = v8::Context::New();
|
v8_context_ = Context::New();
|
||||||
}
|
}
|
||||||
v8_context_->Enter();
|
v8_context_->Enter();
|
||||||
|
|
||||||
@ -476,6 +486,22 @@ static Handle<Value> CreateIsolate(const Arguments& args) {
|
|||||||
}
|
}
|
||||||
isolate->argv_[isolate->argc_] = NULL;
|
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))
|
if (uv_thread_create(&isolate->tid_, RunIsolate, isolate))
|
||||||
return Null(); // wrap is collected by the GC
|
return Null(); // wrap is collected by the GC
|
||||||
else
|
else
|
||||||
@ -493,6 +519,186 @@ void InitIsolates(Handle<Object> target) {
|
|||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
NODE_SET_METHOD(target, "create", CreateIsolate);
|
NODE_SET_METHOD(target, "create", CreateIsolate);
|
||||||
NODE_SET_METHOD(target, "count", CountIsolate);
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,8 +23,10 @@
|
|||||||
#define SRC_NODE_ISOLATE_H_
|
#define SRC_NODE_ISOLATE_H_
|
||||||
|
|
||||||
#include "v8.h"
|
#include "v8.h"
|
||||||
|
#include "v8-debug.h"
|
||||||
#include "uv.h"
|
#include "uv.h"
|
||||||
#include "node_vars.h"
|
#include "node_vars.h"
|
||||||
|
#include "node_object_wrap.h"
|
||||||
#include "ngx-queue.h"
|
#include "ngx-queue.h"
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
@ -42,9 +44,15 @@
|
|||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
|
||||||
|
class Channel;
|
||||||
|
|
||||||
class IsolateWrap;
|
class IsolateWrap;
|
||||||
class IsolateChannel;
|
class IsolateChannel;
|
||||||
class IsolateMessage;
|
class IsolateMessage;
|
||||||
|
class IsolateDebugger;
|
||||||
|
class IsolateDebuggerMessage;
|
||||||
|
|
||||||
class Isolate {
|
class Isolate {
|
||||||
public:
|
public:
|
||||||
@ -52,6 +60,13 @@ public:
|
|||||||
int argc_;
|
int argc_;
|
||||||
uv_thread_t tid_;
|
uv_thread_t tid_;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kNone,
|
||||||
|
kDebug,
|
||||||
|
kDebugBrk
|
||||||
|
} debug_state;
|
||||||
|
IsolateDebugger* debugger_instance;
|
||||||
|
|
||||||
// Call this before instantiating any Isolate
|
// Call this before instantiating any Isolate
|
||||||
static void Initialize();
|
static void Initialize();
|
||||||
static int Count();
|
static int Count();
|
||||||
@ -125,6 +140,44 @@ private:
|
|||||||
bool globals_init_;
|
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
|
} // namespace node
|
||||||
|
|
||||||
#endif // SRC_NODE_ISOLATE_H_
|
#endif // SRC_NODE_ISOLATE_H_
|
||||||
|
@ -171,6 +171,9 @@ struct globals {
|
|||||||
v8::Persistent<v8::FunctionTemplate> wrapped_context_constructor;
|
v8::Persistent<v8::FunctionTemplate> wrapped_context_constructor;
|
||||||
v8::Persistent<v8::FunctionTemplate> wrapped_script_constructor;
|
v8::Persistent<v8::FunctionTemplate> wrapped_script_constructor;
|
||||||
|
|
||||||
|
// node_isolate.cc
|
||||||
|
v8::Persistent<v8::FunctionTemplate> isolate_debugger_constructor;
|
||||||
|
|
||||||
// node_signal_watcher.cc
|
// node_signal_watcher.cc
|
||||||
v8::Persistent<v8::String> callback_symbol;
|
v8::Persistent<v8::String> callback_symbol;
|
||||||
v8::Persistent<v8::FunctionTemplate> signal_watcher_constructor_template;
|
v8::Persistent<v8::FunctionTemplate> signal_watcher_constructor_template;
|
||||||
|
@ -5,7 +5,20 @@ var isolates = process.binding('isolates');
|
|||||||
console.log("count: %d", isolates.count());
|
console.log("count: %d", isolates.count());
|
||||||
|
|
||||||
if (process.tid === 1) {
|
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() {
|
isolate.onmessage = function() {
|
||||||
console.error("onmessage");
|
console.error("onmessage");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user