// 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. #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_provider.h" #include "node_dtrace.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_HTTP_CLIENT_REQUEST(arg0, arg1) #define NODE_HTTP_CLIENT_REQUEST_ENABLED() (0) #define NODE_HTTP_CLIENT_RESPONSE(arg0) #define NODE_HTTP_CLIENT_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) #define NODE_NET_SOCKET_READ(arg0, arg1) #define NODE_NET_SOCKET_READ_ENABLED() (0) #define NODE_NET_SOCKET_WRITE(arg0, arg1) #define NODE_NET_SOCKET_WRITE_ENABLED() (0) #define NODE_GC_START(arg0, arg1) #define NODE_GC_DONE(arg0, arg1) #endif namespace node { using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::GCCallbackFlags; using v8::GCEpilogueCallback; using v8::GCPrologueCallback; using v8::GCType; using v8::Handle; using v8::HandleScope; using v8::Local; using v8::Object; using v8::String; using v8::Value; #define SLURP_STRING(obj, member, valp) \ if (!(obj)->IsObject()) { \ return ThrowError( \ "expected object for " #obj " to contain string member " #member); \ } \ String::Utf8Value _##member(obj->Get(String::New(#member))); \ if ((*(const char **)valp = *_##member) == NULL) \ *(const char **)valp = ""; #define SLURP_INT(obj, member, valp) \ if (!(obj)->IsObject()) { \ return ThrowError( \ "expected object for " #obj " to contain integer member " #member); \ } \ *valp = obj->Get(String::New(#member))->ToInteger()->Value(); #define SLURP_OBJECT(obj, member, valp) \ if (!(obj)->IsObject()) { \ return ThrowError( \ "expected object for " #obj " to contain object member " #member); \ } \ *valp = Local::Cast(obj->Get(String::New(#member))); #define SLURP_CONNECTION(arg, conn) \ if (!(arg)->IsObject()) { \ return ThrowError( \ "expected argument " #arg " to be a connection object"); \ } \ node_dtrace_connection_t conn; \ Local _##conn = Local::Cast(arg); \ Local _handle = (_##conn)->Get(String::New("_handle")); \ if (_handle->IsObject()) { \ SLURP_INT(_handle.As(), fd, &conn.fd); \ } else { \ conn.fd = -1; \ } \ SLURP_STRING(_##conn, remoteAddress, &conn.remote); \ SLURP_INT(_##conn, remotePort, &conn.port); \ SLURP_INT(_##conn, bufferSize, &conn.buffered); #define SLURP_CONNECTION_HTTP_CLIENT(arg, conn) \ if (!(arg)->IsObject()) { \ return ThrowError( \ "expected argument " #arg " to be a connection object"); \ } \ node_dtrace_connection_t conn; \ Local _##conn = Local::Cast(arg); \ SLURP_INT(_##conn, fd, &conn.fd); \ SLURP_STRING(_##conn, host, &conn.remote); \ SLURP_INT(_##conn, port, &conn.port); \ SLURP_INT(_##conn, bufferSize, &conn.buffered); #define SLURP_CONNECTION_HTTP_CLIENT_RESPONSE(arg0, arg1, conn) \ if (!(arg0)->IsObject()) { \ return ThrowError( \ "expected argument " #arg0 " to be a connection object"); \ } \ if (!(arg1)->IsObject()) { \ return ThrowError( \ "expected argument " #arg1 " to be a connection object"); \ } \ node_dtrace_connection_t conn; \ Local _##conn = Local::Cast(arg0); \ SLURP_INT(_##conn, fd, &conn.fd); \ SLURP_INT(_##conn, bufferSize, &conn.buffered); \ _##conn = Local::Cast(arg1); \ SLURP_STRING(_##conn, host, &conn.remote); \ SLURP_INT(_##conn, port, &conn.port); void DTRACE_NET_SERVER_CONNECTION(const FunctionCallbackInfo& args) { #ifndef HAVE_SYSTEMTAP if (!NODE_NET_SERVER_CONNECTION_ENABLED()) return; #endif HandleScope scope(node_isolate); SLURP_CONNECTION(args[0], conn); NODE_NET_SERVER_CONNECTION(&conn, conn.remote, conn.port, conn.fd); } void DTRACE_NET_STREAM_END(const FunctionCallbackInfo& args) { #ifndef HAVE_SYSTEMTAP if (!NODE_NET_STREAM_END_ENABLED()) return; #endif HandleScope scope(node_isolate); SLURP_CONNECTION(args[0], conn); NODE_NET_STREAM_END(&conn, conn.remote, conn.port, conn.fd); } void DTRACE_NET_SOCKET_READ(const FunctionCallbackInfo& args) { #ifndef HAVE_SYSTEMTAP if (!NODE_NET_SOCKET_READ_ENABLED()) return; #endif HandleScope scope(node_isolate); SLURP_CONNECTION(args[0], conn); if (!args[1]->IsNumber()) { return ThrowError("expected argument 1 to be number of bytes"); } int nbytes = args[1]->Int32Value(); NODE_NET_SOCKET_READ(&conn, nbytes, conn.remote, conn.port, conn.fd); } void DTRACE_NET_SOCKET_WRITE(const FunctionCallbackInfo& args) { #ifndef HAVE_SYSTEMTAP if (!NODE_NET_SOCKET_WRITE_ENABLED()) return; #endif HandleScope scope(node_isolate); SLURP_CONNECTION(args[0], conn); if (!args[1]->IsNumber()) { return ThrowError("expected argument 1 to be number of bytes"); } int nbytes = args[1]->Int32Value(); NODE_NET_SOCKET_WRITE(&conn, nbytes, conn.remote, conn.port, conn.fd); } void DTRACE_HTTP_SERVER_REQUEST(const FunctionCallbackInfo& args) { node_dtrace_http_server_request_t req; #ifndef HAVE_SYSTEMTAP if (!NODE_HTTP_SERVER_REQUEST_ENABLED()) return; #endif HandleScope scope(node_isolate); Local arg0 = Local::Cast(args[0]); Local headers; memset(&req, 0, sizeof(req)); req._un.version = 1; SLURP_STRING(arg0, url, &req.url); SLURP_STRING(arg0, method, &req.method); SLURP_OBJECT(arg0, headers, &headers); if (!(headers)->IsObject()) { return ThrowError( "expected object for request to contain string member headers"); } Local strfwdfor = headers->Get(String::New("x-forwarded-for")); String::Utf8Value fwdfor(strfwdfor); if (!strfwdfor->IsString() || (req.forwardedFor = *fwdfor) == NULL) req.forwardedFor = const_cast(""); SLURP_CONNECTION(args[1], conn); NODE_HTTP_SERVER_REQUEST(&req, &conn, conn.remote, conn.port, req.method, \ req.url, conn.fd); } void DTRACE_HTTP_SERVER_RESPONSE(const FunctionCallbackInfo& args) { #ifndef HAVE_SYSTEMTAP if (!NODE_HTTP_SERVER_RESPONSE_ENABLED()) return; #endif HandleScope scope(node_isolate); SLURP_CONNECTION(args[0], conn); NODE_HTTP_SERVER_RESPONSE(&conn, conn.remote, conn.port, conn.fd); } void DTRACE_HTTP_CLIENT_REQUEST(const FunctionCallbackInfo& args) { node_dtrace_http_client_request_t req; char *header; #ifndef HAVE_SYSTEMTAP if (!NODE_HTTP_CLIENT_REQUEST_ENABLED()) return; #endif HandleScope scope(node_isolate); /* * For the method and URL, we're going to dig them out of the header. This * is not as efficient as it could be, but we would rather not force the * caller here to retain their method and URL until the time at which * DTRACE_HTTP_CLIENT_REQUEST can be called. */ Local arg0 = Local::Cast(args[0]); SLURP_STRING(arg0, _header, &header); req.method = header; while (*header != '\0' && *header != ' ') header++; if (*header != '\0') *header++ = '\0'; req.url = header; while (*header != '\0' && *header != ' ') header++; *header = '\0'; SLURP_CONNECTION_HTTP_CLIENT(args[1], conn); NODE_HTTP_CLIENT_REQUEST(&req, &conn, conn.remote, conn.port, req.method, \ req.url, conn.fd); } void DTRACE_HTTP_CLIENT_RESPONSE(const FunctionCallbackInfo& args) { #ifndef HAVE_SYSTEMTAP if (!NODE_HTTP_CLIENT_RESPONSE_ENABLED()) return; #endif HandleScope scope(node_isolate); SLURP_CONNECTION_HTTP_CLIENT_RESPONSE(args[0], args[1], conn); NODE_HTTP_CLIENT_RESPONSE(&conn, conn.remote, conn.port, conn.fd); } static int dtrace_gc_start(GCType type, GCCallbackFlags flags) { NODE_GC_START(type, flags); /* * We avoid the tail-call elimination of the USDT probe (which screws up * args) by forcing a return of 0. */ return 0; } static int dtrace_gc_done(GCType type, GCCallbackFlags flags) { NODE_GC_DONE(type, flags); return 0; } void InitDTrace(Handle target) { HandleScope scope(node_isolate); static struct { const char *name; void (*func)(const FunctionCallbackInfo&); } tab[] = { #define NODE_PROBE(name) #name, name { NODE_PROBE(DTRACE_NET_SERVER_CONNECTION) }, { NODE_PROBE(DTRACE_NET_STREAM_END) }, { NODE_PROBE(DTRACE_NET_SOCKET_READ) }, { NODE_PROBE(DTRACE_NET_SOCKET_WRITE) }, { NODE_PROBE(DTRACE_HTTP_SERVER_REQUEST) }, { NODE_PROBE(DTRACE_HTTP_SERVER_RESPONSE) }, { NODE_PROBE(DTRACE_HTTP_CLIENT_REQUEST) }, { NODE_PROBE(DTRACE_HTTP_CLIENT_RESPONSE) } #undef NODE_PROBE }; for (unsigned int i = 0; i < ARRAY_SIZE(tab); i++) { Local key = String::New(tab[i].name); Local val = FunctionTemplate::New(tab[i].func)->GetFunction(); target->Set(key, val); } #ifdef HAVE_ETW init_etw(); #endif #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 } }