| @@ -23,9 +23,6 @@ | ||
|
|
||
| #if defined(__INTEL_COMPILER) && defined(__unix__) | ||
|
|
||
| #ifdef HAVE_NETINET_IN_H | ||
| # include <netinet/in.h> | ||
| #endif | ||
| @@ -16,11 +16,6 @@ | ||
|
|
||
| #include "ares_setup.h" | ||
|
|
||
| #ifdef HAVE_STRINGS_H | ||
| # include <strings.h> | ||
| #endif | ||
| @@ -0,0 +1,32 @@ | ||
| { | ||
| "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", | ||
| ], | ||
| }, | ||
| 'conditions': [ | ||
| [ 'gcc_version<=44', { | ||
| # GCC versions <= 4.4 do not handle the aliasing in the queue | ||
| # implementation, so disable aliasing on these platforms | ||
| # to avoid subtle bugs | ||
| 'cflags': [ '-fno-strict-aliasing' ], | ||
| }], | ||
| ], | ||
| "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_ |
| @@ -1,11 +1,27 @@ | ||
| /out/ | ||
| core | ||
| tags | ||
| *.o | ||
| test | ||
| test_g | ||
| test_fast | ||
| url_parser | ||
| parsertrace | ||
| parsertrace_g | ||
| *.mk | ||
| *.Makefile | ||
| *.so.* | ||
| *.a | ||
|
|
||
|
|
||
| # Visual Studio uglies | ||
| *.suo | ||
| *.sln | ||
| *.vcxproj | ||
| *.vcxproj.filters | ||
| *.vcxproj.user | ||
| *.opensdf | ||
| *.ncrunchsolution* | ||
| *.sdf | ||
| *.vsp | ||
| *.psess |
| @@ -0,0 +1,13 @@ | ||
| language: c | ||
|
|
||
| compiler: | ||
| - clang | ||
| - gcc | ||
|
|
||
| script: | ||
| - "make" | ||
|
|
||
| notifications: | ||
| email: false | ||
| irc: | ||
| - "irc.freenode.net#libuv" |
| @@ -0,0 +1,156 @@ | ||
| /* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev | ||
| * | ||
| * Additional changes are licensed under the same terms as NGINX and | ||
| * copyright Joyent, Inc. and other Node contributors. All rights reserved. | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| /* Dump what the parser finds to stdout as it happen */ | ||
|
|
||
| #include "http_parser.h" | ||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
|
|
||
| int on_message_begin(http_parser* _) { | ||
| (void)_; | ||
| printf("\n***MESSAGE BEGIN***\n\n"); | ||
| return 0; | ||
| } | ||
|
|
||
| int on_headers_complete(http_parser* _) { | ||
| (void)_; | ||
| printf("\n***HEADERS COMPLETE***\n\n"); | ||
| return 0; | ||
| } | ||
|
|
||
| int on_message_complete(http_parser* _) { | ||
| (void)_; | ||
| printf("\n***MESSAGE COMPLETE***\n\n"); | ||
| return 0; | ||
| } | ||
|
|
||
| int on_url(http_parser* _, const char* at, size_t length) { | ||
| (void)_; | ||
| printf("Url: %.*s\n", (int)length, at); | ||
| return 0; | ||
| } | ||
|
|
||
| int on_header_field(http_parser* _, const char* at, size_t length) { | ||
| (void)_; | ||
| printf("Header field: %.*s\n", (int)length, at); | ||
| return 0; | ||
| } | ||
|
|
||
| int on_header_value(http_parser* _, const char* at, size_t length) { | ||
| (void)_; | ||
| printf("Header value: %.*s\n", (int)length, at); | ||
| return 0; | ||
| } | ||
|
|
||
| int on_body(http_parser* _, const char* at, size_t length) { | ||
| (void)_; | ||
| printf("Body: %.*s\n", (int)length, at); | ||
| return 0; | ||
| } | ||
|
|
||
| void usage(const char* name) { | ||
| fprintf(stderr, | ||
| "Usage: %s $type $filename\n" | ||
| " type: -x, where x is one of {r,b,q}\n" | ||
| " parses file as a Response, reQuest, or Both\n", | ||
| name); | ||
| exit(EXIT_FAILURE); | ||
| } | ||
|
|
||
| int main(int argc, char* argv[]) { | ||
| enum http_parser_type file_type; | ||
|
|
||
| if (argc != 3) { | ||
| usage(argv[0]); | ||
| } | ||
|
|
||
| char* type = argv[1]; | ||
| if (type[0] != '-') { | ||
| usage(argv[0]); | ||
| } | ||
|
|
||
| switch (type[1]) { | ||
| /* in the case of "-", type[1] will be NUL */ | ||
| case 'r': | ||
| file_type = HTTP_RESPONSE; | ||
| break; | ||
| case 'q': | ||
| file_type = HTTP_REQUEST; | ||
| break; | ||
| case 'b': | ||
| file_type = HTTP_BOTH; | ||
| break; | ||
| default: | ||
| usage(argv[0]); | ||
| } | ||
|
|
||
| char* filename = argv[2]; | ||
| FILE* file = fopen(filename, "r"); | ||
| if (file == NULL) { | ||
| perror("fopen"); | ||
| return EXIT_FAILURE; | ||
| } | ||
|
|
||
| fseek(file, 0, SEEK_END); | ||
| long file_length = ftell(file); | ||
| if (file_length == -1) { | ||
| perror("ftell"); | ||
| return EXIT_FAILURE; | ||
| } | ||
| fseek(file, 0, SEEK_SET); | ||
|
|
||
| char* data = malloc(file_length); | ||
| if (fread(data, 1, file_length, file) != (size_t)file_length) { | ||
| fprintf(stderr, "couldn't read entire file\n"); | ||
| free(data); | ||
| return EXIT_FAILURE; | ||
| } | ||
|
|
||
| http_parser_settings settings; | ||
| memset(&settings, 0, sizeof(settings)); | ||
| settings.on_message_begin = on_message_begin; | ||
| settings.on_url = on_url; | ||
| settings.on_header_field = on_header_field; | ||
| settings.on_header_value = on_header_value; | ||
| settings.on_headers_complete = on_headers_complete; | ||
| settings.on_body = on_body; | ||
| settings.on_message_complete = on_message_complete; | ||
|
|
||
| http_parser parser; | ||
| http_parser_init(&parser, file_type); | ||
| size_t nparsed = http_parser_execute(&parser, &settings, data, file_length); | ||
| free(data); | ||
|
|
||
| if (nparsed != (size_t)file_length) { | ||
| fprintf(stderr, | ||
| "Error: %s (%s)\n", | ||
| http_errno_description(HTTP_PARSER_ERRNO(&parser)), | ||
| http_errno_name(HTTP_PARSER_ERRNO(&parser))); | ||
| return EXIT_FAILURE; | ||
| } | ||
|
|
||
| return EXIT_SUCCESS; | ||
| } |