IsolateDebugger C++
This commit is contained in:
parent
44e7033279
commit
99679c6430
17
src/node.cc
17
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<Object> process_l = SetupProcessObject(argc, argv);
|
||||
|
@ -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>
|
||||
@ -31,11 +32,16 @@
|
||||
#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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 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_
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user