Skip to content

Commit f657ce6

Browse files
sblompiscisaureus
authored andcommitted
windows: add tracing with performance counters
Patch by Henry Rawas and Scott Blomquist.
1 parent bc93883 commit f657ce6

17 files changed

+827
-30
lines changed

configure

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,16 @@ parser.add_option("--without-etw",
211211
dest="without_etw",
212212
help="Build without ETW")
213213

214+
parser.add_option("--with-perfctr",
215+
action="store_true",
216+
dest="with_perfctr",
217+
help="Build with performance counters (default is true on Windows)")
218+
219+
parser.add_option("--without-perfctr",
220+
action="store_true",
221+
dest="without_perfctr",
222+
help="Build without performance counters")
223+
214224
# CHECKME does this still work with recent releases of V8?
215225
parser.add_option("--gdb",
216226
action="store_true",
@@ -454,7 +464,7 @@ def configure_node(o):
454464
o['variables']['node_use_systemtap'] = b(options.with_dtrace)
455465
if options.systemtap_includes:
456466
o['include_dirs'] += [options.systemtap_includes]
457-
elif b(options.with_dtrace) == 'true':
467+
elif options.with_dtrace:
458468
raise Exception(
459469
'DTrace is currently only supported on SunOS or Linux systems.')
460470
else:
@@ -467,11 +477,19 @@ def configure_node(o):
467477
# By default, enable ETW on Windows.
468478
if sys.platform.startswith('win32'):
469479
o['variables']['node_use_etw'] = b(not options.without_etw);
470-
elif b(options.with_etw) == 'true':
480+
elif options.with_etw:
471481
raise Exception('ETW is only supported on Windows.')
472482
else:
473483
o['variables']['node_use_etw'] = 'false'
474484

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

476494
def configure_libz(o):
477495
o['variables']['node_shared_zlib'] = b(options.shared_zlib)

lib/http.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,9 +841,11 @@ OutgoingMessage.prototype._finish = function() {
841841
assert(this.connection);
842842
if (this instanceof ServerResponse) {
843843
DTRACE_HTTP_SERVER_RESPONSE(this.connection);
844+
COUNTER_HTTP_SERVER_RESPONSE();
844845
} else {
845846
assert(this instanceof ClientRequest);
846847
DTRACE_HTTP_CLIENT_REQUEST(this, this.connection);
848+
COUNTER_HTTP_CLIENT_REQUEST();
847849
}
848850
this.emit('finish');
849851
};
@@ -1472,6 +1474,7 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
14721474

14731475

14741476
DTRACE_HTTP_CLIENT_RESPONSE(socket, req);
1477+
COUNTER_HTTP_CLIENT_RESPONSE();
14751478
req.emit('response', res);
14761479
req.res = res;
14771480
res.req = req;
@@ -1779,6 +1782,7 @@ function connectionListener(socket) {
17791782
debug('server response shouldKeepAlive: ' + shouldKeepAlive);
17801783
res.shouldKeepAlive = shouldKeepAlive;
17811784
DTRACE_HTTP_SERVER_REQUEST(req, socket);
1785+
COUNTER_HTTP_SERVER_REQUEST();
17821786

17831787
if (socket._httpMessage) {
17841788
// There are already pending outgoing res, append.

lib/net.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ Socket.prototype._destroy = function(exception, cb) {
361361
this.destroyed = true;
362362

363363
if (this.server) {
364+
COUNTER_NET_SERVER_CONNECTION_CLOSE(this);
364365
this.server._connections--;
365366
if (this.server._emitCloseIfDrained) {
366367
this.server._emitCloseIfDrained();
@@ -1054,6 +1055,7 @@ function onconnection(clientHandle) {
10541055
socket.server = self;
10551056

10561057
DTRACE_NET_SERVER_CONNECTION(socket);
1058+
COUNTER_NET_SERVER_CONNECTION(socket);
10571059
self.emit('connection', socket);
10581060
socket.emit('connect');
10591061
}

node.gyp

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
'werror': '',
77
'node_use_dtrace%': 'false',
88
'node_use_etw%': 'false',
9+
'node_use_perfctr%': 'false',
910
'node_shared_v8%': 'false',
1011
'node_shared_zlib%': 'false',
1112
'node_shared_http_parser%': 'false',
@@ -189,6 +190,17 @@
189190
'<(SHARED_INTERMEDIATE_DIR)/node_etw_provider.rc',
190191
]
191192
} ],
193+
[ 'node_use_perfctr=="true"', {
194+
'defines': [ 'HAVE_PERFCTR=1' ],
195+
'dependencies': [ 'node_perfctr' ],
196+
'sources': [
197+
'src/node_win32_perfctr_provider.h',
198+
'src/node_win32_perfctr_provider.cc',
199+
'src/node_counters.cc',
200+
'src/node_counters.h',
201+
'<(SHARED_INTERMEDIATE_DIR)/node_perfctr_provider.rc',
202+
]
203+
} ],
192204
[ 'node_shared_v8=="false"', {
193205
'sources': [
194206
'deps/v8/include/v8.h',
@@ -287,48 +299,61 @@
287299
} ]
288300
]
289301
},
302+
# generate perf counter header and resource files
303+
{
304+
'target_name': 'node_perfctr',
305+
'type': 'none',
306+
'conditions': [
307+
[ 'node_use_perfctr=="true"', {
308+
'actions': [
309+
{
310+
'action_name': 'node_perfctr_man',
311+
'inputs': [ 'src/res/node_perfctr_provider.man' ],
312+
'outputs': [
313+
'<(SHARED_INTERMEDIATE_DIR)/node_perfctr_provider.h',
314+
'<(SHARED_INTERMEDIATE_DIR)/node_perfctr_provider.rc',
315+
],
316+
'action': [ 'ctrpp <@(_inputs) '
317+
'-o <(SHARED_INTERMEDIATE_DIR)/node_perfctr_provider.h '
318+
'-rc <(SHARED_INTERMEDIATE_DIR)/node_perfctr_provider.rc'
319+
]
320+
},
321+
],
322+
} ]
323+
]
324+
},
290325
{
291326
'target_name': 'node_js2c',
292327
'type': 'none',
293328
'toolsets': ['host'],
294329
'actions': [
295330
{
296331
'action_name': 'node_js2c',
297-
298332
'inputs': [
299333
'<@(library_files)',
300334
'./config.gypi',
301335
],
302-
303336
'outputs': [
304337
'<(SHARED_INTERMEDIATE_DIR)/node_natives.h',
305338
],
306-
307-
# FIXME can the following conditions be shorted by just setting
308-
# macros.py into some variable which then gets included in the
309-
# action?
310-
311339
'conditions': [
312-
[ 'node_use_dtrace=="true"'
313-
' or node_use_etw=="true"'
314-
' or node_use_systemtap=="true"',
340+
[ 'node_use_dtrace=="false"'
341+
' and node_use_etw=="false"'
342+
' and node_use_systemtap=="false"',
315343
{
316-
'action': [
317-
'<(python)',
318-
'tools/js2c.py',
319-
'<@(_outputs)',
320-
'<@(_inputs)',
344+
'inputs': ['src/macros.py']
345+
}
321346
],
322-
}, { # No Dtrace
347+
[ 'node_use_perfctr=="false"', {
348+
'inputs': [ 'src/perfctr_macros.py' ]
349+
}]
350+
],
323351
'action': [
324352
'<(python)',
325353
'tools/js2c.py',
326354
'<@(_outputs)',
327355
'<@(_inputs)',
328-
'src/macros.py'
329356
],
330-
}]
331-
],
332357
},
333358
],
334359
}, # end node_js2c
@@ -428,4 +453,3 @@
428453
}
429454
] # end targets
430455
}
431-

