Browse Source

Land Cantrill's DTrace patch

only works on solaris
v0.7.4-release
Ryan Dahl 14 years ago
parent
commit
068b733583
  1. 2
      lib/http.js
  2. 2
      lib/net.js
  3. 3
      src/node.cc
  4. 64
      src/node.d
  5. 113
      src/node_dtrace.cc
  6. 28
      src/node_dtrace.h
  7. 44
      src/node_provider.d
  8. 7
      test/common.js
  9. 97
      wscript

2
lib/http.js

@ -599,6 +599,7 @@ OutgoingMessage.prototype.end = function(data, encoding) {
} }
this.finished = true; this.finished = true;
DTRACE_HTTP_SERVER_RESPONSE(this.connection);
// There is the first message on the outgoing queue, and we've sent // There is the first message on the outgoing queue, and we've sent
// everything to the socket. // everything to the socket.
@ -866,6 +867,7 @@ function connectionListener(socket) {
var res = new ServerResponse(req); var res = new ServerResponse(req);
debug('server response shouldKeepAlive: ' + shouldKeepAlive); debug('server response shouldKeepAlive: ' + shouldKeepAlive);
res.shouldKeepAlive = shouldKeepAlive; res.shouldKeepAlive = shouldKeepAlive;
DTRACE_HTTP_SERVER_REQUEST(req, socket);
if (socket._httpMessage) { if (socket._httpMessage) {
// There are already pending outgoing res, append. // There are already pending outgoing res, append.

2
lib/net.js

@ -789,6 +789,7 @@ Socket.prototype._shutdown = function() {
Socket.prototype.end = function(data, encoding) { Socket.prototype.end = function(data, encoding) {
if (this.writable) { if (this.writable) {
if (this._writeQueueLast() !== END_OF_FILE) { if (this._writeQueueLast() !== END_OF_FILE) {
DTRACE_NET_STREAM_END(this);
if (data) this.write(data, encoding); if (data) this.write(data, encoding);
this._writeQueue.push(END_OF_FILE); this._writeQueue.push(END_OF_FILE);
if (!this._connecting) { if (!this._connecting) {
@ -868,6 +869,7 @@ function Server(/* [ options, ] listener */) {
s.server = self; s.server = self;
s.resume(); s.resume();
DTRACE_NET_SERVER_CONNECTION(s);
self.emit('connection', s); self.emit('connection', s);
// The 'connect' event probably should be removed for server-side // The 'connect' event probably should be removed for server-side

3
src/node.cc

@ -3,6 +3,7 @@
#include <node.h> #include <node.h>
#include <v8-debug.h> #include <v8-debug.h>
#include <node_dtrace.h>
#include <locale.h> #include <locale.h>
#include <stdio.h> #include <stdio.h>
@ -2028,6 +2029,8 @@ static void Load(int argc, char *argv[]) {
Local<Object> global = v8::Context::GetCurrent()->Global(); Local<Object> global = v8::Context::GetCurrent()->Global();
Local<Value> args[1] = { Local<Value>::New(process) }; Local<Value> args[1] = { Local<Value>::New(process) };
InitDTrace(global);
f->Call(global, 1, args); f->Call(global, 1, args);
if (try_catch.HasCaught()) { if (try_catch.HasCaught()) {

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)));
};

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());
}
}
}

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

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

7
test/common.js

@ -39,6 +39,13 @@ process.on('exit', function() {
process, process,
global]; 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) { for (var x in global) {
var found = false; var found = false;

97
wscript

@ -1,7 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
import re import re
import Options import Options
import sys, os, shutil import sys, os, shutil, glob
import Utils
from Utils import cmd_output from Utils import cmd_output
from os.path import join, dirname, abspath from os.path import join, dirname, abspath
from logging import fatal from logging import fatal
@ -158,6 +159,13 @@ def set_options(opt):
, dest='shared_libev_libpath' , 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' opt.add_option( '--product-type'
, action='store' , action='store'
@ -214,6 +222,14 @@ def configure(conf):
#if Options.options.debug: #if Options.options.debug:
# conf.check(lib='profiler', uselib_store='PROFILER') # 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: if Options.options.efence:
conf.check(lib='efence', libpath=['/usr/lib', '/usr/local/lib'], uselib_store='EFENCE') conf.check(lib='efence', libpath=['/usr/lib', '/usr/local/lib'], uselib_store='EFENCE')
@ -562,7 +578,7 @@ def build(bld):
### src/native.cc ### src/native.cc
def make_macros(loc, content): def make_macros(loc, content):
f = open(loc, 'w') f = open(loc, 'a')
f.write(content) f.write(content)
f.close f.close
@ -576,10 +592,27 @@ def build(bld):
"macros.py" "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 make_macros(macros_loc_debug, "") # leave debug(x) as is in debug build
# replace debug(x) with nothing in release build # replace debug(x) with nothing in release build
make_macros(macros_loc_default, "macro debug(x) = ;\n") 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): def javascript_in_c(task):
env = task.env env = task.env
source = map(lambda x: x.srcpath(env), task.inputs) source = map(lambda x: x.srcpath(env), task.inputs)
@ -611,6 +644,65 @@ def build(bld):
native_cc.rule = javascript_in_c 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 lib
node = bld.new_task_gen("cxx", product_type) node = bld.new_task_gen("cxx", product_type)
node.name = "node" node.name = "node"
@ -639,6 +731,7 @@ def build(bld):
src/node_timer.cc src/node_timer.cc
src/node_script.cc src/node_script.cc
src/node_os.cc src/node_os.cc
src/node_dtrace.cc
""" """
if sys.platform.startswith("win32"): if sys.platform.startswith("win32"):

Loading…
Cancel
Save