Skip to content

Commit

Permalink
windows: add tracing with performance counters
Browse files Browse the repository at this point in the history
Patch by Henry Rawas and Scott Blomquist.
  • Loading branch information
sblom authored and piscisaureus committed Nov 21, 2012
1 parent bc93883 commit f657ce6
Show file tree
Hide file tree
Showing 17 changed files with 827 additions and 30 deletions.
22 changes: 20 additions & 2 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,16 @@ parser.add_option("--without-etw",
dest="without_etw",
help="Build without ETW")

parser.add_option("--with-perfctr",
action="store_true",
dest="with_perfctr",
help="Build with performance counters (default is true on Windows)")

parser.add_option("--without-perfctr",
action="store_true",
dest="without_perfctr",
help="Build without performance counters")

# CHECKME does this still work with recent releases of V8?
parser.add_option("--gdb",
action="store_true",
Expand Down Expand Up @@ -454,7 +464,7 @@ def configure_node(o):
o['variables']['node_use_systemtap'] = b(options.with_dtrace)
if options.systemtap_includes:
o['include_dirs'] += [options.systemtap_includes]
elif b(options.with_dtrace) == 'true':
elif options.with_dtrace:
raise Exception(
'DTrace is currently only supported on SunOS or Linux systems.')
else:
Expand All @@ -467,11 +477,19 @@ def configure_node(o):
# By default, enable ETW on Windows.
if sys.platform.startswith('win32'):
o['variables']['node_use_etw'] = b(not options.without_etw);
elif b(options.with_etw) == 'true':
elif options.with_etw:
raise Exception('ETW is only supported on Windows.')
else:
o['variables']['node_use_etw'] = 'false'

# By default, enable Performance counters on Windows.
if sys.platform.startswith('win32'):
o['variables']['node_use_perfctr'] = b(not options.without_perfctr);
elif options.with_perfctr:
raise Exception('Performance counter is only supported on Windows.')
else:
o['variables']['node_use_perfctr'] = 'false'


def configure_libz(o):
o['variables']['node_shared_zlib'] = b(options.shared_zlib)
Expand Down
4 changes: 4 additions & 0 deletions lib/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -841,9 +841,11 @@ OutgoingMessage.prototype._finish = function() {
assert(this.connection);
if (this instanceof ServerResponse) {
DTRACE_HTTP_SERVER_RESPONSE(this.connection);
COUNTER_HTTP_SERVER_RESPONSE();
} else {
assert(this instanceof ClientRequest);
DTRACE_HTTP_CLIENT_REQUEST(this, this.connection);
COUNTER_HTTP_CLIENT_REQUEST();
}
this.emit('finish');
};
Expand Down Expand Up @@ -1472,6 +1474,7 @@ function parserOnIncomingClient(res, shouldKeepAlive) {


DTRACE_HTTP_CLIENT_RESPONSE(socket, req);
COUNTER_HTTP_CLIENT_RESPONSE();
req.emit('response', res);
req.res = res;
res.req = req;
Expand Down Expand Up @@ -1779,6 +1782,7 @@ function connectionListener(socket) {
debug('server response shouldKeepAlive: ' + shouldKeepAlive);
res.shouldKeepAlive = shouldKeepAlive;
DTRACE_HTTP_SERVER_REQUEST(req, socket);
COUNTER_HTTP_SERVER_REQUEST();

if (socket._httpMessage) {
// There are already pending outgoing res, append.
Expand Down
2 changes: 2 additions & 0 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ Socket.prototype._destroy = function(exception, cb) {
this.destroyed = true;

if (this.server) {
COUNTER_NET_SERVER_CONNECTION_CLOSE(this);
this.server._connections--;
if (this.server._emitCloseIfDrained) {
this.server._emitCloseIfDrained();
Expand Down Expand Up @@ -1054,6 +1055,7 @@ function onconnection(clientHandle) {
socket.server = self;

DTRACE_NET_SERVER_CONNECTION(socket);
COUNTER_NET_SERVER_CONNECTION(socket);
self.emit('connection', socket);
socket.emit('connect');
}
Expand Down
64 changes: 44 additions & 20 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
'werror': '',
'node_use_dtrace%': 'false',
'node_use_etw%': 'false',
'node_use_perfctr%': 'false',
'node_shared_v8%': 'false',
'node_shared_zlib%': 'false',
'node_shared_http_parser%': 'false',
Expand Down Expand Up @@ -189,6 +190,17 @@
'<(SHARED_INTERMEDIATE_DIR)/node_etw_provider.rc',
]
} ],
[ 'node_use_perfctr=="true"', {
'defines': [ 'HAVE_PERFCTR=1' ],
'dependencies': [ 'node_perfctr' ],
'sources': [
'src/node_win32_perfctr_provider.h',
'src/node_win32_perfctr_provider.cc',
'src/node_counters.cc',
'src/node_counters.h',
'<(SHARED_INTERMEDIATE_DIR)/node_perfctr_provider.rc',
]
} ],
[ 'node_shared_v8=="false"', {
'sources': [
'deps/v8/include/v8.h',
Expand Down Expand Up @@ -287,48 +299,61 @@
} ]
]
},
# generate perf counter header and resource files
{
'target_name': 'node_perfctr',
'type': 'none',
'conditions': [
[ 'node_use_perfctr=="true"', {
'actions': [
{
'action_name': 'node_perfctr_man',
'inputs': [ 'src/res/node_perfctr_provider.man' ],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/node_perfctr_provider.h',
'<(SHARED_INTERMEDIATE_DIR)/node_perfctr_provider.rc',
],
'action': [ 'ctrpp <@(_inputs) '
'-o <(SHARED_INTERMEDIATE_DIR)/node_perfctr_provider.h '
'-rc <(SHARED_INTERMEDIATE_DIR)/node_perfctr_provider.rc'
]
},
],
} ]
]
},
{
'target_name': 'node_js2c',
'type': 'none',
'toolsets': ['host'],
'actions': [
{
'action_name': 'node_js2c',

'inputs': [
'<@(library_files)',
'./config.gypi',
],

'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/node_natives.h',
],

# FIXME can the following conditions be shorted by just setting
# macros.py into some variable which then gets included in the
# action?

'conditions': [
[ 'node_use_dtrace=="true"'
' or node_use_etw=="true"'
' or node_use_systemtap=="true"',
[ 'node_use_dtrace=="false"'
' and node_use_etw=="false"'
' and node_use_systemtap=="false"',
{
'action': [
'<(python)',
'tools/js2c.py',
'<@(_outputs)',
'<@(_inputs)',
'inputs': ['src/macros.py']
}
],
}, { # No Dtrace
[ 'node_use_perfctr=="false"', {
'inputs': [ 'src/perfctr_macros.py' ]
}]
],
'action': [
'<(python)',
'tools/js2c.py',
'<@(_outputs)',
'<@(_inputs)',
'src/macros.py'
],
}]
],
},
],
}, # end node_js2c
Expand Down Expand Up @@ -428,4 +453,3 @@
}
] # end targets
}

