From f657ce685dddc1bf2d5b42f56d728df9176cceb9 Mon Sep 17 00:00:00 2001 From: Scott Blomquist Date: Wed, 21 Nov 2012 00:27:22 +0100 Subject: [PATCH] windows: add tracing with performance counters Patch by Henry Rawas and Scott Blomquist. --- configure | 22 +- lib/http.js | 4 + lib/net.js | 2 + node.gyp | 72 ++++-- src/node.cc | 7 + src/node_counters.cc | 149 +++++++++++++ src/node_counters.h | 53 +++++ src/node_win32_perfctr_provider.cc | 338 +++++++++++++++++++++++++++++ src/node_win32_perfctr_provider.h | 55 +++++ src/perfctr_macros.py | 9 + src/res/node_perfctr_provider.man | 97 +++++++++ src/stream_wrap.cc | 19 ++ test/common.js | 8 + tools/js2c.py | 8 +- tools/msvs/msi/nodemsi.wixproj | 8 +- tools/msvs/msi/product.wxs | 5 + vcbuild.bat | 9 +- 17 files changed, 831 insertions(+), 34 deletions(-) create mode 100644 src/node_counters.cc create mode 100644 src/node_counters.h create mode 100644 src/node_win32_perfctr_provider.cc create mode 100644 src/node_win32_perfctr_provider.h create mode 100644 src/perfctr_macros.py create mode 100644 src/res/node_perfctr_provider.man diff --git a/configure b/configure index 2778da276df..112d7cda34e 100755 --- a/configure +++ b/configure @@ -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", @@ -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: @@ -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) diff --git a/lib/http.js b/lib/http.js index 3ebca09439d..ef85c1cddf4 100644 --- a/lib/http.js +++ b/lib/http.js @@ -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'); }; @@ -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; @@ -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. diff --git a/lib/net.js b/lib/net.js index aeb37b2c34f..6e3f896a8e1 100644 --- a/lib/net.js +++ b/lib/net.js @@ -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(); @@ -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'); } diff --git a/node.gyp b/node.gyp index b9f72810833..f6651db83d7 100644 --- a/node.gyp +++ b/node.gyp @@ -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', @@ -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', @@ -287,6 +299,29 @@ } ] ] }, + # 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', @@ -294,41 +329,31 @@ '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)', - ], - }, { # No Dtrace - 'action': [ - '<(python)', - 'tools/js2c.py', - '<@(_outputs)', - '<@(_inputs)', - 'src/macros.py' + 'inputs': ['src/macros.py'] + } ], + [ 'node_use_perfctr=="false"', { + 'inputs': [ 'src/perfctr_macros.py' ] }] ], + 'action': [ + '<(python)', + 'tools/js2c.py', + '<@(_outputs)', + '<@(_inputs)', + ], }, ], }, # end node_js2c @@ -428,4 +453,3 @@ } ] # end targets } - diff --git a/src/node.cc b/src/node.cc index 60cc19c4eb9..c56e1494a8d 100644 --- a/src/node.cc +++ b/src/node.cc @@ -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 #include @@ -2314,6 +2317,10 @@ void Load(Handle process_l) { InitDTrace(global); #endif +#if defined HAVE_PERFCTR + InitPerfCounters(global); +#endif + f->Call(global, 1, args); if (try_catch.HasCaught()) { diff --git a/src/node_counters.cc b/src/node_counters.cc new file mode 100644 index 00000000000..3c8d49c00b4 --- /dev/null +++ b/src/node_counters.cc @@ -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 + + +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::Cast(obj->Get(String::New(#member))); + + +Handle COUNTER_NET_SERVER_CONNECTION(const Arguments& args) { + NODE_COUNT_SERVER_CONN_OPEN(); + return Undefined(); +} + + +Handle COUNTER_NET_SERVER_CONNECTION_CLOSE(const Arguments& args) { + NODE_COUNT_SERVER_CONN_CLOSE(); + return Undefined(); +} + + +Handle COUNTER_HTTP_SERVER_REQUEST(const Arguments& args) { + NODE_COUNT_HTTP_SERVER_REQUEST(); + return Undefined(); +} + + +Handle COUNTER_HTTP_SERVER_RESPONSE(const Arguments& args) { + NODE_COUNT_HTTP_SERVER_RESPONSE(); + return Undefined(); +} + + +Handle COUNTER_HTTP_CLIENT_REQUEST(const Arguments& args) { + NODE_COUNT_HTTP_CLIENT_REQUEST(); + return Undefined(); +} + + +Handle 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((gcperiod * 100) / totalperiod); + + NODE_COUNT_GC_PERCENTTIME(percent); + counter_gc_end_time = endgc; + } + } + + return; +} + + +#define NODE_PROBE(name) #name, name + +void InitPerfCounters(Handle target) { + HandleScope scope; + + static struct { + const char* name; + Handle (*func)(const Arguments&); + Persistent 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::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 target) { + // Only Windows performance counters supported + // To enable other OS, use conditional compilation here + TermPerfCountersWin32(); +} + +} diff --git a/src/node_counters.h b/src/node_counters.h new file mode 100644 index 00000000000..c55d99b9091 --- /dev/null +++ b/src/node_counters.h @@ -0,0 +1,53 @@ +// 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. + +#ifndef NODE_COUNTERS_H_ +#define NODE_COUNTERS_H_ + +#include "node.h" +#include "v8.h" + +namespace node { + +void InitPerfCounters(v8::Handle target); +void TermPerfCounters(v8::Handle target); + +} + +#ifdef HAVE_PERFCTR +#include "node_win32_perfctr_provider.h" +#else +#define NODE_COUNTER_ENABLED() (false) +#define NODE_COUNT_HTTP_SERVER_REQUEST() +#define NODE_COUNT_HTTP_SERVER_RESPONSE() +#define NODE_COUNT_HTTP_CLIENT_REQUEST() +#define NODE_COUNT_HTTP_CLIENT_RESPONSE() +#define NODE_COUNT_SERVER_CONN_OPEN() +#define NODE_COUNT_SERVER_CONN_CLOSE() +#define NODE_COUNT_NET_BYTES_SENT(bytes) +#define NODE_COUNT_NET_BYTES_RECV(bytes) +#define NODE_COUNT_GET_GC_RAWTIME() +#define NODE_COUNT_GC_PERCENTTIME() +#define NODE_COUNT_PIPE_BYTES_SENT(bytes) +#define NODE_COUNT_PIPE_BYTES_RECV(bytes) +#endif + +#endif diff --git a/src/node_win32_perfctr_provider.cc b/src/node_win32_perfctr_provider.cc new file mode 100644 index 00000000000..8135726852b --- /dev/null +++ b/src/node_win32_perfctr_provider.cc @@ -0,0 +1,338 @@ +// 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 +#include "node_win32_perfctr_provider.h" + +#define __INIT_node_perfctr_provider_IMP +#include + +typedef ULONG (WINAPI *PerfStartProviderExFunc)( + __in LPGUID ProviderGuid, + __in_opt PPERF_PROVIDER_CONTEXT ProviderContext, + __out PHANDLE Provider + ); + +typedef ULONG (WINAPI *PerfStopProviderFunc)( + __in HANDLE ProviderHandle + ); + +typedef ULONG (WINAPI *PerfSetCounterSetInfoFunc)( + __in HANDLE ProviderHandle, + __inout_bcount(TemplateSize) PPERF_COUNTERSET_INFO Template, + __in ULONG TemplateSize + ); + +typedef PPERF_COUNTERSET_INSTANCE (WINAPI *PerfCreateInstanceFunc)( + __in HANDLE ProviderHandle, + __in LPCGUID CounterSetGuid, + __in PCWSTR Name, + __in ULONG Id + ); + +typedef ULONG (WINAPI *PerfDeleteInstanceFunc)( + __in HANDLE Provider, + __in PPERF_COUNTERSET_INSTANCE InstanceBlock + ); + +typedef ULONG (WINAPI *PerfSetULongCounterValueFunc)( + __in HANDLE Provider, + __inout PPERF_COUNTERSET_INSTANCE Instance, + __in ULONG CounterId, + __in ULONG Value + ); + +typedef ULONG (WINAPI *PerfSetULongLongCounterValueFunc)( + __in HANDLE Provider, + __inout PPERF_COUNTERSET_INSTANCE Instance, + __in ULONG CounterId, + __in ULONGLONG Value + ); + +typedef ULONG (WINAPI *PerfIncrementULongCounterValueFunc)( + __in HANDLE Provider, + __inout PPERF_COUNTERSET_INSTANCE Instance, + __in ULONG CounterId, + __in ULONG Value + ); + +typedef ULONG (WINAPI *PerfIncrementULongLongCounterValueFunc)( + __in HANDLE Provider, + __inout PPERF_COUNTERSET_INSTANCE Instance, + __in ULONG CounterId, + __in ULONGLONG Value + ); + +typedef ULONG (WINAPI *PerfDecrementULongCounterValueFunc)( + __in HANDLE Provider, + __inout PPERF_COUNTERSET_INSTANCE Instance, + __in ULONG CounterId, + __in ULONG Value + ); + +typedef ULONG (WINAPI *PerfDecrementULongLongCounterValueFunc)( + __in HANDLE Provider, + __inout PPERF_COUNTERSET_INSTANCE Instance, + __in ULONG CounterId, + __in ULONGLONG Value + ); + + +HMODULE advapimod; +PerfStartProviderExFunc perfctr_startProvider; +PerfStopProviderFunc perfctr_stopProvider; +PerfSetCounterSetInfoFunc perfctr_setCounterSetInfo; +PerfCreateInstanceFunc perfctr_createInstance; +PerfDeleteInstanceFunc perfctr_deleteInstance; +PerfSetULongCounterValueFunc perfctr_setULongValue; +PerfSetULongLongCounterValueFunc perfctr_setULongLongValue; +PerfIncrementULongCounterValueFunc perfctr_incrementULongValue; +PerfIncrementULongLongCounterValueFunc perfctr_incrementULongLongValue; +PerfDecrementULongCounterValueFunc perfctr_decrementULongValue; +PerfDecrementULongLongCounterValueFunc perfctr_decrementULongLongValue; + +PPERF_COUNTERSET_INSTANCE perfctr_instance; + + +#define NODE_COUNTER_HTTP_SERVER_REQUEST 1 +#define NODE_COUNTER_HTTP_SERVER_RESPONSE 2 +#define NODE_COUNTER_HTTP_CLIENT_REQUEST 3 +#define NODE_COUNTER_HTTP_CLIENT_RESPONSE 4 +#define NODE_COUNTER_SERVER_CONNS 5 +#define NODE_COUNTER_NET_BYTES_SENT 6 +#define NODE_COUNTER_NET_BYTES_RECV 7 +#define NODE_COUNTER_GC_PERCENTTIME 8 +#define NODE_COUNTER_PIPE_BYTES_SENT 9 +#define NODE_COUNTER_PIPE_BYTES_RECV 10 + + +namespace node { + + +EXTERN_C DECLSPEC_SELECTANY HANDLE NodeCounterProvider = NULL; + +void InitPerfCountersWin32() { + ULONG status; + PERF_PROVIDER_CONTEXT providerContext; + + // create instance name using pid +#define INST_MAX_LEN 32 +#define INST_PREFIX_LEN 5 +#define INST_PREFIX L"node_" + + wchar_t Inst[INST_MAX_LEN]; + DWORD pid = GetCurrentProcessId(); + wcscpy_s(Inst, INST_MAX_LEN, INST_PREFIX); + _itow_s(pid, Inst + INST_PREFIX_LEN, INST_MAX_LEN - INST_PREFIX_LEN, 10); + + advapimod = LoadLibrary("advapi32.dll"); + if (advapimod) { + perfctr_startProvider = (PerfStartProviderExFunc) + GetProcAddress(advapimod, "PerfStartProviderEx"); + perfctr_stopProvider = (PerfStopProviderFunc) + GetProcAddress(advapimod, "PerfStopProvider"); + perfctr_setCounterSetInfo = (PerfSetCounterSetInfoFunc) + GetProcAddress(advapimod, "PerfSetCounterSetInfo"); + perfctr_createInstance = (PerfCreateInstanceFunc) + GetProcAddress(advapimod, "PerfCreateInstance"); + perfctr_deleteInstance = (PerfDeleteInstanceFunc) + GetProcAddress(advapimod, "PerfDeleteInstance"); + perfctr_setULongValue = (PerfSetULongCounterValueFunc) + GetProcAddress(advapimod, "PerfSetULongCounterValue"); + perfctr_setULongLongValue = (PerfSetULongLongCounterValueFunc) + GetProcAddress(advapimod, "PerfSetULongLongCounterValue"); + perfctr_incrementULongValue = (PerfIncrementULongCounterValueFunc) + GetProcAddress(advapimod, "PerfIncrementULongCounterValue"); + perfctr_incrementULongLongValue = (PerfIncrementULongLongCounterValueFunc) + GetProcAddress(advapimod, "PerfIncrementULongLongCounterValue"); + perfctr_decrementULongValue = (PerfDecrementULongCounterValueFunc) + GetProcAddress(advapimod, "PerfDecrementULongCounterValue"); + perfctr_decrementULongLongValue = (PerfDecrementULongLongCounterValueFunc) + GetProcAddress(advapimod, "PerfDecrementULongLongCounterValue"); + + ZeroMemory(&providerContext, sizeof(providerContext)); + providerContext.ContextSize = sizeof(providerContext); + + status = perfctr_startProvider(&NodeCounterSetGuid, + &providerContext, + &NodeCounterProvider); + if (status != ERROR_SUCCESS) { + NodeCounterProvider = NULL; + return; + } + + status = perfctr_setCounterSetInfo(NodeCounterProvider, + &NodeCounterSetInfo.CounterSet, + sizeof(NodeCounterSetInfo)); + if (status != ERROR_SUCCESS) { + perfctr_stopProvider(NodeCounterProvider); + NodeCounterProvider = NULL; + return; + } + + perfctr_instance = perfctr_createInstance(NodeCounterProvider, + &NodeCounterSetGuid, + Inst, + 1); + if (perfctr_instance == NULL) { + perfctr_stopProvider(NodeCounterProvider); + NodeCounterProvider = NULL; + } + } +} + + +void TermPerfCountersWin32() { + if (NodeCounterProvider != NULL && + perfctr_stopProvider != NULL) { + perfctr_stopProvider(NodeCounterProvider); + NodeCounterProvider = NULL; + } + + if (advapimod) { + FreeLibrary(advapimod); + advapimod = NULL; + } +} + + +void NODE_COUNT_HTTP_SERVER_REQUEST() { + if (NodeCounterProvider != NULL && perfctr_incrementULongValue != NULL) { + perfctr_incrementULongValue(NodeCounterProvider, + perfctr_instance, + NODE_COUNTER_HTTP_SERVER_REQUEST, + 1); + } +} + + +void NODE_COUNT_HTTP_SERVER_RESPONSE() { + if (NodeCounterProvider != NULL && perfctr_incrementULongValue != NULL) { + perfctr_incrementULongValue(NodeCounterProvider, + perfctr_instance, + NODE_COUNTER_HTTP_SERVER_RESPONSE, + 1); + } +} + + +void NODE_COUNT_HTTP_CLIENT_REQUEST() { + if (NodeCounterProvider != NULL && perfctr_incrementULongValue != NULL) { + perfctr_incrementULongValue(NodeCounterProvider, + perfctr_instance, + NODE_COUNTER_HTTP_CLIENT_REQUEST, + 1); + } +} + + +void NODE_COUNT_HTTP_CLIENT_RESPONSE() { + if (NodeCounterProvider != NULL && perfctr_incrementULongValue != NULL) { + perfctr_incrementULongValue(NodeCounterProvider, + perfctr_instance, + NODE_COUNTER_HTTP_CLIENT_RESPONSE, + 1); + } +} + + +void NODE_COUNT_SERVER_CONN_OPEN() { + if (NodeCounterProvider != NULL && perfctr_incrementULongValue != NULL) { + perfctr_incrementULongValue(NodeCounterProvider, + perfctr_instance, + NODE_COUNTER_SERVER_CONNS, + 1); + } +} + + +void NODE_COUNT_SERVER_CONN_CLOSE() { + if (NodeCounterProvider != NULL && perfctr_decrementULongValue != NULL) { + perfctr_decrementULongValue(NodeCounterProvider, + perfctr_instance, + NODE_COUNTER_SERVER_CONNS, + 1); + } +} + + +void NODE_COUNT_NET_BYTES_SENT(int bytes) { + if (NodeCounterProvider != NULL && perfctr_incrementULongLongValue != NULL) { + perfctr_incrementULongLongValue(NodeCounterProvider, + perfctr_instance, + NODE_COUNTER_NET_BYTES_SENT, + static_cast(bytes)); + } +} + + +void NODE_COUNT_NET_BYTES_RECV(int bytes) { + if (NodeCounterProvider != NULL && perfctr_incrementULongLongValue != NULL) { + perfctr_incrementULongLongValue(NodeCounterProvider, + perfctr_instance, + NODE_COUNTER_NET_BYTES_RECV, + static_cast(bytes)); + } +} + + +uint64_t NODE_COUNT_GET_GC_RAWTIME() { + LARGE_INTEGER timegc; + if (QueryPerformanceCounter(&timegc)) { + return timegc.QuadPart; + } else { + return static_cast(GetTickCount()); + } +} + + +void NODE_COUNT_GC_PERCENTTIME(unsigned int percent) { + if (NodeCounterProvider != NULL && perfctr_setULongValue != NULL) { + perfctr_setULongValue(NodeCounterProvider, + perfctr_instance, + NODE_COUNTER_GC_PERCENTTIME, + percent); + } +} + + +void NODE_COUNT_PIPE_BYTES_SENT(int bytes) { + if (NodeCounterProvider != NULL && perfctr_incrementULongLongValue != NULL) { + perfctr_incrementULongLongValue(NodeCounterProvider, + perfctr_instance, + NODE_COUNTER_PIPE_BYTES_SENT, + static_cast(bytes)); + } +} + + +void NODE_COUNT_PIPE_BYTES_RECV(int bytes) { + if (NodeCounterProvider != NULL && perfctr_incrementULongLongValue != NULL) { + perfctr_incrementULongLongValue(NodeCounterProvider, + perfctr_instance, + NODE_COUNTER_PIPE_BYTES_RECV, + static_cast(bytes)); + } +} + + +} diff --git a/src/node_win32_perfctr_provider.h b/src/node_win32_perfctr_provider.h new file mode 100644 index 00000000000..1b16a3fec85 --- /dev/null +++ b/src/node_win32_perfctr_provider.h @@ -0,0 +1,55 @@ +// 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. + +#ifndef SRC_WINPERFCTRS_H_ +#define SRC_WINPERFCTRS_H_ + +#if defined(_MSC_VER) +# define INLINE __forceinline +#else +# define INLINE inline +#endif + +namespace node { + +extern HANDLE NodeCounterProvider; + +INLINE bool NODE_COUNTER_ENABLED() { return NodeCounterProvider != NULL; } +void NODE_COUNT_HTTP_SERVER_REQUEST(); +void NODE_COUNT_HTTP_SERVER_RESPONSE(); +void NODE_COUNT_HTTP_CLIENT_REQUEST(); +void NODE_COUNT_HTTP_CLIENT_RESPONSE(); +void NODE_COUNT_SERVER_CONN_OPEN(); +void NODE_COUNT_SERVER_CONN_CLOSE(); +void NODE_COUNT_NET_BYTES_SENT(int bytes); +void NODE_COUNT_NET_BYTES_RECV(int bytes); +uint64_t NODE_COUNT_GET_GC_RAWTIME(); +void NODE_COUNT_GC_PERCENTTIME(unsigned int percent); +void NODE_COUNT_PIPE_BYTES_SENT(int bytes); +void NODE_COUNT_PIPE_BYTES_RECV(int bytes); + +void InitPerfCountersWin32(); +void TermPerfCountersWin32(); + +} + +#endif + diff --git a/src/perfctr_macros.py b/src/perfctr_macros.py new file mode 100644 index 00000000000..2f7e3cbf829 --- /dev/null +++ b/src/perfctr_macros.py @@ -0,0 +1,9 @@ +# This file is used by tools/js2c.py to preprocess out the performance counters +# symbols in builds that don't support counters. This is not used in builds +# that support performance counters. +macro COUNTER_NET_SERVER_CONNECTION(x) = ; +macro COUNTER_NET_SERVER_CONNECTION_CLOSE(x) = ; +macro COUNTER_HTTP_SERVER_REQUEST(x) = ; +macro COUNTER_HTTP_SERVER_RESPONSE(x) = ; +macro COUNTER_HTTP_CLIENT_REQUEST(x) = ; +macro COUNTER_HTTP_CLIENT_RESPONSE(x) = ; diff --git a/src/res/node_perfctr_provider.man b/src/res/node_perfctr_provider.man new file mode 100644 index 00000000000..cd945ee5fba --- /dev/null +++ b/src/res/node_perfctr_provider.man @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index e79d212c43a..825b65aa995 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -27,6 +27,7 @@ #include "pipe_wrap.h" #include "tcp_wrap.h" #include "req_wrap.h" +#include "node_counters.h" #include // abort() #include // INT_MAX @@ -226,6 +227,12 @@ void StreamWrap::OnReadCommon(uv_stream_t* handle, ssize_t nread, argc++; } + if (wrap->stream_->type == UV_TCP) { + NODE_COUNT_NET_BYTES_RECV(nread); + } else if (wrap->stream_->type == UV_NAMED_PIPE) { + NODE_COUNT_PIPE_BYTES_RECV(nread); + } + MakeCallback(wrap->object_, onread_sym, argc, argv); } @@ -285,6 +292,12 @@ Handle StreamWrap::WriteBuffer(const Arguments& args) { delete[] storage; return scope.Close(v8::Null()); } else { + if (wrap->stream_->type == UV_TCP) { + NODE_COUNT_NET_BYTES_SENT(length); + } else if (wrap->stream_->type == UV_NAMED_PIPE) { + NODE_COUNT_PIPE_BYTES_SENT(length); + } + return scope.Close(req_wrap->object_); } } @@ -418,6 +431,12 @@ Handle StreamWrap::WriteStringImpl(const Arguments& args) { delete[] storage; return scope.Close(v8::Null()); } else { + if (wrap->stream_->type == UV_TCP) { + NODE_COUNT_NET_BYTES_SENT(buf.len); + } else if (wrap->stream_->type == UV_NAMED_PIPE) { + NODE_COUNT_PIPE_BYTES_SENT(buf.len); + } + return scope.Close(req_wrap->object_); } } diff --git a/test/common.js b/test/common.js index 783466a8f63..948d099f5ea 100644 --- a/test/common.js +++ b/test/common.js @@ -108,6 +108,14 @@ process.on('exit', function() { knownGlobals.push(DTRACE_NET_SOCKET_READ); knownGlobals.push(DTRACE_NET_SOCKET_WRITE); } + if (global.COUNTER_NET_SERVER_CONNECTION) { + knownGlobals.push(COUNTER_NET_SERVER_CONNECTION); + knownGlobals.push(COUNTER_NET_SERVER_CONNECTION_CLOSE); + knownGlobals.push(COUNTER_HTTP_SERVER_REQUEST); + knownGlobals.push(COUNTER_HTTP_SERVER_RESPONSE); + knownGlobals.push(COUNTER_HTTP_CLIENT_REQUEST); + knownGlobals.push(COUNTER_HTTP_CLIENT_RESPONSE); + } if (global.ArrayBuffer) { knownGlobals.push(ArrayBuffer); diff --git a/tools/js2c.py b/tools/js2c.py index 3406373b71a..7c83c67ba66 100755 --- a/tools/js2c.py +++ b/tools/js2c.py @@ -266,13 +266,17 @@ def JS2C(source, target): # Locate the macros file name. consts = {} macros = {} + macro_lines = [] for s in source: - if 'macros.py' == (os.path.split(str(s))[1]): - (consts, macros) = ReadMacros(ReadLines(str(s))) + if (os.path.split(str(s))[1]).endswith('macros.py'): + macro_lines.extend(ReadLines(str(s))) else: modules.append(s) + # Process input from all *macro.py files + (consts, macros) = ReadMacros(macro_lines) + # Build source code lines source_lines = [ ] source_lines_empty = [] diff --git a/tools/msvs/msi/nodemsi.wixproj b/tools/msvs/msi/nodemsi.wixproj index 7cc35c07e65..3f9b0dd4e51 100644 --- a/tools/msvs/msi/nodemsi.wixproj +++ b/tools/msvs/msi/nodemsi.wixproj @@ -16,22 +16,22 @@ ..\..\..\$(Configuration)\ obj\$(Configuration)\ - Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFilesFolder + Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NoPerfCtr=$(NoPerfCtr);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFilesFolder ..\..\..\$(Configuration)\ obj\$(Configuration)\ - Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFilesFolder + Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NoPerfCtr=$(NoPerfCtr);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFilesFolder ..\..\..\$(Configuration)\ obj\$(Configuration)\ - Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFiles64Folder + Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NoPerfCtr=$(NoPerfCtr);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFiles64Folder ..\..\..\$(Configuration)\ obj\$(Configuration)\ - Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFiles64Folder + Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NoPerfCtr=$(NoPerfCtr);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFiles64Folder diff --git a/tools/msvs/msi/product.wxs b/tools/msvs/msi/product.wxs index 443739dc24a..46680085513 100755 --- a/tools/msvs/msi/product.wxs +++ b/tools/msvs/msi/product.wxs @@ -48,6 +48,11 @@ + + + + + "%temp%\node_version.txt" if not errorlevel 0 echo Cannot determine current version of node.js & goto exit for /F "tokens=*" %%i in (%temp%\node_version.txt) do set NODE_VERSION=%%i -msbuild "%~dp0tools\msvs\msi\nodemsi.sln" /m /t:Clean,Build /p:Configuration=%config% /p:Platform=%msiplatform% /p:NodeVersion=%NODE_VERSION% %noetw_msi_arg% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo +msbuild "%~dp0tools\msvs\msi\nodemsi.sln" /m /t:Clean,Build /p:Configuration=%config% /p:Platform=%msiplatform% /p:NodeVersion=%NODE_VERSION% %noetw_msi_arg% %noperfctr_msi_arg% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo if errorlevel 1 goto exit if defined nosign goto run