deps: re-implement debugger-agent
Reviewed-By: Trevor Norris <trevnorris@gmail.com> PR-URL: https://github.com/joyent/node/pull/8476
This commit is contained in:
parent
d71dd638c6
commit
6a610a0f67
2
Makefile
2
Makefile
@ -406,7 +406,7 @@ CPPLINT_EXCLUDE += src/queue.h
|
||||
CPPLINT_EXCLUDE += src/tree.h
|
||||
CPPLINT_EXCLUDE += src/v8abbr.h
|
||||
|
||||
CPPLINT_FILES = $(filter-out $(CPPLINT_EXCLUDE), $(wildcard src/*.cc src/*.h src/*.c tools/icu/*.h tools/icu/*.cc))
|
||||
CPPLINT_FILES = $(filter-out $(CPPLINT_EXCLUDE), $(wildcard src/*.cc src/*.h src/*.c tools/icu/*.h tools/icu/*.cc deps/debugger-agent/include/* deps/debugger-agent/src/*))
|
||||
|
||||
cpplint:
|
||||
@$(PYTHON) tools/cpplint.py $(CPPLINT_FILES)
|
||||
|
24
deps/debugger-agent/debugger-agent.gyp
vendored
Normal file
24
deps/debugger-agent/debugger-agent.gyp
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"targets": [{
|
||||
"target_name": "debugger-agent",
|
||||
"type": "<(library)",
|
||||
"include_dirs": [
|
||||
"src",
|
||||
"include",
|
||||
"../v8/include",
|
||||
"../uv/include",
|
||||
|
||||
# Private node.js folder and stuff needed to include from it
|
||||
"../../src",
|
||||
"../cares/include",
|
||||
],
|
||||
"direct_dependent_settings": {
|
||||
"include_dirs": [
|
||||
"include",
|
||||
],
|
||||
},
|
||||
"sources": [
|
||||
"src/agent.cc",
|
||||
],
|
||||
}],
|
||||
}
|
109
deps/debugger-agent/include/debugger-agent.h
vendored
Normal file
109
deps/debugger-agent/include/debugger-agent.h
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
// Copyright Fedor Indutny and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#ifndef DEPS_DEBUGGER_AGENT_INCLUDE_DEBUGGER_AGENT_H_
|
||||
#define DEPS_DEBUGGER_AGENT_INCLUDE_DEBUGGER_AGENT_H_
|
||||
|
||||
#include "uv.h"
|
||||
#include "v8.h"
|
||||
#include "v8-debug.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
// Forward declaration
|
||||
class Environment;
|
||||
|
||||
namespace debugger {
|
||||
|
||||
// Forward declaration
|
||||
class AgentMessage;
|
||||
|
||||
class Agent {
|
||||
public:
|
||||
explicit Agent(node::Environment* env);
|
||||
~Agent();
|
||||
|
||||
typedef void (*DispatchHandler)(node::Environment* env);
|
||||
|
||||
// Start the debugger agent thread
|
||||
bool Start(int port, bool wait);
|
||||
// Listen for debug events
|
||||
void Enable();
|
||||
// Stop the debugger agent
|
||||
void Stop();
|
||||
|
||||
inline void set_dispatch_handler(DispatchHandler handler) {
|
||||
dispatch_handler_ = handler;
|
||||
}
|
||||
|
||||
inline node::Environment* parent_env() const { return parent_env_; }
|
||||
inline node::Environment* child_env() const { return child_env_; }
|
||||
|
||||
protected:
|
||||
void InitAdaptor(Environment* env);
|
||||
|
||||
// Worker body
|
||||
void WorkerRun();
|
||||
|
||||
static void ThreadCb(Agent* agent);
|
||||
static void ParentSignalCb(uv_async_t* signal);
|
||||
static void ChildSignalCb(uv_async_t* signal);
|
||||
static void MessageHandler(const v8::Debug::Message& message);
|
||||
|
||||
// V8 API
|
||||
static Agent* Unwrap(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void NotifyListen(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void NotifyWait(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SendCommand(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
void EnqueueMessage(AgentMessage* message);
|
||||
|
||||
enum State {
|
||||
kNone,
|
||||
kRunning
|
||||
};
|
||||
|
||||
// TODO(indutny): Verify that there are no races
|
||||
State state_;
|
||||
|
||||
int port_;
|
||||
bool wait_;
|
||||
|
||||
uv_sem_t start_sem_;
|
||||
uv_mutex_t message_mutex_;
|
||||
uv_async_t child_signal_;
|
||||
|
||||
uv_thread_t thread_;
|
||||
node::Environment* parent_env_;
|
||||
node::Environment* child_env_;
|
||||
uv_loop_t child_loop_;
|
||||
v8::Persistent<v8::Object> api_;
|
||||
|
||||
// QUEUE
|
||||
void* messages_[2];
|
||||
|
||||
DispatchHandler dispatch_handler_;
|
||||
};
|
||||
|
||||
} // namespace debugger
|
||||
} // namespace node
|
||||
|
||||
#endif // DEPS_DEBUGGER_AGENT_INCLUDE_DEBUGGER_AGENT_H_
|
191
deps/debugger-agent/lib/_debugger_agent.js
vendored
Normal file
191
deps/debugger-agent/lib/_debugger_agent.js
vendored
Normal file
@ -0,0 +1,191 @@
|
||||
var assert = require('assert');
|
||||
var net = require('net');
|
||||
var util = require('util');
|
||||
var Buffer = require('buffer').Buffer;
|
||||
|
||||
var Transform = require('stream').Transform;
|
||||
|
||||
exports.start = function start() {
|
||||
var agent = new Agent();
|
||||
|
||||
// Do not let `agent.listen()` request listening from cluster master
|
||||
var cluster = require('cluster');
|
||||
cluster.isWorker = false;
|
||||
cluster.isMaster = true;
|
||||
|
||||
agent.on('error', function(err) {
|
||||
process._rawDebug(err.stack || err);
|
||||
});
|
||||
|
||||
agent.listen(process._debugAPI.port, function() {
|
||||
var addr = this.address();
|
||||
process._rawDebug('Debugger listening on port %d', addr.port);
|
||||
process._debugAPI.notifyListen();
|
||||
});
|
||||
|
||||
// Just to spin-off events
|
||||
// TODO(indutny): Figure out why node.cc isn't doing this
|
||||
setImmediate(function() {
|
||||
});
|
||||
|
||||
process._debugAPI.onclose = function() {
|
||||
// We don't care about it, but it prevents loop from cleaning up gently
|
||||
// NOTE: removeAllListeners won't work, as it doesn't call `removeListener`
|
||||
process.listeners('SIGWINCH').forEach(function(fn) {
|
||||
process.removeListener('SIGWINCH', fn);
|
||||
});
|
||||
|
||||
agent.close();
|
||||
};
|
||||
|
||||
// Not used now, but anyway
|
||||
return agent;
|
||||
};
|
||||
|
||||
function Agent() {
|
||||
net.Server.call(this, this.onConnection);
|
||||
|
||||
this.first = true;
|
||||
this.binding = process._debugAPI;
|
||||
|
||||
var self = this;
|
||||
this.binding.onmessage = function(msg) {
|
||||
self.clients.forEach(function(client) {
|
||||
client.send({}, msg);
|
||||
});
|
||||
};
|
||||
|
||||
this.clients = [];
|
||||
assert(this.binding, 'Debugger agent running without bindings!');
|
||||
}
|
||||
util.inherits(Agent, net.Server);
|
||||
|
||||
Agent.prototype.onConnection = function onConnection(socket) {
|
||||
var c = new Client(this, socket);
|
||||
|
||||
c.start();
|
||||
this.clients.push(c);
|
||||
|
||||
var self = this;
|
||||
c.once('close', function() {
|
||||
var index = self.clients.indexOf(c);
|
||||
assert(index !== -1);
|
||||
self.clients.splice(index, 1);
|
||||
});
|
||||
};
|
||||
|
||||
Agent.prototype.notifyWait = function notifyWait() {
|
||||
if (this.first)
|
||||
this.binding.notifyWait();
|
||||
this.first = false;
|
||||
};
|
||||
|
||||
function Client(agent, socket) {
|
||||
Transform.call(this);
|
||||
this._readableState.objectMode = true;
|
||||
|
||||
this.agent = agent;
|
||||
this.binding = this.agent.binding;
|
||||
this.socket = socket;
|
||||
|
||||
// Parse incoming data
|
||||
this.state = 'headers';
|
||||
this.headers = {};
|
||||
this.buffer = '';
|
||||
socket.pipe(this);
|
||||
|
||||
this.on('data', this.onCommand);
|
||||
|
||||
var self = this;
|
||||
this.socket.on('close', function() {
|
||||
self.destroy();
|
||||
});
|
||||
}
|
||||
util.inherits(Client, Transform);
|
||||
|
||||
Client.prototype.destroy = function destroy(msg) {
|
||||
this.socket.destroy();
|
||||
|
||||
this.emit('close');
|
||||
};
|
||||
|
||||
Client.prototype._transform = function _transform(data, enc, cb) {
|
||||
cb();
|
||||
|
||||
this.buffer += data;
|
||||
|
||||
while (true) {
|
||||
if (this.state === 'headers') {
|
||||
// Not enough data
|
||||
if (!/\r\n/.test(this.buffer))
|
||||
break;
|
||||
|
||||
if (/^\r\n/.test(this.buffer)) {
|
||||
this.buffer = this.buffer.slice(2);
|
||||
this.state = 'body';
|
||||
continue;
|
||||
}
|
||||
|
||||
// Match:
|
||||
// Header-name: header-value\r\n
|
||||
var match = this.buffer.match(/^([^:\s\r\n]+)\s*:\s*([^\s\r\n]+)\r\n/);
|
||||
if (!match)
|
||||
return this.destroy('Expected header, but failed to parse it');
|
||||
|
||||
this.headers[match[1].toLowerCase()] = match[2];
|
||||
|
||||
this.buffer = this.buffer.slice(match[0].length);
|
||||
} else {
|
||||
var len = this.headers['content-length'];
|
||||
if (len === undefined)
|
||||
return this.destroy('Expected content-length');
|
||||
|
||||
len = len | 0;
|
||||
if (Buffer.byteLength(this.buffer) < len)
|
||||
break;
|
||||
|
||||
this.push(new Command(this.headers, this.buffer.slice(0, len)));
|
||||
this.state = 'headers';
|
||||
this.buffer = this.buffer.slice(len);
|
||||
this.headers = {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Client.prototype.send = function send(headers, data) {
|
||||
if (!data)
|
||||
data = '';
|
||||
|
||||
var out = [];
|
||||
Object.keys(headers).forEach(function(key) {
|
||||
out.push(key + ': ' + headers[key]);
|
||||
});
|
||||
out.push('Content-Length: ' + Buffer.byteLength(data), '');
|
||||
|
||||
this.socket.cork();
|
||||
this.socket.write(out.join('\r\n') + '\r\n');
|
||||
|
||||
if (data.length > 0)
|
||||
this.socket.write(data);
|
||||
this.socket.uncork();
|
||||
};
|
||||
|
||||
Client.prototype.start = function start() {
|
||||
this.send({
|
||||
Type: 'connect',
|
||||
'V8-Version': process.versions.v8,
|
||||
'Protocol-Version': 1,
|
||||
'Embedding-Host': 'node ' + process.version
|
||||
});
|
||||
};
|
||||
|
||||
Client.prototype.onCommand = function onCommand(cmd) {
|
||||
this.binding.sendCommand(cmd.body);
|
||||
|
||||
this.agent.notifyWait();
|
||||
};
|
||||
|
||||
function Command(headers, body) {
|
||||
this.headers = headers;
|
||||
this.body = body;
|
||||
}
|
347
deps/debugger-agent/src/agent.cc
vendored
Normal file
347
deps/debugger-agent/src/agent.cc
vendored
Normal file
@ -0,0 +1,347 @@
|
||||
// Copyright Fedor Indutny and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "agent.h"
|
||||
#include "debugger-agent.h"
|
||||
|
||||
#include "node.h"
|
||||
#include "node_internals.h" // ARRAY_SIZE
|
||||
#include "env.h"
|
||||
#include "env-inl.h"
|
||||
#include "v8.h"
|
||||
#include "v8-debug.h"
|
||||
#include "util.h"
|
||||
#include "util-inl.h"
|
||||
#include "queue.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace node {
|
||||
namespace debugger {
|
||||
|
||||
using v8::Context;
|
||||
using v8::Function;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::FunctionTemplate;
|
||||
using v8::Handle;
|
||||
using v8::HandleScope;
|
||||
using v8::Integer;
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::Locker;
|
||||
using v8::Object;
|
||||
using v8::String;
|
||||
using v8::Value;
|
||||
|
||||
|
||||
Agent::Agent(Environment* env) : state_(kNone),
|
||||
port_(5858),
|
||||
wait_(false),
|
||||
parent_env_(env),
|
||||
child_env_(NULL),
|
||||
dispatch_handler_(NULL) {
|
||||
int err;
|
||||
|
||||
err = uv_sem_init(&start_sem_, 0);
|
||||
CHECK_EQ(err, 0);
|
||||
|
||||
err = uv_mutex_init(&message_mutex_);
|
||||
CHECK_EQ(err, 0);
|
||||
|
||||
QUEUE_INIT(&messages_);
|
||||
}
|
||||
|
||||
|
||||
Agent::~Agent() {
|
||||
Stop();
|
||||
|
||||
uv_sem_destroy(&start_sem_);
|
||||
uv_mutex_destroy(&message_mutex_);
|
||||
|
||||
// Clean-up messages
|
||||
while (!QUEUE_EMPTY(&messages_)) {
|
||||
QUEUE* q = QUEUE_HEAD(&messages_);
|
||||
QUEUE_REMOVE(q);
|
||||
AgentMessage* msg = ContainerOf(&AgentMessage::member, q);
|
||||
delete msg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Agent::Start(int port, bool wait) {
|
||||
int err;
|
||||
|
||||
if (state_ == kRunning)
|
||||
return false;
|
||||
|
||||
err = uv_loop_init(&child_loop_);
|
||||
if (err != 0)
|
||||
goto loop_init_failed;
|
||||
|
||||
// Interruption signal handler
|
||||
err = uv_async_init(&child_loop_, &child_signal_, ChildSignalCb);
|
||||
if (err != 0)
|
||||
goto async_init_failed;
|
||||
uv_unref(reinterpret_cast<uv_handle_t*>(&child_signal_));
|
||||
|
||||
port_ = port;
|
||||
wait_ = wait;
|
||||
|
||||
err = uv_thread_create(&thread_,
|
||||
reinterpret_cast<uv_thread_cb>(ThreadCb),
|
||||
this);
|
||||
if (err != 0)
|
||||
goto thread_create_failed;
|
||||
|
||||
uv_sem_wait(&start_sem_);
|
||||
|
||||
state_ = kRunning;
|
||||
|
||||
return true;
|
||||
|
||||
thread_create_failed:
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&child_signal_), NULL);
|
||||
|
||||
async_init_failed:
|
||||
err = uv_loop_close(&child_loop_);
|
||||
CHECK_EQ(err, 0);
|
||||
|
||||
loop_init_failed:
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void Agent::Enable() {
|
||||
v8::Debug::SetMessageHandler(MessageHandler);
|
||||
|
||||
// Assign environment to the debugger's context
|
||||
// NOTE: The debugger context is created after `SetMessageHandler()` call
|
||||
parent_env()->AssignToContext(v8::Debug::GetDebugContext());
|
||||
}
|
||||
|
||||
|
||||
void Agent::Stop() {
|
||||
int err;
|
||||
|
||||
if (state_ != kRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
v8::Debug::SetMessageHandler(NULL);
|
||||
|
||||
// Send empty message to terminate things
|
||||
EnqueueMessage(new AgentMessage(NULL, 0));
|
||||
|
||||
// Signal worker thread to make it stop
|
||||
err = uv_async_send(&child_signal_);
|
||||
CHECK_EQ(err, 0);
|
||||
|
||||
err = uv_thread_join(&thread_);
|
||||
CHECK_EQ(err, 0);
|
||||
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&child_signal_), NULL);
|
||||
uv_run(&child_loop_, UV_RUN_NOWAIT);
|
||||
|
||||
err = uv_loop_close(&child_loop_);
|
||||
CHECK_EQ(err, 0);
|
||||
|
||||
state_ = kNone;
|
||||
}
|
||||
|
||||
|
||||
void Agent::WorkerRun() {
|
||||
static const char* argv[] = { "node", "--debug-agent" };
|
||||
Isolate* isolate = Isolate::New();
|
||||
{
|
||||
Locker locker(isolate);
|
||||
Isolate::Scope isolate_scope(isolate);
|
||||
|
||||
HandleScope handle_scope(isolate);
|
||||
Local<Context> context = Context::New(isolate);
|
||||
|
||||
Context::Scope context_scope(context);
|
||||
Environment* env = CreateEnvironment(
|
||||
isolate,
|
||||
&child_loop_,
|
||||
context,
|
||||
ARRAY_SIZE(argv),
|
||||
argv,
|
||||
ARRAY_SIZE(argv),
|
||||
argv);
|
||||
|
||||
child_env_ = env;
|
||||
|
||||
// Expose API
|
||||
InitAdaptor(env);
|
||||
LoadEnvironment(env);
|
||||
|
||||
CHECK_EQ(&child_loop_, env->event_loop());
|
||||
uv_run(&child_loop_, UV_RUN_DEFAULT);
|
||||
|
||||
// Clean-up peristent
|
||||
api_.Reset();
|
||||
|
||||
// Clean-up all running handles
|
||||
env->CleanupHandles();
|
||||
|
||||
env->Dispose();
|
||||
env = NULL;
|
||||
}
|
||||
isolate->Dispose();
|
||||
}
|
||||
|
||||
|
||||
void Agent::InitAdaptor(Environment* env) {
|
||||
Isolate* isolate = env->isolate();
|
||||
HandleScope scope(isolate);
|
||||
|
||||
// Create API adaptor
|
||||
Local<FunctionTemplate> t = FunctionTemplate::New(isolate);
|
||||
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
t->SetClassName(String::NewFromUtf8(isolate, "DebugAPI"));
|
||||
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "notifyListen", NotifyListen);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "notifyWait", NotifyWait);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "sendCommand", SendCommand);
|
||||
|
||||
Local<Object> api = t->GetFunction()->NewInstance();
|
||||
api->SetAlignedPointerInInternalField(0, this);
|
||||
|
||||
api->Set(String::NewFromUtf8(isolate, "port"), Integer::New(isolate, port_));
|
||||
|
||||
env->process_object()->Set(String::NewFromUtf8(isolate, "_debugAPI"), api);
|
||||
api_.Reset(env->isolate(), api);
|
||||
}
|
||||
|
||||
|
||||
Agent* Agent::Unwrap(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
void* ptr = args.Holder()->GetAlignedPointerFromInternalField(0);
|
||||
return reinterpret_cast<Agent*>(ptr);
|
||||
}
|
||||
|
||||
|
||||
void Agent::NotifyListen(const FunctionCallbackInfo<Value>& args) {
|
||||
Agent* a = Unwrap(args);
|
||||
|
||||
// Notify other thread that we are ready to process events
|
||||
uv_sem_post(&a->start_sem_);
|
||||
}
|
||||
|
||||
|
||||
void Agent::NotifyWait(const FunctionCallbackInfo<Value>& args) {
|
||||
Agent* a = Unwrap(args);
|
||||
|
||||
a->wait_ = false;
|
||||
|
||||
int err = uv_async_send(&a->child_signal_);
|
||||
CHECK_EQ(err, 0);
|
||||
}
|
||||
|
||||
|
||||
void Agent::SendCommand(const FunctionCallbackInfo<Value>& args) {
|
||||
Agent* a = Unwrap(args);
|
||||
Environment* env = a->child_env();
|
||||
HandleScope scope(env->isolate());
|
||||
|
||||
String::Value v(args[0]);
|
||||
|
||||
v8::Debug::SendCommand(a->parent_env()->isolate(), *v, v.length());
|
||||
if (a->dispatch_handler_ != NULL)
|
||||
a->dispatch_handler_(a->parent_env());
|
||||
}
|
||||
|
||||
|
||||
void Agent::ThreadCb(Agent* agent) {
|
||||
agent->WorkerRun();
|
||||
}
|
||||
|
||||
|
||||
void Agent::ChildSignalCb(uv_async_t* signal) {
|
||||
Agent* a = ContainerOf(&Agent::child_signal_, signal);
|
||||
Isolate* isolate = a->child_env()->isolate();
|
||||
|
||||
HandleScope scope(isolate);
|
||||
Local<Object> api = PersistentToLocal(isolate, a->api_);
|
||||
|
||||
uv_mutex_lock(&a->message_mutex_);
|
||||
while (!QUEUE_EMPTY(&a->messages_)) {
|
||||
QUEUE* q = QUEUE_HEAD(&a->messages_);
|
||||
AgentMessage* msg = ContainerOf(&AgentMessage::member, q);
|
||||
|
||||
// Time to close everything
|
||||
if (msg->data() == NULL) {
|
||||
QUEUE_REMOVE(q);
|
||||
delete msg;
|
||||
|
||||
MakeCallback(isolate, api, "onclose", 0, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
// Waiting for client, do not send anything just yet
|
||||
// TODO(indutny): move this to js-land
|
||||
if (a->wait_)
|
||||
break;
|
||||
|
||||
QUEUE_REMOVE(q);
|
||||
Local<Value> argv[] = {
|
||||
String::NewFromTwoByte(isolate,
|
||||
msg->data(),
|
||||
String::kNormalString,
|
||||
msg->length())
|
||||
};
|
||||
|
||||
// Emit message
|
||||
MakeCallback(isolate,
|
||||
api,
|
||||
"onmessage",
|
||||
ARRAY_SIZE(argv),
|
||||
argv);
|
||||
delete msg;
|
||||
}
|
||||
uv_mutex_unlock(&a->message_mutex_);
|
||||
}
|
||||
|
||||
|
||||
void Agent::EnqueueMessage(AgentMessage* message) {
|
||||
uv_mutex_lock(&message_mutex_);
|
||||
QUEUE_INSERT_TAIL(&messages_, &message->member);
|
||||
uv_mutex_unlock(&message_mutex_);
|
||||
uv_async_send(&child_signal_);
|
||||
}
|
||||
|
||||
|
||||
void Agent::MessageHandler(const v8::Debug::Message& message) {
|
||||
Isolate* isolate = message.GetIsolate();
|
||||
Environment* env = Environment::GetCurrent(isolate);
|
||||
Agent* a = env->debugger_agent();
|
||||
CHECK_NE(a, NULL);
|
||||
CHECK_EQ(isolate, a->parent_env()->isolate());
|
||||
|
||||
HandleScope scope(isolate);
|
||||
Local<String> json = message.GetJSON();
|
||||
String::Value v(json);
|
||||
|
||||
AgentMessage* msg = new AgentMessage(*v, v.length());
|
||||
a->EnqueueMessage(msg);
|
||||
}
|
||||
|
||||
} // namespace debugger
|
||||
} // namespace node
|
64
deps/debugger-agent/src/agent.h
vendored
Normal file
64
deps/debugger-agent/src/agent.h
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright Fedor Indutny and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#ifndef DEPS_DEBUGGER_AGENT_SRC_AGENT_H_
|
||||
#define DEPS_DEBUGGER_AGENT_SRC_AGENT_H_
|
||||
|
||||
#include "v8.h"
|
||||
#include "v8-debug.h"
|
||||
#include "queue.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace node {
|
||||
namespace debugger {
|
||||
|
||||
class AgentMessage {
|
||||
public:
|
||||
AgentMessage(uint16_t* val, int length) : length_(length) {
|
||||
if (val == NULL) {
|
||||
data_ = val;
|
||||
} else {
|
||||
data_ = new uint16_t[length];
|
||||
memcpy(data_, val, length * sizeof(*data_));
|
||||
}
|
||||
}
|
||||
|
||||
~AgentMessage() {
|
||||
delete[] data_;
|
||||
data_ = NULL;
|
||||
}
|
||||
|
||||
inline const uint16_t* data() const { return data_; }
|
||||
inline int length() const { return length_; }
|
||||
|
||||
QUEUE member;
|
||||
|
||||
private:
|
||||
uint16_t* data_;
|
||||
int length_;
|
||||
};
|
||||
|
||||
} // namespace debugger
|
||||
} // namespace node
|
||||
|
||||
#endif // DEPS_DEBUGGER_AGENT_SRC_AGENT_H_
|
@ -249,6 +249,10 @@ Client.prototype._onResponse = function(res) {
|
||||
this._removeScript(res.body.body.script);
|
||||
handled = true;
|
||||
|
||||
} else if (res.body && res.body.event === 'compileError') {
|
||||
// This event is not used anywhere right now, perhaps somewhere in the
|
||||
// future?
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (cb) {
|
||||
|
2
node.gyp
2
node.gyp
@ -67,6 +67,7 @@
|
||||
'lib/util.js',
|
||||
'lib/vm.js',
|
||||
'lib/zlib.js',
|
||||
'deps/debugger-agent/lib/_debugger_agent.js',
|
||||
],
|
||||
},
|
||||
|
||||
@ -77,6 +78,7 @@
|
||||
|
||||
'dependencies': [
|
||||
'node_js2c#host',
|
||||
'deps/debugger-agent/debugger-agent.gyp:debugger-agent',
|
||||
],
|
||||
|
||||
'include_dirs': [
|
||||
|
@ -1200,6 +1200,20 @@ static void StrError(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
|
||||
static void CaresTimerCloseCb(uv_handle_t* handle) {
|
||||
Environment* env = Environment::from_cares_timer_handle(
|
||||
reinterpret_cast<uv_timer_t*>(handle));
|
||||
env->FinishHandleCleanup(handle);
|
||||
}
|
||||
|
||||
|
||||
static void CaresTimerClose(Environment* env,
|
||||
uv_handle_t* handle,
|
||||
void* arg) {
|
||||
uv_close(handle, CaresTimerCloseCb);
|
||||
}
|
||||
|
||||
|
||||
static void Initialize(Handle<Object> target,
|
||||
Handle<Value> unused,
|
||||
Handle<Context> context) {
|
||||
@ -1223,6 +1237,10 @@ static void Initialize(Handle<Object> target,
|
||||
/* Initialize the timeout timer. The timer won't be started until the */
|
||||
/* first socket is opened. */
|
||||
uv_timer_init(env->event_loop(), env->cares_timer_handle());
|
||||
env->RegisterHandleCleanup(
|
||||
reinterpret_cast<uv_handle_t*>(env->cares_timer_handle()),
|
||||
CaresTimerClose,
|
||||
NULL);
|
||||
|
||||
NODE_SET_METHOD(target, "queryA", Query<QueryAWrap>);
|
||||
NODE_SET_METHOD(target, "queryAaaa", Query<QueryAaaaWrap>);
|
||||
|
@ -74,10 +74,10 @@ inline Environment::IsolateData* Environment::IsolateData::Get(
|
||||
}
|
||||
|
||||
inline Environment::IsolateData* Environment::IsolateData::GetOrCreate(
|
||||
v8::Isolate* isolate) {
|
||||
v8::Isolate* isolate, uv_loop_t* loop) {
|
||||
IsolateData* isolate_data = Get(isolate);
|
||||
if (isolate_data == NULL) {
|
||||
isolate_data = new IsolateData(isolate);
|
||||
isolate_data = new IsolateData(isolate, loop);
|
||||
isolate->SetData(kIsolateSlot, isolate_data);
|
||||
}
|
||||
isolate_data->ref_count_ += 1;
|
||||
@ -91,8 +91,9 @@ inline void Environment::IsolateData::Put() {
|
||||
}
|
||||
}
|
||||
|
||||
inline Environment::IsolateData::IsolateData(v8::Isolate* isolate)
|
||||
: event_loop_(uv_default_loop()),
|
||||
inline Environment::IsolateData::IsolateData(v8::Isolate* isolate,
|
||||
uv_loop_t* loop)
|
||||
: event_loop_(loop),
|
||||
isolate_(isolate),
|
||||
#define V(PropertyName, StringValue) \
|
||||
PropertyName ## _(isolate, FIXED_ONE_BYTE_STRING(isolate, StringValue)),
|
||||
@ -188,8 +189,9 @@ inline void Environment::TickInfo::set_last_threw(bool value) {
|
||||
last_threw_ = value;
|
||||
}
|
||||
|
||||
inline Environment* Environment::New(v8::Local<v8::Context> context) {
|
||||
Environment* env = new Environment(context);
|
||||
inline Environment* Environment::New(v8::Local<v8::Context> context,
|
||||
uv_loop_t* loop) {
|
||||
Environment* env = new Environment(context, loop);
|
||||
env->AssignToContext(context);
|
||||
return env;
|
||||
}
|
||||
@ -207,12 +209,14 @@ inline Environment* Environment::GetCurrent(v8::Local<v8::Context> context) {
|
||||
context->GetAlignedPointerFromEmbedderData(kContextEmbedderDataIndex));
|
||||
}
|
||||
|
||||
inline Environment::Environment(v8::Local<v8::Context> context)
|
||||
inline Environment::Environment(v8::Local<v8::Context> context,
|
||||
uv_loop_t* loop)
|
||||
: isolate_(context->GetIsolate()),
|
||||
isolate_data_(IsolateData::GetOrCreate(context->GetIsolate())),
|
||||
isolate_data_(IsolateData::GetOrCreate(context->GetIsolate(), loop)),
|
||||
using_smalloc_alloc_cb_(false),
|
||||
using_domains_(false),
|
||||
printed_error_(false),
|
||||
debugger_agent_(this),
|
||||
context_(context->GetIsolate(), context) {
|
||||
// We'll be creating new objects so make sure we've entered the context.
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
@ -221,6 +225,10 @@ inline Environment::Environment(v8::Local<v8::Context> context)
|
||||
set_module_load_list_array(v8::Array::New(isolate()));
|
||||
RB_INIT(&cares_task_list_);
|
||||
QUEUE_INIT(&gc_tracker_queue_);
|
||||
QUEUE_INIT(&req_wrap_queue_);
|
||||
QUEUE_INIT(&handle_wrap_queue_);
|
||||
QUEUE_INIT(&handle_cleanup_queue_);
|
||||
handle_cleanup_waiting_ = 0;
|
||||
}
|
||||
|
||||
inline Environment::~Environment() {
|
||||
@ -233,6 +241,21 @@ inline Environment::~Environment() {
|
||||
isolate_data()->Put();
|
||||
}
|
||||
|
||||
inline void Environment::CleanupHandles() {
|
||||
while (!QUEUE_EMPTY(&handle_cleanup_queue_)) {
|
||||
QUEUE* q = QUEUE_HEAD(&handle_cleanup_queue_);
|
||||
QUEUE_REMOVE(q);
|
||||
|
||||
HandleCleanup* hc = ContainerOf(&HandleCleanup::handle_cleanup_queue_, q);
|
||||
handle_cleanup_waiting_++;
|
||||
hc->cb_(this, hc->handle_, hc->arg_);
|
||||
delete hc;
|
||||
}
|
||||
|
||||
while (handle_cleanup_waiting_ != 0)
|
||||
uv_run(event_loop(), UV_RUN_ONCE);
|
||||
}
|
||||
|
||||
inline void Environment::Dispose() {
|
||||
delete this;
|
||||
}
|
||||
@ -287,6 +310,17 @@ inline uv_check_t* Environment::idle_check_handle() {
|
||||
return &idle_check_handle_;
|
||||
}
|
||||
|
||||
inline void Environment::RegisterHandleCleanup(uv_handle_t* handle,
|
||||
HandleCleanupCb cb,
|
||||
void *arg) {
|
||||
HandleCleanup* hc = new HandleCleanup(handle, cb, arg);
|
||||
QUEUE_INSERT_TAIL(&handle_cleanup_queue_, &hc->handle_cleanup_queue_);
|
||||
}
|
||||
|
||||
inline void Environment::FinishHandleCleanup(uv_handle_t* handle) {
|
||||
handle_cleanup_waiting_--;
|
||||
}
|
||||
|
||||
inline uv_loop_t* Environment::event_loop() const {
|
||||
return isolate_data()->event_loop();
|
||||
}
|
||||
|
52
src/env.h
52
src/env.h
@ -28,6 +28,7 @@
|
||||
#include "uv.h"
|
||||
#include "v8.h"
|
||||
#include "queue.h"
|
||||
#include "debugger-agent.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@ -356,11 +357,34 @@ class Environment {
|
||||
DISALLOW_COPY_AND_ASSIGN(TickInfo);
|
||||
};
|
||||
|
||||
typedef void (*HandleCleanupCb)(Environment* env,
|
||||
uv_handle_t* handle,
|
||||
void* arg);
|
||||
|
||||
class HandleCleanup {
|
||||
private:
|
||||
friend class Environment;
|
||||
|
||||
HandleCleanup(uv_handle_t* handle, HandleCleanupCb cb, void* arg)
|
||||
: handle_(handle),
|
||||
cb_(cb),
|
||||
arg_(arg) {
|
||||
QUEUE_INIT(&handle_cleanup_queue_);
|
||||
}
|
||||
|
||||
uv_handle_t* handle_;
|
||||
HandleCleanupCb cb_;
|
||||
void* arg_;
|
||||
QUEUE handle_cleanup_queue_;
|
||||
};
|
||||
|
||||
static inline Environment* GetCurrent(v8::Isolate* isolate);
|
||||
static inline Environment* GetCurrent(v8::Local<v8::Context> context);
|
||||
|
||||
// See CreateEnvironment() in src/node.cc.
|
||||
static inline Environment* New(v8::Local<v8::Context> context);
|
||||
static inline Environment* New(v8::Local<v8::Context> context,
|
||||
uv_loop_t* loop);
|
||||
inline void CleanupHandles();
|
||||
inline void Dispose();
|
||||
|
||||
// Defined in src/node_profiler.cc.
|
||||
@ -385,6 +409,12 @@ class Environment {
|
||||
static inline Environment* from_idle_check_handle(uv_check_t* handle);
|
||||
inline uv_check_t* idle_check_handle();
|
||||
|
||||
// Register clean-up cb to be called on env->Dispose()
|
||||
inline void RegisterHandleCleanup(uv_handle_t* handle,
|
||||
HandleCleanupCb cb,
|
||||
void *arg);
|
||||
inline void FinishHandleCleanup(uv_handle_t* handle);
|
||||
|
||||
inline AsyncListener* async_listener();
|
||||
inline DomainFlag* domain_flag();
|
||||
inline TickInfo* tick_info();
|
||||
@ -434,12 +464,19 @@ class Environment {
|
||||
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
|
||||
#undef V
|
||||
|
||||
inline debugger::Agent* debugger_agent() {
|
||||
return &debugger_agent_;
|
||||
}
|
||||
|
||||
inline QUEUE* handle_wrap_queue() { return &handle_wrap_queue_; }
|
||||
inline QUEUE* req_wrap_queue() { return &req_wrap_queue_; }
|
||||
|
||||
private:
|
||||
static const int kIsolateSlot = NODE_ISOLATE_SLOT;
|
||||
|
||||
class GCInfo;
|
||||
class IsolateData;
|
||||
inline explicit Environment(v8::Local<v8::Context> context);
|
||||
inline Environment(v8::Local<v8::Context> context, uv_loop_t* loop);
|
||||
inline ~Environment();
|
||||
inline IsolateData* isolate_data() const;
|
||||
void AfterGarbageCollectionCallback(const GCInfo* before,
|
||||
@ -465,6 +502,12 @@ class Environment {
|
||||
bool using_domains_;
|
||||
QUEUE gc_tracker_queue_;
|
||||
bool printed_error_;
|
||||
debugger::Agent debugger_agent_;
|
||||
|
||||
QUEUE handle_wrap_queue_;
|
||||
QUEUE req_wrap_queue_;
|
||||
QUEUE handle_cleanup_queue_;
|
||||
int handle_cleanup_waiting_;
|
||||
|
||||
#define V(PropertyName, TypeName) \
|
||||
v8::Persistent<TypeName> PropertyName ## _;
|
||||
@ -494,7 +537,8 @@ class Environment {
|
||||
// Per-thread, reference-counted singleton.
|
||||
class IsolateData {
|
||||
public:
|
||||
static inline IsolateData* GetOrCreate(v8::Isolate* isolate);
|
||||
static inline IsolateData* GetOrCreate(v8::Isolate* isolate,
|
||||
uv_loop_t* loop);
|
||||
inline void Put();
|
||||
inline uv_loop_t* event_loop() const;
|
||||
|
||||
@ -509,7 +553,7 @@ class Environment {
|
||||
|
||||
private:
|
||||
inline static IsolateData* Get(v8::Isolate* isolate);
|
||||
inline explicit IsolateData(v8::Isolate* isolate);
|
||||
inline explicit IsolateData(v8::Isolate* isolate, uv_loop_t* loop);
|
||||
inline v8::Isolate* isolate() const;
|
||||
|
||||
// Defined in src/node_profiler.cc.
|
||||
|
@ -39,9 +39,6 @@ using v8::Local;
|
||||
using v8::Object;
|
||||
using v8::Value;
|
||||
|
||||
// defined in node.cc
|
||||
extern QUEUE handle_wrap_queue;
|
||||
|
||||
|
||||
void HandleWrap::Ref(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args.GetIsolate());
|
||||
@ -100,7 +97,7 @@ HandleWrap::HandleWrap(Environment* env,
|
||||
handle__->data = this;
|
||||
HandleScope scope(env->isolate());
|
||||
Wrap<HandleWrap>(object, this);
|
||||
QUEUE_INSERT_TAIL(&handle_wrap_queue, &handle_wrap_queue_);
|
||||
QUEUE_INSERT_TAIL(env->handle_wrap_queue(), &handle_wrap_queue_);
|
||||
}
|
||||
|
||||
|
||||
|
200
src/node.cc
200
src/node.cc
@ -122,10 +122,6 @@ using v8::V8;
|
||||
using v8::Value;
|
||||
using v8::kExternalUint32Array;
|
||||
|
||||
// FIXME(bnoordhuis) Make these per-context?
|
||||
QUEUE handle_wrap_queue = { &handle_wrap_queue, &handle_wrap_queue };
|
||||
QUEUE req_wrap_queue = { &req_wrap_queue, &req_wrap_queue };
|
||||
|
||||
static bool print_eval = false;
|
||||
static bool force_repl = false;
|
||||
static bool trace_deprecation = false;
|
||||
@ -1554,13 +1550,14 @@ static Local<Value> ExecuteString(Environment* env,
|
||||
|
||||
|
||||
static void GetActiveRequests(const FunctionCallbackInfo<Value>& args) {
|
||||
HandleScope scope(args.GetIsolate());
|
||||
Environment* env = Environment::GetCurrent(args.GetIsolate());
|
||||
HandleScope scope(env->isolate());
|
||||
|
||||
Local<Array> ary = Array::New(args.GetIsolate());
|
||||
QUEUE* q = NULL;
|
||||
int i = 0;
|
||||
|
||||
QUEUE_FOREACH(q, &req_wrap_queue) {
|
||||
QUEUE_FOREACH(q, env->req_wrap_queue()) {
|
||||
ReqWrap<uv_req_t>* w = ContainerOf(&ReqWrap<uv_req_t>::req_wrap_queue_, q);
|
||||
if (w->persistent().IsEmpty())
|
||||
continue;
|
||||
@ -1583,7 +1580,7 @@ void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
Local<String> owner_sym = env->owner_string();
|
||||
|
||||
QUEUE_FOREACH(q, &handle_wrap_queue) {
|
||||
QUEUE_FOREACH(q, env->handle_wrap_queue()) {
|
||||
HandleWrap* w = ContainerOf(&HandleWrap::handle_wrap_queue_, q);
|
||||
if (w->persistent().IsEmpty() || (w->flags_ & HandleWrap::kUnref))
|
||||
continue;
|
||||
@ -1967,8 +1964,8 @@ static void Uptime(const FunctionCallbackInfo<Value>& args) {
|
||||
HandleScope scope(env->isolate());
|
||||
double uptime;
|
||||
|
||||
uv_update_time(uv_default_loop());
|
||||
uptime = uv_now(uv_default_loop()) - prog_start_time;
|
||||
uv_update_time(env->event_loop());
|
||||
uptime = uv_now(env->event_loop()) - prog_start_time;
|
||||
|
||||
args.GetReturnValue().Set(Number::New(env->isolate(), uptime / 1000));
|
||||
}
|
||||
@ -2860,9 +2857,12 @@ static void RawDebug(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
|
||||
void Load(Environment* env) {
|
||||
void LoadEnvironment(Environment* env) {
|
||||
HandleScope handle_scope(env->isolate());
|
||||
|
||||
V8::SetFatalErrorHandler(node::OnFatalError);
|
||||
V8::AddMessageListener(OnMessage);
|
||||
|
||||
// Compile, execute the src/node.js file. (Which was included as static C
|
||||
// string in node_natives.h. 'natve_node' is the string containing that
|
||||
// source code.)
|
||||
@ -3121,40 +3121,33 @@ static void ParseArgs(int* argc,
|
||||
|
||||
|
||||
// Called from V8 Debug Agent TCP thread.
|
||||
static void DispatchMessagesDebugAgentCallback() {
|
||||
static void DispatchMessagesDebugAgentCallback(Environment* env) {
|
||||
// TODO(indutny): move async handle to environment
|
||||
uv_async_send(&dispatch_debug_messages_async);
|
||||
}
|
||||
|
||||
|
||||
// Called from the main thread.
|
||||
static void EnableDebug(Isolate* isolate, bool wait_connect) {
|
||||
assert(debugger_running == false);
|
||||
Isolate::Scope isolate_scope(isolate);
|
||||
HandleScope handle_scope(isolate);
|
||||
v8::Debug::SetDebugMessageDispatchHandler(DispatchMessagesDebugAgentCallback,
|
||||
false);
|
||||
debugger_running = v8::Debug::EnableAgent("node " NODE_VERSION,
|
||||
debug_port,
|
||||
wait_connect);
|
||||
static void StartDebug(Environment* env, bool wait) {
|
||||
CHECK(!debugger_running);
|
||||
|
||||
env->debugger_agent()->set_dispatch_handler(
|
||||
DispatchMessagesDebugAgentCallback);
|
||||
debugger_running = env->debugger_agent()->Start(debug_port, wait);
|
||||
if (debugger_running == false) {
|
||||
fprintf(stderr, "Starting debugger on port %d failed\n", debug_port);
|
||||
fflush(stderr);
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "Debugger listening on port %d\n", debug_port);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
if (isolate == NULL)
|
||||
return; // Still starting up.
|
||||
Local<Context> context = isolate->GetCurrentContext();
|
||||
if (context.IsEmpty())
|
||||
return; // Still starting up.
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
|
||||
// Assign environment to the debugger's context
|
||||
env->AssignToContext(v8::Debug::GetDebugContext());
|
||||
// Called from the main thread.
|
||||
static void EnableDebug(Environment* env) {
|
||||
CHECK(debugger_running);
|
||||
|
||||
// Send message to enable debug in workers
|
||||
HandleScope handle_scope(env->isolate());
|
||||
|
||||
Context::Scope context_scope(env->context());
|
||||
Local<Object> message = Object::New(env->isolate());
|
||||
message->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "cmd"),
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), "NODE_DEBUG_ENABLED"));
|
||||
@ -3163,6 +3156,9 @@ static void EnableDebug(Isolate* isolate, bool wait_connect) {
|
||||
message
|
||||
};
|
||||
MakeCallback(env, env->process_object(), "emit", ARRAY_SIZE(argv), argv);
|
||||
|
||||
// Enabled debugger, possibly making it wait on a semaphore
|
||||
env->debugger_agent()->Enable();
|
||||
}
|
||||
|
||||
|
||||
@ -3170,7 +3166,12 @@ static void EnableDebug(Isolate* isolate, bool wait_connect) {
|
||||
static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle) {
|
||||
if (debugger_running == false) {
|
||||
fprintf(stderr, "Starting debugger agent.\n");
|
||||
EnableDebug(node_isolate, false);
|
||||
|
||||
Environment* env = Environment::GetCurrent(node_isolate);
|
||||
Context::Scope context_scope(env->context());
|
||||
|
||||
StartDebug(env, false);
|
||||
EnableDebug(env);
|
||||
}
|
||||
Isolate::Scope isolate_scope(node_isolate);
|
||||
v8::Debug::ProcessDebugMessages();
|
||||
@ -3399,7 +3400,8 @@ static void DebugPause(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
static void DebugEnd(const FunctionCallbackInfo<Value>& args) {
|
||||
if (debugger_running) {
|
||||
v8::Debug::DisableAgent();
|
||||
Environment* env = Environment::GetCurrent(args.GetIsolate());
|
||||
env->debugger_agent()->Stop();
|
||||
debugger_running = false;
|
||||
}
|
||||
}
|
||||
@ -3512,13 +3514,7 @@ void Init(int* argc,
|
||||
RegisterSignalHandler(SIGTERM, SignalExit, true);
|
||||
#endif // __POSIX__
|
||||
|
||||
V8::SetFatalErrorHandler(node::OnFatalError);
|
||||
V8::AddMessageListener(OnMessage);
|
||||
|
||||
// If the --debug flag was specified then initialize the debug thread.
|
||||
if (use_debug_agent) {
|
||||
EnableDebug(node_isolate, debug_wait_connect);
|
||||
} else {
|
||||
if (!use_debug_agent) {
|
||||
RegisterDebugSignalHandler();
|
||||
}
|
||||
}
|
||||
@ -3591,22 +3587,62 @@ int EmitExit(Environment* env) {
|
||||
}
|
||||
|
||||
|
||||
// Just a convenience method
|
||||
Environment* CreateEnvironment(Isolate* isolate,
|
||||
Handle<Context> context,
|
||||
int argc,
|
||||
const char* const* argv,
|
||||
int exec_argc,
|
||||
const char* const* exec_argv) {
|
||||
Environment* env;
|
||||
Context::Scope context_scope(context);
|
||||
|
||||
env = CreateEnvironment(isolate,
|
||||
uv_default_loop(),
|
||||
context,
|
||||
argc,
|
||||
argv,
|
||||
exec_argc,
|
||||
exec_argv);
|
||||
|
||||
LoadEnvironment(env);
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
|
||||
static void HandleCloseCb(uv_handle_t* handle) {
|
||||
Environment* env = reinterpret_cast<Environment*>(handle->data);
|
||||
env->FinishHandleCleanup(handle);
|
||||
}
|
||||
|
||||
|
||||
static void HandleCleanup(Environment* env,
|
||||
uv_handle_t* handle,
|
||||
void* arg) {
|
||||
handle->data = env;
|
||||
uv_close(handle, HandleCloseCb);
|
||||
}
|
||||
|
||||
|
||||
Environment* CreateEnvironment(Isolate* isolate,
|
||||
uv_loop_t* loop,
|
||||
Handle<Context> context,
|
||||
int argc,
|
||||
const char* const* argv,
|
||||
int exec_argc,
|
||||
const char* const* exec_argv) {
|
||||
HandleScope handle_scope(isolate);
|
||||
|
||||
Context::Scope context_scope(context);
|
||||
Environment* env = Environment::New(context);
|
||||
Environment* env = Environment::New(context, loop);
|
||||
|
||||
isolate->SetAutorunMicrotasks(false);
|
||||
|
||||
uv_check_init(env->event_loop(), env->immediate_check_handle());
|
||||
uv_unref(
|
||||
reinterpret_cast<uv_handle_t*>(env->immediate_check_handle()));
|
||||
|
||||
uv_idle_init(env->event_loop(), env->immediate_idle_handle());
|
||||
|
||||
// Inform V8's CPU profiler when we're idle. The profiler is sampling-based
|
||||
@ -3623,6 +3659,24 @@ Environment* CreateEnvironment(Isolate* isolate,
|
||||
uv_unref(reinterpret_cast<uv_handle_t*>(env->idle_prepare_handle()));
|
||||
uv_unref(reinterpret_cast<uv_handle_t*>(env->idle_check_handle()));
|
||||
|
||||
// Register handle cleanups
|
||||
env->RegisterHandleCleanup(
|
||||
reinterpret_cast<uv_handle_t*>(env->immediate_check_handle()),
|
||||
HandleCleanup,
|
||||
NULL);
|
||||
env->RegisterHandleCleanup(
|
||||
reinterpret_cast<uv_handle_t*>(env->immediate_idle_handle()),
|
||||
HandleCleanup,
|
||||
NULL);
|
||||
env->RegisterHandleCleanup(
|
||||
reinterpret_cast<uv_handle_t*>(env->idle_prepare_handle()),
|
||||
HandleCleanup,
|
||||
NULL);
|
||||
env->RegisterHandleCleanup(
|
||||
reinterpret_cast<uv_handle_t*>(env->idle_check_handle()),
|
||||
HandleCleanup,
|
||||
NULL);
|
||||
|
||||
if (v8_is_profiling) {
|
||||
StartProfilerIdleNotifier(env);
|
||||
}
|
||||
@ -3634,7 +3688,6 @@ Environment* CreateEnvironment(Isolate* isolate,
|
||||
env->set_process_object(process_object);
|
||||
|
||||
SetupProcessObject(env, argc, argv, exec_argc, exec_argv);
|
||||
Load(env);
|
||||
|
||||
return env;
|
||||
}
|
||||
@ -3676,34 +3729,41 @@ int Start(int argc, char** argv) {
|
||||
HandleScope handle_scope(node_isolate);
|
||||
Local<Context> context = Context::New(node_isolate);
|
||||
Environment* env = CreateEnvironment(
|
||||
node_isolate, context, argc, argv, exec_argc, exec_argv);
|
||||
// Assign env to the debugger's context
|
||||
if (debugger_running) {
|
||||
HandleScope scope(env->isolate());
|
||||
env->AssignToContext(v8::Debug::GetDebugContext());
|
||||
}
|
||||
// This Context::Scope is here so EnableDebug() can look up the current
|
||||
// environment with Environment::GetCurrent().
|
||||
// TODO(bnoordhuis) Reorder the debugger initialization logic so it can
|
||||
// be removed.
|
||||
{
|
||||
Context::Scope context_scope(env->context());
|
||||
bool more;
|
||||
do {
|
||||
more = uv_run(env->event_loop(), UV_RUN_ONCE);
|
||||
if (more == false) {
|
||||
EmitBeforeExit(env);
|
||||
node_isolate,
|
||||
uv_default_loop(),
|
||||
context,
|
||||
argc,
|
||||
argv,
|
||||
exec_argc,
|
||||
exec_argv);
|
||||
Context::Scope context_scope(context);
|
||||
|
||||
// Start debug agent when argv has --debug
|
||||
if (use_debug_agent)
|
||||
StartDebug(env, debug_wait_connect);
|
||||
|
||||
LoadEnvironment(env);
|
||||
|
||||
// Enable debugger
|
||||
if (use_debug_agent)
|
||||
EnableDebug(env);
|
||||
|
||||
bool more;
|
||||
do {
|
||||
more = uv_run(env->event_loop(), UV_RUN_ONCE);
|
||||
if (more == false) {
|
||||
EmitBeforeExit(env);
|
||||
|
||||
// Emit `beforeExit` if the loop became alive either after emitting
|
||||
// event, or after running some callbacks.
|
||||
more = uv_loop_alive(env->event_loop());
|
||||
if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0)
|
||||
more = true;
|
||||
}
|
||||
} while (more == true);
|
||||
code = EmitExit(env);
|
||||
RunAtExit(env);
|
||||
|
||||
// Emit `beforeExit` if the loop became alive either after emitting
|
||||
// event, or after running some callbacks.
|
||||
more = uv_loop_alive(env->event_loop());
|
||||
if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0)
|
||||
more = true;
|
||||
}
|
||||
} while (more == true);
|
||||
code = EmitExit(env);
|
||||
RunAtExit(env);
|
||||
}
|
||||
env->Dispose();
|
||||
env = NULL;
|
||||
}
|
||||
|
17
src/node.h
17
src/node.h
@ -63,6 +63,9 @@
|
||||
|
||||
#define NODE_DEPRECATED(msg, fn) V8_DEPRECATED(msg, fn)
|
||||
|
||||
// Forward-declare libuv loop
|
||||
struct uv_loop_s;
|
||||
|
||||
// Forward-declare these functions now to stop MSVS from becoming
|
||||
// terminally confused when it's done in node_internals.h
|
||||
namespace node {
|
||||
@ -178,11 +181,25 @@ NODE_EXTERN void Init(int* argc,
|
||||
class Environment;
|
||||
|
||||
NODE_EXTERN Environment* CreateEnvironment(v8::Isolate* isolate,
|
||||
struct uv_loop_s* loop,
|
||||
v8::Handle<v8::Context> context,
|
||||
int argc,
|
||||
const char* const* argv,
|
||||
int exec_argc,
|
||||
const char* const* exec_argv);
|
||||
NODE_EXTERN void LoadEnvironment(Environment* env);
|
||||
|
||||
// NOTE: Calling this is the same as calling
|
||||
// CreateEnvironment() + LoadEnvironment() from above.
|
||||
// `uv_default_loop()` will be passed as `loop`.
|
||||
NODE_EXTERN Environment* CreateEnvironment(v8::Isolate* isolate,
|
||||
v8::Handle<v8::Context> context,
|
||||
int argc,
|
||||
const char* const* argv,
|
||||
int exec_argc,
|
||||
const char* const* exec_argv);
|
||||
|
||||
|
||||
NODE_EXTERN void EmitBeforeExit(Environment* env);
|
||||
NODE_EXTERN int EmitExit(Environment* env);
|
||||
NODE_EXTERN void RunAtExit(Environment* env);
|
||||
|
10
src/node.js
10
src/node.js
@ -56,7 +56,10 @@
|
||||
startup.processKillAndExit();
|
||||
startup.processSignalHandlers();
|
||||
|
||||
startup.processChannel();
|
||||
// Do not initialize channel in debugger agent, it deletes env variable
|
||||
// and the main thread won't see it.
|
||||
if (process.argv[1] !== '--debug-agent')
|
||||
startup.processChannel();
|
||||
|
||||
startup.processRawDebug();
|
||||
|
||||
@ -80,6 +83,11 @@
|
||||
var d = NativeModule.require('_debugger');
|
||||
d.start();
|
||||
|
||||
} else if (process.argv[1] == '--debug-agent') {
|
||||
// Start the debugger agent
|
||||
var d = NativeModule.require('_debugger_agent');
|
||||
d.start();
|
||||
|
||||
} else if (process._eval != null) {
|
||||
// User passed '-e' or '--eval' arguments to Node.
|
||||
evalScript('[eval]');
|
||||
|
@ -31,9 +31,6 @@
|
||||
|
||||
namespace node {
|
||||
|
||||
// defined in node.cc
|
||||
extern QUEUE req_wrap_queue;
|
||||
|
||||
template <typename T>
|
||||
class ReqWrap : public AsyncWrap {
|
||||
public:
|
||||
@ -44,7 +41,7 @@ class ReqWrap : public AsyncWrap {
|
||||
if (env->in_domain())
|
||||
object->Set(env->domain_string(), env->domain_array()->Get(0));
|
||||
|
||||
QUEUE_INSERT_TAIL(&req_wrap_queue, &req_wrap_queue_);
|
||||
QUEUE_INSERT_TAIL(env->req_wrap_queue(), &req_wrap_queue_);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user