summaryrefslogtreecommitdiff
path: root/deps
diff options
context:
space:
mode:
authorFedor Indutny <fedor@indutny.com>2014-10-04 18:44:39 +0400
committerFedor Indutny <fedor@indutny.com>2014-10-08 15:44:40 +0400
commit7a0cfe9b116c6b5d56f7412d6ea71f5a8d8440e2 (patch)
tree65f1811b365ca5e994d7621ff807b44a2854ef6d /deps
parent8efcc7f456f863cb04724c7b35fb935a8c441fe6 (diff)
downloadnode-7a0cfe9b116c6b5d56f7412d6ea71f5a8d8440e2.tar.gz
deps: re-implement debugger-agent
Reviewed-By: Trevor Norris <trevnorris@gmail.com> PR-URL: https://github.com/joyent/node/pull/8476
Diffstat (limited to 'deps')
-rw-r--r--deps/debugger-agent/debugger-agent.gyp24
-rw-r--r--deps/debugger-agent/include/debugger-agent.h109
-rw-r--r--deps/debugger-agent/lib/_debugger_agent.js191
-rw-r--r--deps/debugger-agent/src/agent.cc347
-rw-r--r--deps/debugger-agent/src/agent.h64
5 files changed, 735 insertions, 0 deletions
diff --git a/deps/debugger-agent/debugger-agent.gyp b/deps/debugger-agent/debugger-agent.gyp
new file mode 100644
index 000000000..e98206849
--- /dev/null
+++ b/deps/debugger-agent/debugger-agent.gyp
@@ -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",
+ ],
+ }],
+}
diff --git a/deps/debugger-agent/include/debugger-agent.h b/deps/debugger-agent/include/debugger-agent.h
new file mode 100644
index 000000000..762a687a0
--- /dev/null
+++ b/deps/debugger-agent/include/debugger-agent.h
@@ -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_
diff --git a/deps/debugger-agent/lib/_debugger_agent.js b/deps/debugger-agent/lib/_debugger_agent.js
new file mode 100644
index 000000000..680c5e95c
--- /dev/null
+++ b/deps/debugger-agent/lib/_debugger_agent.js
@@ -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;
+}
diff --git a/deps/debugger-agent/src/agent.cc b/deps/debugger-agent/src/agent.cc
new file mode 100644
index 000000000..335737ffe
--- /dev/null
+++ b/deps/debugger-agent/src/agent.cc
@@ -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
diff --git a/deps/debugger-agent/src/agent.h b/deps/debugger-agent/src/agent.h
new file mode 100644
index 000000000..82db5e5e1
--- /dev/null
+++ b/deps/debugger-agent/src/agent.h
@@ -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_