mirror of https://github.com/lukechilds/node.git
Browse Source
This commit adds the ability to enable userspace tracing with lttng in io.js. It adds tracepoints for all the equivalent dtrace and ETW tracepoints. To use these tracepoints enable --with-lttng on linux. PR-URL: https://github.com/iojs/io.js/pull/702 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Ryan Graham <ryan@strongloop.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>v1.8.0-commit
Glen Keane
10 years ago
committed by
Ben Noordhuis
14 changed files with 602 additions and 2 deletions
@ -0,0 +1,266 @@ |
|||
#include "util.h" |
|||
|
|||
#ifdef HAVE_LTTNG |
|||
#include "node_lttng.h" |
|||
#include "node_lttng_provider.h" |
|||
#include <string.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_GC_START(arg0, arg1, arg2) |
|||
#define NODE_GC_DONE(arg0, arg1, arg2) |
|||
#endif |
|||
|
|||
#include "env.h" |
|||
#include "env-inl.h" |
|||
|
|||
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::Isolate; |
|||
using v8::Local; |
|||
using v8::Object; |
|||
using v8::String; |
|||
using v8::Value; |
|||
|
|||
#define SLURP_STRING(obj, member, valp) \ |
|||
if (!(obj)->IsObject()) { \ |
|||
return env->ThrowError( \ |
|||
"expected object for " #obj " to contain string member " #member); \ |
|||
} \ |
|||
node::Utf8Value _##member(env->isolate(), \ |
|||
obj->Get(OneByteString(env->isolate(), #member))); \ |
|||
if ((*(const char **)valp = *_##member) == nullptr) \ |
|||
*(const char **)valp = "<unknown>"; |
|||
|
|||
#define SLURP_INT(obj, member, valp) \ |
|||
if (!(obj)->IsObject()) { \ |
|||
return env->ThrowError( \ |
|||
"expected object for " #obj " to contain integer member " #member); \ |
|||
} \ |
|||
*valp = obj->Get(OneByteString(env->isolate(), #member)) \ |
|||
->ToInteger(env->isolate())->Value(); |
|||
|
|||
#define SLURP_OBJECT(obj, member, valp) \ |
|||
if (!(obj)->IsObject()) { \ |
|||
return env->ThrowError( \ |
|||
"expected object for " #obj " to contain object member " #member); \ |
|||
} \ |
|||
*valp = Local<Object>::Cast(obj->Get(OneByteString(env->isolate(), #member))); |
|||
|
|||
#define SLURP_CONNECTION(arg, conn) \ |
|||
if (!(arg)->IsObject()) { \ |
|||
return env->ThrowError( \ |
|||
"expected argument " #arg " to be a connection object"); \ |
|||
} \ |
|||
node_lttng_connection_t conn; \ |
|||
Local<Object> _##conn = Local<Object>::Cast(arg); \ |
|||
Local<Value> _handle = \ |
|||
(_##conn)->Get(FIXED_ONE_BYTE_STRING(env->isolate(), "_handle")); \ |
|||
if (_handle->IsObject()) { \ |
|||
SLURP_INT(_handle.As<Object>(), 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 env->ThrowError( \ |
|||
"expected argument " #arg " to be a connection object"); \ |
|||
} \ |
|||
node_lttng_connection_t conn; \ |
|||
Local<Object> _##conn = Local<Object>::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 env->ThrowError( \ |
|||
"expected argument " #arg0 " to be a connection object"); \ |
|||
} \ |
|||
if (!(arg1)->IsObject()) { \ |
|||
return env->ThrowError( \ |
|||
"expected argument " #arg1 " to be a connection object"); \ |
|||
} \ |
|||
node_lttng_connection_t conn; \ |
|||
Local<Object> _##conn = Local<Object>::Cast(arg0); \ |
|||
SLURP_INT(_##conn, fd, &conn.fd); \ |
|||
SLURP_INT(_##conn, bufferSize, &conn.buffered); \ |
|||
_##conn = Local<Object>::Cast(arg1); \ |
|||
SLURP_STRING(_##conn, host, &conn.remote); \ |
|||
SLURP_INT(_##conn, port, &conn.port); |
|||
|
|||
|
|||
void LTTNG_NET_SERVER_CONNECTION(const FunctionCallbackInfo<Value>& args) { |
|||
if (!NODE_NET_SERVER_CONNECTION_ENABLED()) |
|||
return; |
|||
Environment* env = Environment::GetCurrent(args); |
|||
SLURP_CONNECTION(args[0], conn); |
|||
NODE_NET_SERVER_CONNECTION(&conn, conn.remote, conn.port, conn.fd); |
|||
} |
|||
|
|||
|
|||
void LTTNG_NET_STREAM_END(const FunctionCallbackInfo<Value>& args) { |
|||
if (!NODE_NET_STREAM_END_ENABLED()) |
|||
return; |
|||
Environment* env = Environment::GetCurrent(args); |
|||
SLURP_CONNECTION(args[0], conn); |
|||
NODE_NET_STREAM_END(&conn, conn.remote, conn.port, conn.fd); |
|||
} |
|||
|
|||
|
|||
void LTTNG_HTTP_SERVER_REQUEST(const FunctionCallbackInfo<Value>& args) { |
|||
node_lttng_http_server_request_t req; |
|||
|
|||
if (!NODE_HTTP_SERVER_REQUEST_ENABLED()) |
|||
return; |
|||
|
|||
if (!args[0]->IsObject()) |
|||
return; |
|||
|
|||
Environment* env = Environment::GetCurrent(args); |
|||
Local<Object> arg0 = args[0].As<Object>(); |
|||
Local<Object> 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 env->ThrowError( |
|||
"expected object for request to contain string member headers"); |
|||
} |
|||
|
|||
Local<Value> strfwdfor = headers->Get(env->x_forwarded_string()); |
|||
node::Utf8Value fwdfor(env->isolate(), strfwdfor); |
|||
req.forwarded_for = *fwdfor; |
|||
|
|||
if (!strfwdfor->IsString() || req.forwarded_for == nullptr) |
|||
req.forwarded_for = ""; |
|||
|
|||
SLURP_CONNECTION(args[1], conn); |
|||
NODE_HTTP_SERVER_REQUEST(&req, &conn, conn.remote, conn.port, req.method, \ |
|||
req.url, conn.fd); |
|||
} |
|||
|
|||
|
|||
void LTTNG_HTTP_SERVER_RESPONSE(const FunctionCallbackInfo<Value>& args) { |
|||
if (!NODE_HTTP_SERVER_RESPONSE_ENABLED()) |
|||
return; |
|||
Environment* env = Environment::GetCurrent(args); |
|||
SLURP_CONNECTION(args[0], conn); |
|||
NODE_HTTP_SERVER_RESPONSE(&conn, conn.remote, conn.port, conn.fd); |
|||
} |
|||
|
|||
|
|||
void LTTNG_HTTP_CLIENT_REQUEST(const FunctionCallbackInfo<Value>& args) { |
|||
node_lttng_http_client_request_t req; |
|||
char* header; |
|||
|
|||
if (!NODE_HTTP_CLIENT_REQUEST_ENABLED()) |
|||
return; |
|||
|
|||
Environment* env = Environment::GetCurrent(args); |
|||
|
|||
/*
|
|||
* 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 |
|||
* LTTNG_HTTP_CLIENT_REQUEST can be called. |
|||
*/ |
|||
Local<Object> arg0 = args[0].As<Object>(); |
|||
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 LTTNG_HTTP_CLIENT_RESPONSE(const FunctionCallbackInfo<Value>& args) { |
|||
if (!NODE_HTTP_CLIENT_RESPONSE_ENABLED()) |
|||
return; |
|||
Environment* env = Environment::GetCurrent(args); |
|||
SLURP_CONNECTION_HTTP_CLIENT_RESPONSE(args[0], args[1], conn); |
|||
NODE_HTTP_CLIENT_RESPONSE(&conn, conn.remote, conn.port, conn.fd); |
|||
} |
|||
|
|||
|
|||
void lttng_gc_start(Isolate* isolate, GCType type, GCCallbackFlags flags) { |
|||
NODE_GC_START(type, flags, isolate); |
|||
} |
|||
|
|||
|
|||
void lttng_gc_done(Isolate* isolate, GCType type, GCCallbackFlags flags) { |
|||
NODE_GC_DONE(type, flags, isolate); |
|||
} |
|||
|
|||
void InitLTTNG(Environment* env, Handle<Object> target) { |
|||
HandleScope scope(env->isolate()); |
|||
|
|||
static struct { |
|||
const char *name; |
|||
void (*func)(const FunctionCallbackInfo<Value>&); |
|||
} tab[] = { |
|||
#define NODE_PROBE(name) #name, name |
|||
{ NODE_PROBE(LTTNG_NET_SERVER_CONNECTION) }, |
|||
{ NODE_PROBE(LTTNG_NET_STREAM_END) }, |
|||
{ NODE_PROBE(LTTNG_HTTP_SERVER_REQUEST) }, |
|||
{ NODE_PROBE(LTTNG_HTTP_SERVER_RESPONSE) }, |
|||
{ NODE_PROBE(LTTNG_HTTP_CLIENT_REQUEST) }, |
|||
{ NODE_PROBE(LTTNG_HTTP_CLIENT_RESPONSE) } |
|||
#undef NODE_PROBE |
|||
}; |
|||
|
|||
for (unsigned int i = 0; i < ARRAY_SIZE(tab); i++) { |
|||
Local<String> key = OneByteString(env->isolate(), tab[i].name); |
|||
Local<Value> val = env->NewFunctionTemplate(tab[i].func)->GetFunction(); |
|||
target->Set(key, val); |
|||
} |
|||
|
|||
#if defined HAVE_LTTNG |
|||
env->isolate()->AddGCPrologueCallback(lttng_gc_start); |
|||
env->isolate()->AddGCEpilogueCallback(lttng_gc_done); |
|||
#endif |
|||
} |
|||
|
|||
} // namespace node
|
@ -0,0 +1,40 @@ |
|||
#ifndef SRC_NODE_LTTNG_H_ |
|||
#define SRC_NODE_LTTNG_H_ |
|||
|
|||
#include "node.h" |
|||
#include "v8.h" |
|||
#include "env.h" |
|||
|
|||
extern "C" { |
|||
typedef struct { |
|||
int32_t fd; |
|||
int32_t port; |
|||
const char* remote; |
|||
int32_t buffered; |
|||
} node_lttng_connection_t; |
|||
|
|||
typedef struct { |
|||
const char* url; |
|||
const char* method; |
|||
} node_lttng_http_client_request_t; |
|||
|
|||
typedef struct { |
|||
union { |
|||
uint32_t version; |
|||
uintptr_t unused; /* for compat. with old 64-bit struct */ |
|||
} _un; |
|||
const char* url; |
|||
const char* method; |
|||
const char* forwarded_for; |
|||
const char* _pad[8]; |
|||
} node_lttng_http_server_request_t; |
|||
|
|||
} // extern "C"
|
|||
|
|||
namespace node { |
|||
|
|||
void InitLTTNG(Environment* env, v8::Handle<v8::Object> target); |
|||
|
|||
} // namespace node
|
|||
|
|||
#endif // SRC_NODE_LTTNG_H_
|
@ -0,0 +1,100 @@ |
|||
#ifndef SRC_NODE_LTTNG_PROVIDER_H_ |
|||
#define SRC_NODE_LTTNG_PROVIDER_H_ |
|||
|
|||
#define TRACEPOINT_CREATE_PROBES |
|||
#define TRACEPOINT_DEFINE |
|||
#include "node_lttng_tp.h" |
|||
|
|||
namespace node { |
|||
|
|||
void NODE_HTTP_SERVER_REQUEST(node_lttng_http_server_request_t* req, |
|||
node_lttng_connection_t* conn, |
|||
const char *remote, int port, |
|||
const char *method, const char *url, |
|||
int fd) { |
|||
tracepoint(node, http_server_request, req->url, req->method, \ |
|||
req->forwarded_for); |
|||
} |
|||
|
|||
void NODE_HTTP_SERVER_RESPONSE(node_lttng_connection_t* conn, |
|||
const char *remote, int port, int fd) { |
|||
tracepoint(node, http_server_response, port, conn->remote, fd); |
|||
} |
|||
|
|||
void NODE_HTTP_CLIENT_REQUEST(node_lttng_http_client_request_t* req, |
|||
node_lttng_connection_t* conn, |
|||
const char *remote, int port, |
|||
const char *method, const char *url, |
|||
int fd) { |
|||
tracepoint(node, http_client_request, req->url, req->method); |
|||
} |
|||
|
|||
void NODE_HTTP_CLIENT_RESPONSE(node_lttng_connection_t* conn, |
|||
const char *remote, int port, int fd) { |
|||
tracepoint(node, http_client_response, port, conn->remote, fd); |
|||
} |
|||
|
|||
void NODE_NET_SERVER_CONNECTION(node_lttng_connection_t* conn, |
|||
const char *remote, int port, int fd) { |
|||
tracepoint(node, net_server_connection, conn->remote, port, fd, \ |
|||
conn->buffered); |
|||
} |
|||
|
|||
void NODE_NET_STREAM_END(node_lttng_connection_t* conn, |
|||
const char *remote, int port, int fd) { |
|||
tracepoint(node, net_stream_end, conn->remote, port, fd); |
|||
} |
|||
|
|||
void NODE_GC_START(v8::GCType type, |
|||
v8::GCCallbackFlags flags, |
|||
v8::Isolate* isolate) { |
|||
const char* typeStr = ""; |
|||
const char* flagsStr = ""; |
|||
if (type == v8::GCType::kGCTypeScavenge) { |
|||
typeStr = "kGCTypeScavenge"; |
|||
} else if (type == v8::GCType::kGCTypeMarkSweepCompact) { |
|||
typeStr = "kGCTypeMarkSweepCompact"; |
|||
} else if (type == v8::GCType::kGCTypeAll) { |
|||
typeStr = "kGCTypeAll"; |
|||
} |
|||
if (flags == v8::GCCallbackFlags::kNoGCCallbackFlags) { |
|||
flagsStr = "kNoGCCallbackFlags"; |
|||
} else if (flags == v8::GCCallbackFlags::kGCCallbackFlagCompacted) { |
|||
flagsStr = "kGCCallbackFlagCompacted"; |
|||
} |
|||
|
|||
tracepoint(node, gc_start, typeStr, flagsStr); |
|||
} |
|||
|
|||
|
|||
void NODE_GC_DONE(v8::GCType type, |
|||
v8::GCCallbackFlags flags, |
|||
v8::Isolate* isolate) { |
|||
const char* typeStr = ""; |
|||
const char* flagsStr = ""; |
|||
if (type == v8::GCType::kGCTypeScavenge) { |
|||
typeStr = "kGCTypeScavenge"; |
|||
} else if (type == v8::GCType::kGCTypeMarkSweepCompact) { |
|||
typeStr = "kGCTypeMarkSweepCompact"; |
|||
} else if (type == v8::GCType::kGCTypeAll) { |
|||
typeStr = "kGCTypeAll"; |
|||
} |
|||
if (flags == v8::GCCallbackFlags::kNoGCCallbackFlags) { |
|||
flagsStr = "kNoGCCallbackFlags"; |
|||
} else if (flags == v8::GCCallbackFlags::kGCCallbackFlagCompacted) { |
|||
flagsStr = "kGCCallbackFlagCompacted"; |
|||
} |
|||
|
|||
tracepoint(node, gc_done, typeStr, flagsStr); |
|||
} |
|||
|
|||
bool NODE_HTTP_SERVER_REQUEST_ENABLED() { return true; } |
|||
bool NODE_HTTP_SERVER_RESPONSE_ENABLED() { return true; } |
|||
bool NODE_HTTP_CLIENT_REQUEST_ENABLED() { return true; } |
|||
bool NODE_HTTP_CLIENT_RESPONSE_ENABLED() { return true; } |
|||
bool NODE_NET_SERVER_CONNECTION_ENABLED() { return true; } |
|||
bool NODE_NET_STREAM_END_ENABLED() { return true; } |
|||
|
|||
} // namespace node
|
|||
|
|||
#endif // SRC_NODE_LTTNG_PROVIDER_H_
|
@ -0,0 +1,130 @@ |
|||
#undef TRACEPOINT_PROVIDER |
|||
#define TRACEPOINT_PROVIDER node |
|||
|
|||
#undef TRACEPOINT_INCLUDE |
|||
#define TRACEPOINT_INCLUDE "./node_lttng_tp.h" |
|||
|
|||
#if !defined(__NODE_LTTNG_TP_H) || defined(TRACEPOINT_HEADER_MULTI_READ) |
|||
#define __NODE_LTTNG_TP_H |
|||
|
|||
#include <lttng/tracepoint.h> |
|||
|
|||
TRACEPOINT_EVENT( |
|||
node, |
|||
http_server_request, |
|||
TP_ARGS( |
|||
const char*, url, |
|||
const char*, method, |
|||
const char*, forwardedFor |
|||
), |
|||
TP_FIELDS( |
|||
ctf_string(url, url) |
|||
ctf_string(method, method) |
|||
ctf_string(forwardedFor, forwardedFor) |
|||
) |
|||
) |
|||
|
|||
TRACEPOINT_EVENT( |
|||
node, |
|||
http_server_response, |
|||
TP_ARGS( |
|||
int, port, |
|||
const char*, remote, |
|||
int, fd |
|||
), |
|||
TP_FIELDS( |
|||
ctf_integer(int, port, port) |
|||
ctf_string(remote, remote) |
|||
ctf_integer(int, fd, fd) |
|||
) |
|||
) |
|||
|
|||
TRACEPOINT_EVENT( |
|||
node, |
|||
http_client_request, |
|||
TP_ARGS( |
|||
const char*, url, |
|||
const char*, method |
|||
), |
|||
TP_FIELDS( |
|||
ctf_string(url, url) |
|||
ctf_string(method, method) |
|||
) |
|||
) |
|||
|
|||
TRACEPOINT_EVENT( |
|||
node, |
|||
http_client_response, |
|||
TP_ARGS( |
|||
int, port, |
|||
const char*, remote, |
|||
int, fd |
|||
), |
|||
TP_FIELDS( |
|||
ctf_integer(int, port, port) |
|||
ctf_string(remote, remote) |
|||
ctf_integer(int, fd, fd) |
|||
) |
|||
) |
|||
|
|||
TRACEPOINT_EVENT( |
|||
node, |
|||
net_server_connection, |
|||
TP_ARGS( |
|||
const char*, remote, |
|||
int, port, |
|||
int, fd, |
|||
int, buffered |
|||
), |
|||
TP_FIELDS( |
|||
ctf_string(remote, remote) |
|||
ctf_integer(int, port, port) |
|||
ctf_integer(int, fd, fd) |
|||
ctf_integer(int, buffered, buffered) |
|||
) |
|||
) |
|||
|
|||
TRACEPOINT_EVENT( |
|||
node, |
|||
net_stream_end, |
|||
TP_ARGS( |
|||
const char*, remote, |
|||
int, port, |
|||
int, fd |
|||
), |
|||
TP_FIELDS( |
|||
ctf_string(remote, remote) |
|||
ctf_integer(int, port, port) |
|||
ctf_integer(int, fd, fd) |
|||
) |
|||
) |
|||
|
|||
TRACEPOINT_EVENT( |
|||
node, |
|||
gc_start, |
|||
TP_ARGS( |
|||
const char*, gctype, |
|||
const char*, gcflags |
|||
), |
|||
TP_FIELDS( |
|||
ctf_string(gctype, gctype) |
|||
ctf_string(gcflags, gcflags) |
|||
) |
|||
) |
|||
|
|||
TRACEPOINT_EVENT( |
|||
node, |
|||
gc_done, |
|||
TP_ARGS( |
|||
const char*, gctype, |
|||
const char*, gcflags |
|||
), |
|||
TP_FIELDS( |
|||
ctf_string(gctype, gctype) |
|||
ctf_string(gcflags, gcflags) |
|||
) |
|||
) |
|||
|
|||
#endif /* __NODE_LTTNG_TP_H */ |
|||
|
|||
#include <lttng/tracepoint-event.h> |
@ -0,0 +1,9 @@ |
|||
# This file is used by tools/js2c.py to preprocess out the LTTNG symbols in |
|||
# builds that don't support LTTNG. This is not used in builds that support |
|||
# LTTNG. |
|||
macro LTTNG_HTTP_CLIENT_REQUEST(x) = ; |
|||
macro LTTNG_HTTP_CLIENT_RESPONSE(x) = ; |
|||
macro LTTNG_HTTP_SERVER_REQUEST(x) = ; |
|||
macro LTTNG_HTTP_SERVER_RESPONSE(x) = ; |
|||
macro LTTNG_NET_SERVER_CONNECTION(x) = ; |
|||
macro LTTNG_NET_STREAM_END(x) = ; |
Loading…
Reference in new issue