mirror of https://github.com/lukechilds/node.git
Browse Source
Reviewed-By: Trevor Norris <trevnorris@gmail.com> PR-URL: https://github.com/joyent/node/pull/8476v0.11.15-release
Fedor Indutny
10 years ago
17 changed files with 1009 additions and 93 deletions
@ -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", |
||||
|
], |
||||
|
}], |
||||
|
} |
@ -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_
|
@ -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; |
||||
|
} |
@ -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
|
@ -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_
|
Loading…
Reference in new issue