From 06810b29fae7c549c3a016f2faa3cf6b444a1149 Mon Sep 17 00:00:00 2001 From: Jan Wynholds Date: Tue, 9 Oct 2012 15:09:07 -0700 Subject: [PATCH] tracing: add systemtap support --- configure | 6 ++- node.gyp | 33 +++++++++++++++- src/node.cc | 7 +++- src/node_dtrace.cc | 92 ++++++++++++++++++++++++++++++++++++-------- src/node_systemtap.d | 51 ++++++++++++++++++++++++ 5 files changed, 168 insertions(+), 21 deletions(-) create mode 100644 src/node_systemtap.d diff --git a/configure b/configure index e13e4245cd..ad7aa933a0 100755 --- a/configure +++ b/configure @@ -377,8 +377,12 @@ def configure_node(o): # SunOS, and we haven't implemented it.) if sys.platform.startswith('sunos'): o['variables']['node_use_dtrace'] = b(not options.without_dtrace) + elif sys.platform.startswith('linux'): + o['variables']['node_use_dtrace'] = 'false' + o['variables']['node_use_systemtap'] = b(not options.without_dtrace) elif b(options.with_dtrace) == 'true': - raise Exception('DTrace is currently only supported on SunOS systems.') + raise Exception( + 'DTrace is currently only supported on SunOS or Linux systems.') else: o['variables']['node_use_dtrace'] = 'false' diff --git a/node.gyp b/node.gyp index c7928d6787..cefa502ce3 100644 --- a/node.gyp +++ b/node.gyp @@ -9,6 +9,7 @@ 'node_shared_v8%': 'false', 'node_shared_zlib%': 'false', 'node_use_openssl%': 'true', + 'node_use_systemtap%': 'false', 'node_shared_openssl%': 'false', 'library_files': [ 'src/node.js', @@ -146,7 +147,6 @@ }, { 'defines': [ 'HAVE_OPENSSL=0' ] }], - [ 'node_use_dtrace=="true"', { 'defines': [ 'HAVE_DTRACE=1' ], 'dependencies': [ 'node_dtrace_header' ], @@ -168,6 +168,15 @@ } ] ], } ], + [ 'node_use_systemtap=="true"', { + 'defines': [ 'HAVE_SYSTEMTAP=1', 'STAP_SDT_V1=1' ], + 'dependencies': [ 'node_systemtap_header' ], + 'include_dirs': [ '<(SHARED_INTERMEDIATE_DIR)' ], + 'sources': [ + 'src/node_dtrace.cc', + '<(SHARED_INTERMEDIATE_DIR)/node_systemtap.h', + ], + } ], [ 'node_use_etw=="true"', { 'defines': [ 'HAVE_ETW=1' ], 'dependencies': [ 'node_etw' ], @@ -285,7 +294,10 @@ # action? 'conditions': [ - [ 'node_use_dtrace=="true" or node_use_etw=="true"', { + [ 'node_use_dtrace=="true"' + ' or node_use_etw=="true"' + ' or node_use_systemtap=="true"', + { 'action': [ 'python', 'tools/js2c.py', @@ -322,6 +334,23 @@ } ] ] }, + { + 'target_name': 'node_systemtap_header', + 'type': 'none', + 'conditions': [ + [ 'node_use_systemtap=="true"', { + 'actions': [ + { + 'action_name': 'node_systemtap_header', + 'inputs': [ 'src/node_systemtap.d' ], + 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/node_systemtap.h' ], + 'action': [ 'dtrace', '-h', '-C', '-s', '<@(_inputs)', + '-o', '<@(_outputs)' ] + } + ] + } ] + ] + }, { 'target_name': 'node_dtrace_provider', 'type': 'none', diff --git a/src/node.cc b/src/node.cc index f8801d8b20..60cc19c4eb 100644 --- a/src/node.cc +++ b/src/node.cc @@ -27,7 +27,7 @@ #include "uv.h" #include "v8-debug.h" -#if defined HAVE_DTRACE || defined HAVE_ETW +#if defined HAVE_DTRACE || defined HAVE_ETW || defined HAVE_SYSTEMTAP # include "node_dtrace.h" #endif @@ -72,6 +72,9 @@ typedef int mode_t; #if HAVE_OPENSSL # include "node_crypto.h" #endif +#if HAVE_SYSTEMTAP +#include "node_systemtap.h" +#endif #include "node_script.h" #include "v8_typed_array.h" @@ -2307,7 +2310,7 @@ void Load(Handle process_l) { Local global = v8::Context::GetCurrent()->Global(); Local args[1] = { Local::New(process_l) }; -#if defined HAVE_DTRACE || defined HAVE_ETW +#if defined HAVE_DTRACE || defined HAVE_ETW || defined HAVE_SYSTEMTAP InitDTrace(global); #endif diff --git a/src/node_dtrace.cc b/src/node_dtrace.cc index e560999c52..df42af2a97 100644 --- a/src/node_dtrace.cc +++ b/src/node_dtrace.cc @@ -19,14 +19,23 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -#include "node_dtrace.h" -#include #ifdef HAVE_DTRACE +#include "node_dtrace.h" +#include #include "node_provider.h" #elif HAVE_ETW +#include "node_dtrace.h" +#include #include "node_win32_etw_provider.h" #include "node_win32_etw_provider-inl.h" +#elif HAVE_SYSTEMTAP +#include +#include +#include +#include +#include "node_systemtap.h" +#include "node_dtrace.h" #else #define NODE_HTTP_SERVER_REQUEST(arg0, arg1) #define NODE_HTTP_SERVER_REQUEST_ENABLED() (0) @@ -118,67 +127,86 @@ using namespace v8; Handle DTRACE_NET_SERVER_CONNECTION(const Arguments& args) { +#ifndef HAVE_SYSTEMTAP if (!NODE_NET_SERVER_CONNECTION_ENABLED()) return Undefined(); +#endif HandleScope scope; SLURP_CONNECTION(args[0], conn); +#ifdef HAVE_SYSTEMTAP + NODE_NET_SERVER_CONNECTION(conn.fd, conn.remote, conn.port, \ + conn.buffered); +#else NODE_NET_SERVER_CONNECTION(&conn); +#endif return Undefined(); } Handle DTRACE_NET_STREAM_END(const Arguments& args) { +#ifndef HAVE_SYSTEMTAP if (!NODE_NET_STREAM_END_ENABLED()) return Undefined(); +#endif HandleScope scope; SLURP_CONNECTION(args[0], conn); +#ifdef HAVE_SYSTEMTAP + NODE_NET_STREAM_END(conn.fd, conn.remote, conn.port, conn.buffered); +#else NODE_NET_STREAM_END(&conn); +#endif return Undefined(); } Handle DTRACE_NET_SOCKET_READ(const Arguments& args) { +#ifndef HAVE_SYSTEMTAP if (!NODE_NET_SOCKET_READ_ENABLED()) return Undefined(); +#endif HandleScope scope; - int nbytes; SLURP_CONNECTION(args[0], conn); +#ifdef HAVE_SYSTEMTAP + NODE_NET_SOCKET_READ(conn.fd, conn.remote, conn.port, conn.buffered); +#else if (!args[1]->IsNumber()) { return (ThrowException(Exception::Error(String::New("expected " "argument 1 to be number of bytes")))); } - - nbytes = args[1]->Int32Value(); - + int nbytes = args[1]->Int32Value(); NODE_NET_SOCKET_READ(&conn, nbytes); +#endif return Undefined(); } Handle DTRACE_NET_SOCKET_WRITE(const Arguments& args) { +#ifndef HAVE_SYSTEMTAP if (!NODE_NET_SOCKET_WRITE_ENABLED()) return Undefined(); +#endif HandleScope scope; - int nbytes; SLURP_CONNECTION(args[0], conn); +#ifdef HAVE_SYSTEMTAP + NODE_NET_SOCKET_WRITE(conn.fd, conn.remote, conn.port, conn.buffered); +#else if (!args[1]->IsNumber()) { return (ThrowException(Exception::Error(String::New("expected " "argument 1 to be number of bytes")))); } - - nbytes = args[1]->Int32Value(); - + int nbytes = args[1]->Int32Value(); NODE_NET_SOCKET_WRITE(&conn, nbytes); +#endif return Undefined(); } @@ -186,8 +214,10 @@ Handle DTRACE_NET_SOCKET_WRITE(const Arguments& args) { Handle DTRACE_HTTP_SERVER_REQUEST(const Arguments& args) { node_dtrace_http_server_request_t req; +#ifndef HAVE_SYSTEMTAP if (!NODE_HTTP_SERVER_REQUEST_ENABLED()) return Undefined(); +#endif HandleScope scope; @@ -213,18 +243,29 @@ Handle DTRACE_HTTP_SERVER_REQUEST(const Arguments& args) { SLURP_CONNECTION(args[1], conn); +#ifdef HAVE_SYSTEMTAP + NODE_HTTP_SERVER_REQUEST(&req, conn.fd, conn.remote, conn.port, \ + conn.buffered); +#else NODE_HTTP_SERVER_REQUEST(&req, &conn); +#endif return Undefined(); } Handle DTRACE_HTTP_SERVER_RESPONSE(const Arguments& args) { +#ifndef HAVE_SYSTEMTAP if (!NODE_HTTP_SERVER_RESPONSE_ENABLED()) return Undefined(); +#endif HandleScope scope; SLURP_CONNECTION(args[0], conn); +#ifdef HAVE_SYSTEMTAP + NODE_HTTP_SERVER_RESPONSE(conn.fd, conn.remote, conn.port, conn.buffered); +#else NODE_HTTP_SERVER_RESPONSE(&conn); +#endif return Undefined(); } @@ -233,8 +274,10 @@ Handle DTRACE_HTTP_CLIENT_REQUEST(const Arguments& args) { node_dtrace_http_client_request_t req; char *header; +#ifndef HAVE_SYSTEMTAP if (!NODE_HTTP_CLIENT_REQUEST_ENABLED()) return Undefined(); +#endif HandleScope scope; @@ -263,26 +306,40 @@ Handle DTRACE_HTTP_CLIENT_REQUEST(const Arguments& args) { *header = '\0'; SLURP_CONNECTION_HTTP_CLIENT(args[1], conn); +#ifdef HAVE_SYSTEMTAP + NODE_HTTP_CLIENT_REQUEST(&req, conn.fd, conn.remote, conn.port, \ + conn.buffered); +#else NODE_HTTP_CLIENT_REQUEST(&req, &conn); +#endif return Undefined(); } Handle DTRACE_HTTP_CLIENT_RESPONSE(const Arguments& args) { +#ifndef HAVE_SYSTEMTAP if (!NODE_HTTP_CLIENT_RESPONSE_ENABLED()) return Undefined(); - +#endif HandleScope scope; SLURP_CONNECTION_HTTP_CLIENT_RESPONSE(args[0], args[1], conn); +#ifdef HAVE_SYSTEMTAP + NODE_HTTP_CLIENT_RESPONSE(conn.fd, conn.remote, conn.port, conn.buffered); +#else NODE_HTTP_CLIENT_RESPONSE(&conn); +#endif return Undefined(); } -#define NODE_PROBE(name) #name, name +#define NODE_PROBE(name) #name, name, Persistent() static int dtrace_gc_start(GCType type, GCCallbackFlags flags) { +#ifdef HAVE_SYSTEMTAP + NODE_GC_START(); +#else NODE_GC_START(type, flags); +#endif /* * We avoid the tail-call elimination of the USDT probe (which screws up * args) by forcing a return of 0. @@ -291,7 +348,11 @@ static int dtrace_gc_start(GCType type, GCCallbackFlags flags) { } static int dtrace_gc_done(GCType type, GCCallbackFlags flags) { +#ifdef HAVE_SYSTEMTAP + NODE_GC_DONE(); +#else NODE_GC_DONE(type, flags); +#endif return 0; } @@ -308,11 +369,10 @@ void InitDTrace(Handle target) { { NODE_PROBE(DTRACE_HTTP_SERVER_REQUEST) }, { NODE_PROBE(DTRACE_HTTP_SERVER_RESPONSE) }, { NODE_PROBE(DTRACE_HTTP_CLIENT_REQUEST) }, - { NODE_PROBE(DTRACE_HTTP_CLIENT_RESPONSE) }, - { NULL } + { NODE_PROBE(DTRACE_HTTP_CLIENT_RESPONSE) } }; - for (int i = 0; tab[i].name != NULL; i++) { + for (unsigned int i = 0; i < ARRAY_SIZE(tab); i++) { tab[i].templ = Persistent::New( FunctionTemplate::New(tab[i].func)); target->Set(String::NewSymbol(tab[i].name), tab[i].templ->GetFunction()); @@ -322,7 +382,7 @@ void InitDTrace(Handle target) { init_etw(); #endif -#if defined HAVE_DTRACE || defined HAVE_ETW +#if defined HAVE_DTRACE || defined HAVE_ETW || defined HAVE_SYSTEMTAP v8::V8::AddGCPrologueCallback((GCPrologueCallback)dtrace_gc_start); v8::V8::AddGCEpilogueCallback((GCEpilogueCallback)dtrace_gc_done); #endif diff --git a/src/node_systemtap.d b/src/node_systemtap.d new file mode 100644 index 0000000000..5247cc7fc2 --- /dev/null +++ b/src/node_systemtap.d @@ -0,0 +1,51 @@ +// Copyright Joyent, Inc. 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. + +// Hints: +// This .d defines compiled in probes +// probes are handles (untyped pointers) +// v8 forward declared objs (dtrace_connection_t) are defined +// in node_dtrace.cc which builds an InitDtrace object which +// gets populated with the probes +// The probes gather the following: +// PROBE_REQUEST(req, fd, remote, port, buffered) +// PROBE_OTHER(fd, remote, port, buffered) +// other notes: +// using any PROBE_ENABLED() macros in dtrace.cc sdt broke it +// can only pass strings/ints/primitives not dtrace_connection_t +// conn or other structs +// verify probe existence by using +// $ stap -l 'process("out/Release/node").mark("*")' +// TODO: write .stp scripts (node.stp, node_v8ustack.stp + ???) + + +provider node { + probe http__client__request(string, int, string, int, int); + probe http__client__response(int, string, int, int); + probe http__server__request(string, int, string, int, int); + probe http__server__response(int, string, int, int); + probe net__server__connection(int, string, int, int); + probe net__socket__read(int, string, int, int); + probe net__socket__write(int, string, int, int); + probe net__stream__end(int, string, int, int); + probe gc__done(); + probe gc__start(); +};