src/node.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
#if defined HAVE_DTRACE || defined HAVE_ETW || defined HAVE_SYSTEMTAP
3131
# include "node_dtrace.h"
3232
#endif
33+
#if defined HAVE_PERFCTR
34+
# include "node_counters.h"
35+
#endif
3336

3437
#include <locale.h>
3538
#include <signal.h>
@@ -2314,6 +2317,10 @@ void Load(Handle<Object> process_l) {
23142317
InitDTrace(global);
23152318
#endif
23162319

2320+
#if defined HAVE_PERFCTR
2321+
InitPerfCounters(global);
2322+
#endif
2323+
23172324
f->Call(global, 1, args);
23182325

23192326
if (try_catch.HasCaught()) {

src/node_counters.cc

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
#include "node_counters.h"
23+
24+
#include "uv.h"
25+
26+
#include <string.h>
27+
28+
29+
namespace node {
30+
31+
using namespace v8;
32+
33+
34+
static uint64_t counter_gc_start_time;
35+
static uint64_t counter_gc_end_time;
36+
37+
#define SLURP_OBJECT(obj, member, valp) \
38+
if (!(obj)->IsObject()) { \
39+
return (ThrowException(Exception::Error(String::New("expected " \
40+
"object for " #obj " to contain object member " #member)))); \
41+
} \
42+
*valp = Local<Object>::Cast(obj->Get(String::New(#member)));
43+
44+
45+
Handle<Value> COUNTER_NET_SERVER_CONNECTION(const Arguments& args) {
46+
NODE_COUNT_SERVER_CONN_OPEN();
47+
return Undefined();
48+
}
49+
50+
51+
Handle<Value> COUNTER_NET_SERVER_CONNECTION_CLOSE(const Arguments& args) {
52+
NODE_COUNT_SERVER_CONN_CLOSE();
53+
return Undefined();
54+
}
55+
56+
57+
Handle<Value> COUNTER_HTTP_SERVER_REQUEST(const Arguments& args) {
58+
NODE_COUNT_HTTP_SERVER_REQUEST();
59+
return Undefined();
60+
}
61+
62+
63+
Handle<Value> COUNTER_HTTP_SERVER_RESPONSE(const Arguments& args) {
64+
NODE_COUNT_HTTP_SERVER_RESPONSE();
65+
return Undefined();
66+
}
67+
68+
69+
Handle<Value> COUNTER_HTTP_CLIENT_REQUEST(const Arguments& args) {
70+
NODE_COUNT_HTTP_CLIENT_REQUEST();
71+
return Undefined();
72+
}
73+
74+
75+
Handle<Value> COUNTER_HTTP_CLIENT_RESPONSE(const Arguments& args) {
76+
NODE_COUNT_HTTP_CLIENT_RESPONSE();
77+
return Undefined();
78+
}
79+
80+
81+
static void counter_gc_start(GCType type, GCCallbackFlags flags) {
82+
counter_gc_start_time = NODE_COUNT_GET_GC_RAWTIME();
83+
84+
return;
85+
}
86+
87+
88+
static void counter_gc_done(GCType type, GCCallbackFlags flags) {
89+
uint64_t endgc = NODE_COUNT_GET_GC_RAWTIME();
90+
if (endgc != 0) {
91+
uint64_t totalperiod = endgc - counter_gc_end_time;
92+
uint64_t gcperiod = endgc - counter_gc_start_time;
93+
94+
if (totalperiod > 0) {
95+
unsigned int percent = static_cast<unsigned int>((gcperiod * 100) / totalperiod);
96+
97+
NODE_COUNT_GC_PERCENTTIME(percent);
98+
counter_gc_end_time = endgc;
99+
}
100+
}
101+
102+
return;
103+
}
104+
105+
106+
#define NODE_PROBE(name) #name, name
107+
108+
void InitPerfCounters(Handle<Object> target) {
109+
HandleScope scope;
110+
111+
static struct {
112+
const char* name;
113+
Handle<Value> (*func)(const Arguments&);
114+
Persistent<FunctionTemplate> templ;
115+
} tab[] = {
116+
{ NODE_PROBE(COUNTER_NET_SERVER_CONNECTION) },
117+
{ NODE_PROBE(COUNTER_NET_SERVER_CONNECTION_CLOSE) },
118+
{ NODE_PROBE(COUNTER_HTTP_SERVER_REQUEST) },
119+
{ NODE_PROBE(COUNTER_HTTP_SERVER_RESPONSE) },
120+
{ NODE_PROBE(COUNTER_HTTP_CLIENT_REQUEST) },
121+
{ NODE_PROBE(COUNTER_HTTP_CLIENT_RESPONSE) }
122+
};
123+
124+
for (int i = 0; i < ARRAY_SIZE(tab); i++) {
125+
tab[i].templ = Persistent<FunctionTemplate>::New(
126+
FunctionTemplate::New(tab[i].func));
127+
target->Set(String::NewSymbol(tab[i].name), tab[i].templ->GetFunction());
128+
}
129+
130+
// Only Windows performance counters supported
131+
// To enable other OS, use conditional compilation here
132+
InitPerfCountersWin32();
133+
134+
// init times for GC percent calculation and hook callbacks
135+
counter_gc_start_time = NODE_COUNT_GET_GC_RAWTIME();
136+
counter_gc_end_time = counter_gc_start_time;
137+
138+
v8::V8::AddGCPrologueCallback(counter_gc_start);
139+
v8::V8::AddGCEpilogueCallback(counter_gc_done);
140+
}
141+
142+
143+
void TermPerfCounters(Handle<Object> target) {
144+
// Only Windows performance counters supported
145+
// To enable other OS, use conditional compilation here
146+
TermPerfCountersWin32();
147+
}
148+
149+
}

0 commit comments

Comments
 (0)