mirror of https://github.com/nodejs/node.git
parent
91cc2d8c4b
commit
068b733583
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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)));
|
||||
};
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
|
97
wscript
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"):
|
||||
|
|
Loading…
Reference in New Issue