Skip to content
This repository
Browse code

Land Cantrill's DTrace patch

only works on solaris
  • Loading branch information...
commit 068b733583a4f0781a8418ddf42f7912e3dd53a6 1 parent 91cc2d8
ry ry authored
2  lib/http.js
@@ -599,6 +599,7 @@ OutgoingMessage.prototype.end = function(data, encoding) {
599 599 }
600 600
601 601 this.finished = true;
  602 + DTRACE_HTTP_SERVER_RESPONSE(this.connection);
602 603
603 604 // There is the first message on the outgoing queue, and we've sent
604 605 // everything to the socket.
@@ -866,6 +867,7 @@ function connectionListener(socket) {
866 867 var res = new ServerResponse(req);
867 868 debug('server response shouldKeepAlive: ' + shouldKeepAlive);
868 869 res.shouldKeepAlive = shouldKeepAlive;
  870 + DTRACE_HTTP_SERVER_REQUEST(req, socket);
869 871
870 872 if (socket._httpMessage) {
871 873 // There are already pending outgoing res, append.
2  lib/net.js
@@ -789,6 +789,7 @@ Socket.prototype._shutdown = function() {
789 789 Socket.prototype.end = function(data, encoding) {
790 790 if (this.writable) {
791 791 if (this._writeQueueLast() !== END_OF_FILE) {
  792 + DTRACE_NET_STREAM_END(this);
792 793 if (data) this.write(data, encoding);
793 794 this._writeQueue.push(END_OF_FILE);
794 795 if (!this._connecting) {
@@ -868,6 +869,7 @@ function Server(/* [ options, ] listener */) {
868 869 s.server = self;
869 870 s.resume();
870 871
  872 + DTRACE_NET_SERVER_CONNECTION(s);
871 873 self.emit('connection', s);
872 874
873 875 // The 'connect' event probably should be removed for server-side
3  src/node.cc
@@ -3,6 +3,7 @@
3 3 #include <node.h>
4 4
5 5 #include <v8-debug.h>
  6 +#include <node_dtrace.h>
6 7
7 8 #include <locale.h>
8 9 #include <stdio.h>
@@ -2028,6 +2029,8 @@ static void Load(int argc, char *argv[]) {
2028 2029 Local<Object> global = v8::Context::GetCurrent()->Global();
2029 2030 Local<Value> args[1] = { Local<Value>::New(process) };
2030 2031
  2032 + InitDTrace(global);
  2033 +
2031 2034 f->Call(global, 1, args);
2032 2035
2033 2036 if (try_catch.HasCaught()) {
64 src/node.d
... ... @@ -0,0 +1,64 @@
  1 +/*
  2 + * This is the DTrace library file for the node provider, which includes
  3 + * the necessary translators to get from the args[] to something useful.
  4 + * Be warned: the mechanics here are seriously ugly -- and one must always
  5 + * keep in mind that clean abstractions often require filthy systems.
  6 + */
  7 +#pragma D depends_on library procfs.d
  8 +
  9 +typedef struct {
  10 + int32_t fd;
  11 + int32_t port;
  12 + uint32_t remote;
  13 +} node_dtrace_connection_t;
  14 +
  15 +typedef struct {
  16 + int32_t fd;
  17 + int32_t port;
  18 + uint64_t remote;
  19 +} node_dtrace_connection64_t;
  20 +
  21 +typedef struct {
  22 + int fd;
  23 + string remoteAddress;
  24 + int remotePort;
  25 +} node_connection_t;
  26 +
  27 +translator node_connection_t <node_dtrace_connection_t *nc> {
  28 + fd = *(int32_t *)copyin((uintptr_t)&nc->fd, sizeof (int32_t));
  29 + remotePort =
  30 + *(int32_t *)copyin((uintptr_t)&nc->port, sizeof (int32_t));
  31 + remoteAddress = curpsinfo->pr_dmodel == PR_MODEL_ILP32 ?
  32 + copyinstr((uintptr_t)*(uint32_t *)copyin((uintptr_t)&nc->remote,
  33 + sizeof (int32_t))) :
  34 + copyinstr((uintptr_t)*(uint64_t *)copyin((uintptr_t)
  35 + &((node_dtrace_connection64_t *)nc)->remote, sizeof (int64_t)));
  36 +};
  37 +
  38 +typedef struct {
  39 + uint32_t url;
  40 + uint32_t method;
  41 +} node_dtrace_http_request_t;
  42 +
  43 +typedef struct {
  44 + uint64_t url;
  45 + uint64_t method;
  46 +} node_dtrace_http_request64_t;
  47 +
  48 +typedef struct {
  49 + string url;
  50 + string method;
  51 +} node_http_request_t;
  52 +
  53 +translator node_http_request_t <node_dtrace_http_request_t *nd> {
  54 + url = curpsinfo->pr_dmodel == PR_MODEL_ILP32 ?
  55 + copyinstr((uintptr_t)*(uint32_t *)copyin((uintptr_t)&nd->url,
  56 + sizeof (int32_t))) :
  57 + copyinstr((uintptr_t)*(uint64_t *)copyin((uintptr_t)
  58 + &((node_dtrace_http_request64_t *)nd)->url, sizeof (int64_t)));
  59 + method = curpsinfo->pr_dmodel == PR_MODEL_ILP32 ?
  60 + copyinstr((uintptr_t)*(uint32_t *)copyin((uintptr_t)&nd->method,
  61 + sizeof (int32_t))) :
  62 + copyinstr((uintptr_t)*(uint64_t *)copyin((uintptr_t)
  63 + &((node_dtrace_http_request64_t *)nd)->method, sizeof (int64_t)));
  64 +};
113 src/node_dtrace.cc
... ... @@ -0,0 +1,113 @@
  1 +#include <node_dtrace.h>
  2 +
  3 +#ifdef HAVE_DTRACE
  4 +#include "node_provider.h"
  5 +#else
  6 +#define NODE_HTTP_SERVER_REQUEST(arg0, arg1)
  7 +#define NODE_HTTP_SERVER_REQUEST_ENABLED() (0)
  8 +#define NODE_HTTP_SERVER_RESPONSE(arg0)
  9 +#define NODE_HTTP_SERVER_RESPONSE_ENABLED() (0)
  10 +#define NODE_NET_SERVER_CONNECTION(arg0)
  11 +#define NODE_NET_SERVER_CONNECTION_ENABLED() (0)
  12 +#define NODE_NET_STREAM_END(arg0)
  13 +#define NODE_NET_STREAM_END_ENABLED() (0)
  14 +#endif
  15 +
  16 +namespace node {
  17 +
  18 +using namespace v8;
  19 +
  20 +#define SLURP_STRING(obj, member, valp) \
  21 + String::Utf8Value _##member(obj->Get(String::New(#member))->ToString()); \
  22 + if ((*(const char **)valp = *_##member) == NULL) \
  23 + *(const char **)valp = "<unknown>";
  24 +
  25 +#define SLURP_INT(obj, member, valp) \
  26 + *valp = obj->Get(String::New(#member))->ToInteger()->Value();
  27 +
  28 +#define SLURP_CONNECTION(arg, conn) \
  29 + node_dtrace_connection_t conn; \
  30 + Local<Object> _##conn = Local<Object>::Cast(arg); \
  31 + SLURP_INT(_##conn, fd, &conn.fd); \
  32 + SLURP_STRING(_##conn, remoteAddress, &conn.remote); \
  33 + SLURP_INT(_##conn, remotePort, &conn.port);
  34 +
  35 +Handle<Value> DTRACE_NET_SERVER_CONNECTION(const Arguments& args) {
  36 + if (!NODE_NET_SERVER_CONNECTION_ENABLED())
  37 + return Undefined();
  38 +
  39 + HandleScope scope;
  40 +
  41 + SLURP_CONNECTION(args[0], conn);
  42 + NODE_NET_SERVER_CONNECTION(&conn);
  43 +
  44 + return Undefined();
  45 +}
  46 +
  47 +Handle<Value> DTRACE_NET_STREAM_END(const Arguments& args) {
  48 + if (!NODE_NET_STREAM_END_ENABLED())
  49 + return Undefined();
  50 +
  51 + HandleScope scope;
  52 +
  53 + SLURP_CONNECTION(args[0], conn);
  54 + NODE_NET_STREAM_END(&conn);
  55 +
  56 + return Undefined();
  57 +}
  58 +
  59 +Handle<Value> DTRACE_HTTP_SERVER_REQUEST(const Arguments& args) {
  60 + node_dtrace_http_request_t req;
  61 +
  62 + if (!NODE_HTTP_SERVER_REQUEST_ENABLED())
  63 + return Undefined();
  64 +
  65 + HandleScope scope;
  66 +
  67 + Local<Object> arg0 = Local<Object>::Cast(args[0]);
  68 + Local<Object> arg1 = Local<Object>::Cast(args[1]);
  69 +
  70 + SLURP_STRING(arg0, url, &req.url);
  71 + SLURP_STRING(arg0, method, &req.method);
  72 +
  73 + SLURP_CONNECTION(args[1], conn);
  74 +
  75 + NODE_HTTP_SERVER_REQUEST(&req, &conn);
  76 + return Undefined();
  77 +}
  78 +
  79 +Handle<Value> DTRACE_HTTP_SERVER_RESPONSE(const Arguments& args) {
  80 + if (!NODE_HTTP_SERVER_RESPONSE_ENABLED())
  81 + return Undefined();
  82 +
  83 + HandleScope scope;
  84 +
  85 + SLURP_CONNECTION(args[0], conn);
  86 + NODE_HTTP_SERVER_RESPONSE(&conn);
  87 +
  88 + return Undefined();
  89 +}
  90 +
  91 +#define NODE_PROBE(name) #name, name
  92 +
  93 +void InitDTrace(Handle<Object> target) {
  94 + static struct {
  95 + const char *name;
  96 + Handle<Value> (*func)(const Arguments&);
  97 + Persistent<FunctionTemplate> templ;
  98 + } tab[] = {
  99 + { NODE_PROBE(DTRACE_NET_SERVER_CONNECTION) },
  100 + { NODE_PROBE(DTRACE_NET_STREAM_END) },
  101 + { NODE_PROBE(DTRACE_HTTP_SERVER_REQUEST) },
  102 + { NODE_PROBE(DTRACE_HTTP_SERVER_RESPONSE) },
  103 + { NULL }
  104 + };
  105 +
  106 + for (int i = 0; tab[i].name != NULL; i++) {
  107 + tab[i].templ = Persistent<FunctionTemplate>::New(
  108 + FunctionTemplate::New(tab[i].func));
  109 + target->Set(String::NewSymbol(tab[i].name), tab[i].templ->GetFunction());
  110 + }
  111 +}
  112 +
  113 +}
28 src/node_dtrace.h
... ... @@ -0,0 +1,28 @@
  1 +#ifndef NODE_DTRACE_H_
  2 +#define NODE_DTRACE_H_
  3 +
  4 +#include <node.h>
  5 +#include <v8.h>
  6 +
  7 +extern "C" {
  8 +
  9 +typedef struct {
  10 + int32_t fd;
  11 + int32_t port;
  12 + char *remote;
  13 +} node_dtrace_connection_t;
  14 +
  15 +typedef struct {
  16 + char *url;
  17 + char *method;
  18 +} node_dtrace_http_request_t;
  19 +
  20 +}
  21 +
  22 +namespace node {
  23 +
  24 +void InitDTrace(v8::Handle<v8::Object> target);
  25 +
  26 +}
  27 +
  28 +#endif
44 src/node_provider.d
... ... @@ -0,0 +1,44 @@
  1 +/*
  2 + * DTrace provider for node.js.
  3 + */
  4 +
  5 +/*
  6 + * In order to have the information we need here to create the provider,
  7 + * we must declare bogus definitions for our depended-upon structures. And
  8 + * yes, the fact that we need to do this represents a shortcoming in DTrace,
  9 + * one that would be resolved by that elusive El Dorado: dynamic translators.
  10 + */
  11 +
  12 +typedef struct {
  13 + int dummy;
  14 +} node_dtrace_connection_t;
  15 +
  16 +typedef struct {
  17 + int dummy;
  18 +} node_connection_t;
  19 +
  20 +typedef struct {
  21 + int dummy;
  22 +} node_dtrace_http_request_t;
  23 +
  24 +typedef struct {
  25 + int dummy;
  26 +} node_http_request_t;
  27 +
  28 +provider node {
  29 + probe net__server__connection(node_dtrace_connection_t *c) :
  30 + (node_connection_t *c);
  31 + probe net__stream__end(node_dtrace_connection_t *c) :
  32 + (node_connection_t *c);
  33 + probe http__server__request(node_dtrace_http_request_t *h,
  34 + node_dtrace_connection_t *c) :
  35 + (node_http_request_t *h, node_connection_t *c);
  36 + probe http__server__response(node_dtrace_connection_t *c) :
  37 + (node_connection_t *c);
  38 +};
  39 +
  40 +#pragma D attributes Evolving/Evolving/ISA provider node provider
  41 +#pragma D attributes Private/Private/Unknown provider node module
  42 +#pragma D attributes Private/Private/Unknown provider node function
  43 +#pragma D attributes Private/Private/ISA provider node name
  44 +#pragma D attributes Evolving/Evolving/ISA provider node args
7 test/common.js
@@ -39,6 +39,13 @@ process.on('exit', function() {
39 39 process,
40 40 global];
41 41
  42 + if (DTRACE_HTTP_SERVER_RESPONSE) {
  43 + knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE);
  44 + knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST);
  45 + knownGlobals.push(DTRACE_NET_STREAM_END);
  46 + knownGlobals.push(DTRACE_NET_SERVER_CONNECTION);
  47 + }
  48 +
42 49 for (var x in global) {
43 50 var found = false;
44 51
97 wscript
... ... @@ -1,7 +1,8 @@
1 1 #!/usr/bin/env python
2 2 import re
3 3 import Options
4   -import sys, os, shutil
  4 +import sys, os, shutil, glob
  5 +import Utils
5 6 from Utils import cmd_output
6 7 from os.path import join, dirname, abspath
7 8 from logging import fatal
@@ -158,6 +159,13 @@ def set_options(opt):
158 159 , dest='shared_libev_libpath'
159 160 )
160 161
  162 + opt.add_option( '--with-dtrace'
  163 + , action='store_true'
  164 + , default=False
  165 + , help='Build with DTrace (experimental)'
  166 + , dest='dtrace'
  167 + )
  168 +
161 169
162 170 opt.add_option( '--product-type'
163 171 , action='store'
@@ -214,6 +222,14 @@ def configure(conf):
214 222 #if Options.options.debug:
215 223 # conf.check(lib='profiler', uselib_store='PROFILER')
216 224
  225 + if Options.options.dtrace:
  226 + if not sys.platform.startswith("sunos"):
  227 + conf.fatal('DTrace support only currently available on Solaris')
  228 +
  229 + conf.find_program('dtrace', var='DTRACE', mandatory=True)
  230 + conf.env["USE_DTRACE"] = True
  231 + conf.env.append_value("CXXFLAGS", "-DHAVE_DTRACE=1")
  232 +
217 233 if Options.options.efence:
218 234 conf.check(lib='efence', libpath=['/usr/lib', '/usr/local/lib'], uselib_store='EFENCE')
219 235
@@ -562,7 +578,7 @@ def build(bld):
562 578
563 579 ### src/native.cc
564 580 def make_macros(loc, content):
565   - f = open(loc, 'w')
  581 + f = open(loc, 'a')
566 582 f.write(content)
567 583 f.close
568 584
@@ -576,10 +592,27 @@ def build(bld):
576 592 "macros.py"
577 593 )
578 594
  595 + ### We need to truncate the macros.py file
  596 + f = open(macros_loc_debug, 'w')
  597 + f.close
  598 + f = open(macros_loc_default, 'w')
  599 + f.close
  600 +
579 601 make_macros(macros_loc_debug, "") # leave debug(x) as is in debug build
580 602 # replace debug(x) with nothing in release build
581 603 make_macros(macros_loc_default, "macro debug(x) = ;\n")
582 604
  605 + if not bld.env["USE_DTRACE"]:
  606 + make_macros(macros_loc_default, "macro DTRACE_HTTP_SERVER_RESPONSE(x) = ;\n");
  607 + make_macros(macros_loc_default, "macro DTRACE_HTTP_SERVER_REQUEST(x) = ;\n");
  608 + make_macros(macros_loc_default, "macro DTRACE_NET_SERVER_CONNECTION(x) = ;\n");
  609 + make_macros(macros_loc_default, "macro DTRACE_NET_STREAM_END(x) = ;\n");
  610 + make_macros(macros_loc_debug, "macro DTRACE_HTTP_SERVER_RESPONSE(x) = ;\n");
  611 + make_macros(macros_loc_debug, "macro DTRACE_HTTP_SERVER_REQUEST(x) = ;\n");
  612 + make_macros(macros_loc_debug, "macro DTRACE_NET_SERVER_CONNECTION(x) = ;\n");
  613 + make_macros(macros_loc_debug, "macro DTRACE_NET_STREAM_END(x) = ;\n");
  614 +
  615 +
583 616 def javascript_in_c(task):
584 617 env = task.env
585 618 source = map(lambda x: x.srcpath(env), task.inputs)
@@ -610,6 +643,65 @@ def build(bld):
610 643 native_cc_debug.rule = javascript_in_c_debug
611 644
612 645 native_cc.rule = javascript_in_c
  646 +
  647 + if bld.env["USE_DTRACE"]:
  648 + dtrace = bld.new_task_gen(
  649 + name = "dtrace",
  650 + source = "src/node_provider.d",
  651 + target = "src/node_provider.h",
  652 + rule = "%s -x nolibs -h -o ${TGT} -s ${SRC}" % (bld.env.DTRACE),
  653 + before = "cxx",
  654 + )
  655 +
  656 + if bld.env["USE_DEBUG"]:
  657 + dtrace_g = dtrace.clone("debug")
  658 +
  659 + bld.install_files('/usr/lib/dtrace', 'src/node.d')
  660 +
  661 + if sys.platform.startswith("sunos"):
  662 + #
  663 + # The USDT DTrace provider works slightly differently on Solaris than on
  664 + # the Mac; on Solaris, any objects that have USDT DTrace probes must be
  665 + # post-processed with the DTrace command. (This is not true on the
  666 + # Mac, which has first-class linker support for USDT probes.) On
  667 + # Solaris, we must therefore post-process our object files. Waf doesn't
  668 + # seem to really have a notion for this, so we inject a task after
  669 + # compiling and before linking, and then find all of the node object
  670 + # files and shuck them off to dtrace (which will modify them in place
  671 + # as appropriate).
  672 + #
  673 + def dtrace_postprocess(task):
  674 + abspath = bld.srcnode.abspath(bld.env_of_name(task.env.variant()))
  675 + objs = glob.glob(abspath + 'src/*.o')
  676 +
  677 + Utils.exec_command('%s -G -x nolibs -s %s %s' % (task.env.DTRACE,
  678 + task.inputs[0].srcpath(task.env), ' '.join(objs)))
  679 +
  680 + dtracepost = bld.new_task_gen(
  681 + name = "dtrace-postprocess",
  682 + source = "src/node_provider.d",
  683 + always = True,
  684 + before = "cxx_link",
  685 + after = "cxx",
  686 + )
  687 +
  688 + bld.env.append_value('LINKFLAGS', 'node_provider.o')
  689 +
  690 + #
  691 + # Note that for the same (mysterious) issue outlined above with respect
  692 + # to assigning the rule to native_cc/native_cc_debug, we must apply the
  693 + # rule to dtracepost/dtracepost_g only after they have been cloned. We
  694 + # also must put node_provider.o on the link line, but because we
  695 + # (apparently?) lack LINKFLAGS in debug, we (shamelessly) stowaway on
  696 + # LINKFLAGS_V8_G.
  697 + #
  698 + if bld.env["USE_DEBUG"]:
  699 + dtracepost_g = dtracepost.clone("debug")
  700 + dtracepost_g.rule = dtrace_postprocess
  701 + bld.env_of_name("debug").append_value('LINKFLAGS_V8_G',
  702 + 'node_provider.o')
  703 +
  704 + dtracepost.rule = dtrace_postprocess
613 705
614 706 ### node lib
615 707 node = bld.new_task_gen("cxx", product_type)
@@ -639,6 +731,7 @@ def build(bld):
639 731 src/node_timer.cc
640 732 src/node_script.cc
641 733 src/node_os.cc
  734 + src/node_dtrace.cc
642 735 """
643 736
644 737 if sys.platform.startswith("win32"):

0 comments on commit 068b733

Please sign in to comment.
Something went wrong with that request. Please try again.