Browse files

Land Cantrill's DTrace patch

only works on solaris
  • Loading branch information...
1 parent 91cc2d8 commit 068b733583a4f0781a8418ddf42f7912e3dd53a6 @ry ry committed Jan 25, 2011
Showing with 358 additions and 2 deletions.
  1. +2 −0 lib/http.js
  2. +2 −0 lib/net.js
  3. +3 −0 src/node.cc
  4. +64 −0 src/node.d
  5. +113 −0 src/node_dtrace.cc
  6. +28 −0 src/node_dtrace.h
  7. +44 −0 src/node_provider.d
  8. +7 −0 test/common.js
  9. +95 −2 wscript
View
2 lib/http.js
@@ -599,6 +599,7 @@ OutgoingMessage.prototype.end = function(data, encoding) {
}
this.finished = true;
+ DTRACE_HTTP_SERVER_RESPONSE(this.connection);
// There is the first message on the outgoing queue, and we've sent
// everything to the socket.
@@ -866,6 +867,7 @@ function connectionListener(socket) {
var res = new ServerResponse(req);
debug('server response shouldKeepAlive: ' + shouldKeepAlive);
res.shouldKeepAlive = shouldKeepAlive;
+ DTRACE_HTTP_SERVER_REQUEST(req, socket);
if (socket._httpMessage) {
// There are already pending outgoing res, append.
View
2 lib/net.js
@@ -789,6 +789,7 @@ Socket.prototype._shutdown = function() {
Socket.prototype.end = function(data, encoding) {
if (this.writable) {
if (this._writeQueueLast() !== END_OF_FILE) {
+ DTRACE_NET_STREAM_END(this);
if (data) this.write(data, encoding);
this._writeQueue.push(END_OF_FILE);
if (!this._connecting) {
@@ -868,6 +869,7 @@ function Server(/* [ options, ] listener */) {
s.server = self;
s.resume();
+ DTRACE_NET_SERVER_CONNECTION(s);
self.emit('connection', s);
// The 'connect' event probably should be removed for server-side
View
3 src/node.cc
@@ -3,6 +3,7 @@
#include <node.h>
#include <v8-debug.h>
+#include <node_dtrace.h>
#include <locale.h>
#include <stdio.h>
@@ -2028,6 +2029,8 @@ static void Load(int argc, char *argv[]) {
Local<Object> global = v8::Context::GetCurrent()->Global();
Local<Value> args[1] = { Local<Value>::New(process) };
+ InitDTrace(global);
+
f->Call(global, 1, args);
if (try_catch.HasCaught()) {
View
64 src/node.d
@@ -0,0 +1,64 @@
+/*
+ * This is the DTrace library file for the node provider, which includes
+ * the necessary translators to get from the args[] to something useful.
+ * Be warned: the mechanics here are seriously ugly -- and one must always
+ * keep in mind that clean abstractions often require filthy systems.
+ */
+#pragma D depends_on library procfs.d
+
+typedef struct {
+ int32_t fd;
+ int32_t port;
+ uint32_t remote;
+} node_dtrace_connection_t;
+
+typedef struct {
+ int32_t fd;
+ int32_t port;
+ uint64_t remote;
+} node_dtrace_connection64_t;
+
+typedef struct {
+ int fd;
+ string remoteAddress;
+ int remotePort;
+} node_connection_t;
+
+translator node_connection_t <node_dtrace_connection_t *nc> {
+ fd = *(int32_t *)copyin((uintptr_t)&nc->fd, sizeof (int32_t));
+ remotePort =
+ *(int32_t *)copyin((uintptr_t)&nc->port, sizeof (int32_t));
+ remoteAddress = curpsinfo->pr_dmodel == PR_MODEL_ILP32 ?
+ copyinstr((uintptr_t)*(uint32_t *)copyin((uintptr_t)&nc->remote,
+ sizeof (int32_t))) :
+ copyinstr((uintptr_t)*(uint64_t *)copyin((uintptr_t)
+ &((node_dtrace_connection64_t *)nc)->remote, sizeof (int64_t)));
+};
+
+typedef struct {
+ uint32_t url;
+ uint32_t method;
+} node_dtrace_http_request_t;
+
+typedef struct {
+ uint64_t url;
+ uint64_t method;
+} node_dtrace_http_request64_t;
+
+typedef struct {
+ string url;
+ string method;
+} node_http_request_t;
+
+translator node_http_request_t <node_dtrace_http_request_t *nd> {
+ url = curpsinfo->pr_dmodel == PR_MODEL_ILP32 ?
+ copyinstr((uintptr_t)*(uint32_t *)copyin((uintptr_t)&nd->url,
+ sizeof (int32_t))) :
+ copyinstr((uintptr_t)*(uint64_t *)copyin((uintptr_t)
+ &((node_dtrace_http_request64_t *)nd)->url, sizeof (int64_t)));
+ method = curpsinfo->pr_dmodel == PR_MODEL_ILP32 ?
+ copyinstr((uintptr_t)*(uint32_t *)copyin((uintptr_t)&nd->method,
+ sizeof (int32_t))) :
+ copyinstr((uintptr_t)*(uint64_t *)copyin((uintptr_t)
+ &((node_dtrace_http_request64_t *)nd)->method, sizeof (int64_t)));
+};
View
113 src/node_dtrace.cc
@@ -0,0 +1,113 @@
+#include <node_dtrace.h>
+
+#ifdef HAVE_DTRACE
+#include "node_provider.h"
+#else
+#define NODE_HTTP_SERVER_REQUEST(arg0, arg1)
+#define NODE_HTTP_SERVER_REQUEST_ENABLED() (0)
+#define NODE_HTTP_SERVER_RESPONSE(arg0)
+#define NODE_HTTP_SERVER_RESPONSE_ENABLED() (0)
+#define NODE_NET_SERVER_CONNECTION(arg0)
+#define NODE_NET_SERVER_CONNECTION_ENABLED() (0)
+#define NODE_NET_STREAM_END(arg0)
+#define NODE_NET_STREAM_END_ENABLED() (0)
+#endif
+
+namespace node {
+
+using namespace v8;
+
+#define SLURP_STRING(obj, member, valp) \
+ String::Utf8Value _##member(obj->Get(String::New(#member))->ToString()); \
+ if ((*(const char **)valp = *_##member) == NULL) \
+ *(const char **)valp = "<unknown>";
+
+#define SLURP_INT(obj, member, valp) \
+ *valp = obj->Get(String::New(#member))->ToInteger()->Value();
+
+#define SLURP_CONNECTION(arg, conn) \
+ node_dtrace_connection_t conn; \
+ Local<Object> _##conn = Local<Object>::Cast(arg); \
+ SLURP_INT(_##conn, fd, &conn.fd); \
+ SLURP_STRING(_##conn, remoteAddress, &conn.remote); \
+ SLURP_INT(_##conn, remotePort, &conn.port);
+
+Handle<Value> DTRACE_NET_SERVER_CONNECTION(const Arguments& args) {
+ if (!NODE_NET_SERVER_CONNECTION_ENABLED())
+ return Undefined();
+
+ HandleScope scope;
+
+ SLURP_CONNECTION(args[0], conn);
+ NODE_NET_SERVER_CONNECTION(&conn);
+
+ return Undefined();
+}
+
+Handle<Value> DTRACE_NET_STREAM_END(const Arguments& args) {
+ if (!NODE_NET_STREAM_END_ENABLED())
+ return Undefined();
+
+ HandleScope scope;
+
+ SLURP_CONNECTION(args[0], conn);
+ NODE_NET_STREAM_END(&conn);
+
+ return Undefined();
+}
+
+Handle<Value> DTRACE_HTTP_SERVER_REQUEST(const Arguments& args) {
+ node_dtrace_http_request_t req;
+
+ if (!NODE_HTTP_SERVER_REQUEST_ENABLED())
+ return Undefined();
+
+ HandleScope scope;
+
+ Local<Object> arg0 = Local<Object>::Cast(args[0]);
+ Local<Object> arg1 = Local<Object>::Cast(args[1]);
+
+ SLURP_STRING(arg0, url, &req.url);
+ SLURP_STRING(arg0, method, &req.method);
+
+ SLURP_CONNECTION(args[1], conn);
+
+ NODE_HTTP_SERVER_REQUEST(&req, &conn);
+ return Undefined();
+}
+
+Handle<Value> DTRACE_HTTP_SERVER_RESPONSE(const Arguments& args) {
+ if (!NODE_HTTP_SERVER_RESPONSE_ENABLED())
+ return Undefined();
+
+ HandleScope scope;
+
+ SLURP_CONNECTION(args[0], conn);
+ NODE_HTTP_SERVER_RESPONSE(&conn);
+
+ return Undefined();
+}
+
+#define NODE_PROBE(name) #name, name
+
+void InitDTrace(Handle<Object> target) {
+ static struct {
+ const char *name;
+ Handle<Value> (*func)(const Arguments&);
+ Persistent<FunctionTemplate> templ;
+ } tab[] = {
+ { NODE_PROBE(DTRACE_NET_SERVER_CONNECTION) },
+ { NODE_PROBE(DTRACE_NET_STREAM_END) },
+ { NODE_PROBE(DTRACE_HTTP_SERVER_REQUEST) },
+ { NODE_PROBE(DTRACE_HTTP_SERVER_RESPONSE) },
+ { NULL }
+ };
+
+ for (int i = 0; tab[i].name != NULL; i++) {
+ tab[i].templ = Persistent<FunctionTemplate>::New(
+ FunctionTemplate::New(tab[i].func));
+ target->Set(String::NewSymbol(tab[i].name), tab[i].templ->GetFunction());
+ }
+}
+
+}
View
28 src/node_dtrace.h
@@ -0,0 +1,28 @@
+#ifndef NODE_DTRACE_H_
+#define NODE_DTRACE_H_
+
+#include <node.h>
+#include <v8.h>
+
+extern "C" {
+
+typedef struct {
+ int32_t fd;
+ int32_t port;
+ char *remote;
+} node_dtrace_connection_t;
+
+typedef struct {
+ char *url;
+ char *method;
+} node_dtrace_http_request_t;
+
+}
+
+namespace node {
+
+void InitDTrace(v8::Handle<v8::Object> target);
+
+}
+
+#endif
View
44 src/node_provider.d
@@ -0,0 +1,44 @@
+/*
+ * DTrace provider for node.js.
+ */
+
+/*
+ * In order to have the information we need here to create the provider,
+ * we must declare bogus definitions for our depended-upon structures. And
+ * yes, the fact that we need to do this represents a shortcoming in DTrace,
+ * one that would be resolved by that elusive El Dorado: dynamic translators.
+ */
+
+typedef struct {
+ int dummy;
+} node_dtrace_connection_t;
+
+typedef struct {
+ int dummy;
+} node_connection_t;
+
+typedef struct {
+ int dummy;
+} node_dtrace_http_request_t;
+
+typedef struct {
+ int dummy;
+} node_http_request_t;
+
+provider node {
+ probe net__server__connection(node_dtrace_connection_t *c) :
+ (node_connection_t *c);
+ probe net__stream__end(node_dtrace_connection_t *c) :
+ (node_connection_t *c);
+ probe http__server__request(node_dtrace_http_request_t *h,
+ node_dtrace_connection_t *c) :
+ (node_http_request_t *h, node_connection_t *c);
+ probe http__server__response(node_dtrace_connection_t *c) :
+ (node_connection_t *c);
+};
+
+#pragma D attributes Evolving/Evolving/ISA provider node provider
+#pragma D attributes Private/Private/Unknown provider node module
+#pragma D attributes Private/Private/Unknown provider node function
+#pragma D attributes Private/Private/ISA provider node name
+#pragma D attributes Evolving/Evolving/ISA provider node args
View
7 test/common.js
@@ -39,6 +39,13 @@ process.on('exit', function() {
process,
global];
+ if (DTRACE_HTTP_SERVER_RESPONSE) {
+ knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE);
+ knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST);
+ knownGlobals.push(DTRACE_NET_STREAM_END);
+ knownGlobals.push(DTRACE_NET_SERVER_CONNECTION);
+ }
+
for (var x in global) {
var found = false;
View
97 wscript
@@ -1,7 +1,8 @@
#!/usr/bin/env python
import re
import Options
-import sys, os, shutil
+import sys, os, shutil, glob
+import Utils
from Utils import cmd_output
from os.path import join, dirname, abspath
from logging import fatal
@@ -158,6 +159,13 @@ def set_options(opt):
, dest='shared_libev_libpath'
)
+ opt.add_option( '--with-dtrace'
+ , action='store_true'
+ , default=False
+ , help='Build with DTrace (experimental)'
+ , dest='dtrace'
+ )
+
opt.add_option( '--product-type'
, action='store'
@@ -214,6 +222,14 @@ def configure(conf):
#if Options.options.debug:
# conf.check(lib='profiler', uselib_store='PROFILER')
+ if Options.options.dtrace:
+ if not sys.platform.startswith("sunos"):
+ conf.fatal('DTrace support only currently available on Solaris')
+
+ conf.find_program('dtrace', var='DTRACE', mandatory=True)
+ conf.env["USE_DTRACE"] = True
+ conf.env.append_value("CXXFLAGS", "-DHAVE_DTRACE=1")
+
if Options.options.efence:
conf.check(lib='efence', libpath=['/usr/lib', '/usr/local/lib'], uselib_store='EFENCE')
@@ -562,7 +578,7 @@ def build(bld):
### src/native.cc
def make_macros(loc, content):
- f = open(loc, 'w')
+ f = open(loc, 'a')
f.write(content)
f.close
@@ -576,10 +592,27 @@ def build(bld):
"macros.py"
)
+ ### We need to truncate the macros.py file
+ f = open(macros_loc_debug, 'w')
+ f.close
+ f = open(macros_loc_default, 'w')
+ f.close
+
make_macros(macros_loc_debug, "") # leave debug(x) as is in debug build
# replace debug(x) with nothing in release build
make_macros(macros_loc_default, "macro debug(x) = ;\n")
+ if not bld.env["USE_DTRACE"]:
+ make_macros(macros_loc_default, "macro DTRACE_HTTP_SERVER_RESPONSE(x) = ;\n");
+ make_macros(macros_loc_default, "macro DTRACE_HTTP_SERVER_REQUEST(x) = ;\n");
+ make_macros(macros_loc_default, "macro DTRACE_NET_SERVER_CONNECTION(x) = ;\n");
+ make_macros(macros_loc_default, "macro DTRACE_NET_STREAM_END(x) = ;\n");
+ make_macros(macros_loc_debug, "macro DTRACE_HTTP_SERVER_RESPONSE(x) = ;\n");
+ make_macros(macros_loc_debug, "macro DTRACE_HTTP_SERVER_REQUEST(x) = ;\n");
+ make_macros(macros_loc_debug, "macro DTRACE_NET_SERVER_CONNECTION(x) = ;\n");
+ make_macros(macros_loc_debug, "macro DTRACE_NET_STREAM_END(x) = ;\n");
+
+
def javascript_in_c(task):
env = task.env
source = map(lambda x: x.srcpath(env), task.inputs)
@@ -610,6 +643,65 @@ def build(bld):
native_cc_debug.rule = javascript_in_c_debug
native_cc.rule = javascript_in_c
+
+ if bld.env["USE_DTRACE"]:
+ dtrace = bld.new_task_gen(
+ name = "dtrace",
+ source = "src/node_provider.d",
+ target = "src/node_provider.h",
+ rule = "%s -x nolibs -h -o ${TGT} -s ${SRC}" % (bld.env.DTRACE),
+ before = "cxx",
+ )
+
+ if bld.env["USE_DEBUG"]:
+ dtrace_g = dtrace.clone("debug")
+
+ bld.install_files('/usr/lib/dtrace', 'src/node.d')
+
+ if sys.platform.startswith("sunos"):
+ #
+ # The USDT DTrace provider works slightly differently on Solaris than on
+ # the Mac; on Solaris, any objects that have USDT DTrace probes must be
+ # post-processed with the DTrace command. (This is not true on the
+ # Mac, which has first-class linker support for USDT probes.) On
+ # Solaris, we must therefore post-process our object files. Waf doesn't
+ # seem to really have a notion for this, so we inject a task after
+ # compiling and before linking, and then find all of the node object
+ # files and shuck them off to dtrace (which will modify them in place
+ # as appropriate).
+ #
+ def dtrace_postprocess(task):
+ abspath = bld.srcnode.abspath(bld.env_of_name(task.env.variant()))
+ objs = glob.glob(abspath + 'src/*.o')
+
+ Utils.exec_command('%s -G -x nolibs -s %s %s' % (task.env.DTRACE,
+ task.inputs[0].srcpath(task.env), ' '.join(objs)))
+
+ dtracepost = bld.new_task_gen(
+ name = "dtrace-postprocess",
+ source = "src/node_provider.d",
+ always = True,
+ before = "cxx_link",
+ after = "cxx",
+ )
+
+ bld.env.append_value('LINKFLAGS', 'node_provider.o')
+
+ #
+ # Note that for the same (mysterious) issue outlined above with respect
+ # to assigning the rule to native_cc/native_cc_debug, we must apply the
+ # rule to dtracepost/dtracepost_g only after they have been cloned. We
+ # also must put node_provider.o on the link line, but because we
+ # (apparently?) lack LINKFLAGS in debug, we (shamelessly) stowaway on
+ # LINKFLAGS_V8_G.
+ #
+ if bld.env["USE_DEBUG"]:
+ dtracepost_g = dtracepost.clone("debug")
+ dtracepost_g.rule = dtrace_postprocess
+ bld.env_of_name("debug").append_value('LINKFLAGS_V8_G',
+ 'node_provider.o')
+
+ dtracepost.rule = dtrace_postprocess
### node lib
node = bld.new_task_gen("cxx", product_type)
@@ -639,6 +731,7 @@ def build(bld):
src/node_timer.cc
src/node_script.cc
src/node_os.cc
+ src/node_dtrace.cc
"""
if sys.platform.startswith("win32"):

0 comments on commit 068b733

Please sign in to comment.