7 changes: 7 additions & 0 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
#if defined HAVE_DTRACE || defined HAVE_ETW || defined HAVE_SYSTEMTAP
# include "node_dtrace.h"
#endif
#if defined HAVE_PERFCTR
# include "node_counters.h"
#endif

#include <locale.h>
#include <signal.h>
Expand Down Expand Up @@ -2314,6 +2317,10 @@ void Load(Handle<Object> process_l) {
InitDTrace(global);
#endif

#if defined HAVE_PERFCTR
InitPerfCounters(global);
#endif

f->Call(global, 1, args);

if (try_catch.HasCaught()) {
Expand Down
149 changes: 149 additions & 0 deletions src/node_counters.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// 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.

#include "node_counters.h"

#include "uv.h"

#include <string.h>


namespace node {

using namespace v8;


static uint64_t counter_gc_start_time;
static uint64_t counter_gc_end_time;

#define SLURP_OBJECT(obj, member, valp) \
if (!(obj)->IsObject()) { \
return (ThrowException(Exception::Error(String::New("expected " \
"object for " #obj " to contain object member " #member)))); \
} \
*valp = Local<Object>::Cast(obj->Get(String::New(#member)));


Handle<Value> COUNTER_NET_SERVER_CONNECTION(const Arguments& args) {
NODE_COUNT_SERVER_CONN_OPEN();
return Undefined();
}


Handle<Value> COUNTER_NET_SERVER_CONNECTION_CLOSE(const Arguments& args) {
NODE_COUNT_SERVER_CONN_CLOSE();
return Undefined();
}


Handle<Value> COUNTER_HTTP_SERVER_REQUEST(const Arguments& args) {
NODE_COUNT_HTTP_SERVER_REQUEST();
return Undefined();
}


Handle<Value> COUNTER_HTTP_SERVER_RESPONSE(const Arguments& args) {
NODE_COUNT_HTTP_SERVER_RESPONSE();
return Undefined();
}


Handle<Value> COUNTER_HTTP_CLIENT_REQUEST(const Arguments& args) {
NODE_COUNT_HTTP_CLIENT_REQUEST();
return Undefined();
}


Handle<Value> COUNTER_HTTP_CLIENT_RESPONSE(const Arguments& args) {
NODE_COUNT_HTTP_CLIENT_RESPONSE();
return Undefined();
}


static void counter_gc_start(GCType type, GCCallbackFlags flags) {
counter_gc_start_time = NODE_COUNT_GET_GC_RAWTIME();

return;
}


static void counter_gc_done(GCType type, GCCallbackFlags flags) {
uint64_t endgc = NODE_COUNT_GET_GC_RAWTIME();
if (endgc != 0) {
uint64_t totalperiod = endgc - counter_gc_end_time;
uint64_t gcperiod = endgc - counter_gc_start_time;

if (totalperiod > 0) {
unsigned int percent = static_cast<unsigned int>((gcperiod * 100) / totalperiod);

NODE_COUNT_GC_PERCENTTIME(percent);
counter_gc_end_time = endgc;
}
}

return;
}


#define NODE_PROBE(name) #name, name

void InitPerfCounters(Handle<Object> target) {
HandleScope scope;

static struct {
const char* name;
Handle<Value> (*func)(const Arguments&);
Persistent<FunctionTemplate> templ;
} tab[] = {
{ NODE_PROBE(COUNTER_NET_SERVER_CONNECTION) },
{ NODE_PROBE(COUNTER_NET_SERVER_CONNECTION_CLOSE) },
{ NODE_PROBE(COUNTER_HTTP_SERVER_REQUEST) },
{ NODE_PROBE(COUNTER_HTTP_SERVER_RESPONSE) },
{ NODE_PROBE(COUNTER_HTTP_CLIENT_REQUEST) },
{ NODE_PROBE(COUNTER_HTTP_CLIENT_RESPONSE) }
};

for (int i = 0; i < ARRAY_SIZE(tab); i++) {
tab[i].templ = Persistent<FunctionTemplate>::New(
FunctionTemplate::New(tab[i].func));
target->Set(String::NewSymbol(tab[i].name), tab[i].templ->GetFunction());
}

// Only Windows performance counters supported
// To enable other OS, use conditional compilation here
InitPerfCountersWin32();

// init times for GC percent calculation and hook callbacks
counter_gc_start_time = NODE_COUNT_GET_GC_RAWTIME();
counter_gc_end_time = counter_gc_start_time;

v8::V8::AddGCPrologueCallback(counter_gc_start);
v8::V8::AddGCEpilogueCallback(counter_gc_done);
}


void TermPerfCounters(Handle<Object> target) {
// Only Windows performance counters supported
// To enable other OS, use conditional compilation here
TermPerfCountersWin32();
}

}
Loading

0 comments on commit f657ce6

Please sign in to comment.