windows: add tracing with performance counters
authorScott Blomquist <sblom@microsoft.com>
Tue, 20 Nov 2012 23:27:22 +0000 (00:27 +0100)
committerBert Belder <bertbelder@gmail.com>
Wed, 21 Nov 2012 00:21:53 +0000 (01:21 +0100)
Patch by Henry Rawas and Scott Blomquist.

17 files changed:
configure
lib/http.js
lib/net.js
node.gyp
src/node.cc
src/node_counters.cc [new file with mode: 0644]
src/node_counters.h [new file with mode: 0644]
src/node_win32_perfctr_provider.cc [new file with mode: 0644]
src/node_win32_perfctr_provider.h [new file with mode: 0644]
src/perfctr_macros.py [new file with mode: 0644]
src/res/node_perfctr_provider.man [new file with mode: 0644]
src/stream_wrap.cc
test/common.js
tools/js2c.py
tools/msvs/msi/nodemsi.wixproj
tools/msvs/msi/product.wxs
vcbuild.bat

index 2778da2..112d7cd 100755 (executable)
--- 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)
index 3ebca09..ef85c1c 100644 (file)
@@ -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.
index aeb37b2..6e3f896 100644 (file)
@@ -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');
 }
index b9f7281..f6651db 100644 (file)
--- 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',
             '<(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',
         } ]
       ]
     },
+    # 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',
       '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
     }
   ] # end targets
 }
-
index 60cc19c..c56e149 100644 (file)
@@ -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>
@@ -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())  {
diff --git a/src/node_counters.cc b/src/node_counters.cc
new file mode 100644 (file)
index 0000000..3c8d49c
--- /dev/null
@@ -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();
+}
+
+}
diff --git a/src/node_counters.h b/src/node_counters.h
new file mode 100644 (file)
index 0000000..c55d99b
--- /dev/null
@@ -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<v8::Object> target);
+void TermPerfCounters(v8::Handle<v8::Object> 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 (file)
index 0000000..8135726
--- /dev/null
@@ -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 <perflib.h>
+#include "node_win32_perfctr_provider.h"
+
+#define __INIT_node_perfctr_provider_IMP
+#include <node_perfctr_provider.h>
+
+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<ULONGLONG>(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<ULONGLONG>(bytes));
+  }
+}
+
+
+uint64_t NODE_COUNT_GET_GC_RAWTIME() {
+  LARGE_INTEGER timegc;
+  if (QueryPerformanceCounter(&timegc)) {
+    return timegc.QuadPart;
+  } else {
+    return static_cast<uint64_t>(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<ULONGLONG>(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<ULONGLONG>(bytes));
+  }
+}
+
+
+}
diff --git a/src/node_win32_perfctr_provider.h b/src/node_win32_perfctr_provider.h
new file mode 100644 (file)
index 0000000..1b16a3f
--- /dev/null
@@ -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 (file)
index 0000000..2f7e3cb
--- /dev/null
@@ -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 (file)
index 0000000..cd945ee
--- /dev/null
@@ -0,0 +1,97 @@
+<instrumentationManifest
+    xmlns="http://schemas.microsoft.com/win/2004/08/events"
+    xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events"
+    xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <instrumentation>
+      <counters xmlns="http://schemas.microsoft.com/win/2005/12/counters"
+                schemaVersion="1.1">
+        <provider symbol="NodeCounterProvider"
+                  applicationIdentity="node.exe"
+                  providerType="userMode"
+                  providerGuid="{1E2E15D7-3760-470E-8699-B9DB5248EDD5}">
+          <counterSet symbol="NodeCounterSet"
+                      guid="{3A22A8EC-297C-48AC-AB15-33EC93033FD8}"
+                      uri="Microsoft.Windows.System.PerfCounters.NodeCounterSet"
+                      name="Node.js"
+                      description="Node.js performance counters"
+                      instances="multiple">
+
+            <counter id="1"
+                     uri="Microsoft.Windows.System.PerfCounters.NodeCounterSet.httpsrvreq"
+                     name="HTTP server requests"
+                     description="Number of HTTP server requests"
+                     type="perf_counter_counter"
+                     detailLevel="standard" />
+
+            <counter id="2"
+                     uri="Microsoft.Windows.System.PerfCounters.NodeCounterSet.httpsrvrsp"
+                     name="HTTP server responses"
+                     description="Number of HTTP server responses"
+                     type="perf_counter_counter"
+                     detailLevel="standard" />
+
+            <counter id="3"
+                     uri="Microsoft.Windows.System.PerfCounters.NodeCounterSet.httpclireq"
+                     name="HTTP client requests"
+                     description="Number of HTTP client requests"
+                     type="perf_counter_counter"
+                     detailLevel="standard" />
+
+            <counter id="4"
+                     uri="Microsoft.Windows.System.PerfCounters.NodeCounterSet.httpclirsp"
+                     name="HTTP client responses"
+                     description="Number of HTTP client responses"
+                     type="perf_counter_counter"
+                     detailLevel="standard" />
+
+            <counter id="5"
+                     uri="Microsoft.Windows.System.PerfCounters.NodeCounterSet.netsrvconn"
+                     name="Active server connections"
+                     description="Number of server connections"
+                     type="perf_counter_rawcount"
+                     detailLevel="standard" />
+
+            <counter id="6"
+                     uri="Microsoft.Windows.System.PerfCounters.NodeCounterSet.netbytesent"
+                     name="Network bytes sent"
+                     description="Number of bytes sent using TCP"
+                     type="perf_counter_bulk_count"
+                     detailLevel="standard"
+                     defaultScale="-3" />
+
+            <counter id="7"
+                     uri="Microsoft.Windows.System.PerfCounters.NodeCounterSet.netbyterecv"
+                     name="Network bytes received"
+                     description="Number of bytes received using TCP"
+                     type="perf_counter_bulk_count"
+                     detailLevel="standard"
+                     defaultScale="-3" />
+
+            <counter id="8"
+                     uri="Microsoft.Windows.System.PerfCounters.NodeCounterSet.gctime"
+                     name="%Time in GC"
+                     description="Percent of time for last GC"
+                     type="perf_counter_rawcount"
+                     detailLevel="standard" />
+
+            <counter id="9"
+                     uri="Microsoft.Windows.System.PerfCounters.NodeCounterSet.pipebytesent"
+                     name="Pipe bytes sent"
+                     description="Number of bytes sent using pipe"
+                     type="perf_counter_bulk_count"
+                     detailLevel="standard"
+                     defaultScale="-3" />
+
+            <counter id="10"
+                     uri="Microsoft.Windows.System.PerfCounters.NodeCounterSet.pipebyterecv"
+                     name="Pipe bytes received"
+                     description="Number of bytes received using pipe"
+                     type="perf_counter_bulk_count"
+                     detailLevel="standard"
+                     defaultScale="-3" />
+
+          </counterSet>
+        </provider>
+      </counters>
+    </instrumentation>
+</instrumentationManifest>
index e79d212..825b65a 100644 (file)
@@ -27,6 +27,7 @@
 #include "pipe_wrap.h"
 #include "tcp_wrap.h"
 #include "req_wrap.h"
+#include "node_counters.h"
 
 #include <stdlib.h> // abort()
 #include <limits.h> // 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<Value> 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<Value> 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_);
   }
 }
index 783466a..948d099 100644 (file)
@@ -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);
index 3406373..7c83c67 100755 (executable)
@@ -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 = []
index 7cc35c0..3f9b0dd 100644 (file)
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
     <OutputPath>..\..\..\$(Configuration)\</OutputPath>
     <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
-    <DefineConstants>Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFilesFolder</DefineConstants>
+    <DefineConstants>Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NoPerfCtr=$(NoPerfCtr);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFilesFolder</DefineConstants>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
     <OutputPath>..\..\..\$(Configuration)\</OutputPath>
     <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
-    <DefineConstants>Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFilesFolder</DefineConstants>
+    <DefineConstants>Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NoPerfCtr=$(NoPerfCtr);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFilesFolder</DefineConstants>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
     <OutputPath>..\..\..\$(Configuration)\</OutputPath>
     <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
-    <DefineConstants>Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFiles64Folder</DefineConstants>
+    <DefineConstants>Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NoPerfCtr=$(NoPerfCtr);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFiles64Folder</DefineConstants>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
     <OutputPath>..\..\..\$(Configuration)\</OutputPath>
     <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
-    <DefineConstants>Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFiles64Folder</DefineConstants>
+    <DefineConstants>Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NoPerfCtr=$(NoPerfCtr);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFiles64Folder</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="product.wxs" />
index 443739d..4668008 100755 (executable)
               <util:EventManifest MessageFile="[APPLICATIONROOTDIRECTORY]node.exe" ResourceFile="[APPLICATIONROOTDIRECTORY]node.exe"/>
             </File>
             <?endif?>
+            <?if $(var.NoPerfCtr) != 1 ?>
+            <File Id="node_perfctr_provider_man" Name="node_perfctr_provider.man" Source="$(var.RepoDir)\src\res\node_perfctr_provider.man" >
+              <util:PerfCounterManifest ResourceFileDirectory="[APPLICATIONROOTDIRECTORY]"/>
+            </File>
+            <?endif?>
             <Environment Id="npm_env"
                          Action="set"
                          Name="PATH"
index b9770c2..f4020df 100644 (file)
@@ -32,6 +32,9 @@ set buildnodeweak=
 set noetw=
 set noetw_arg=
 set noetw_msi_arg=
+set noperfctr=
+set noperfctr_arg=
+set noperfctr_msi_arg=
 
 :next-arg
 if "%1"=="" goto args-done
@@ -46,6 +49,7 @@ if /i "%1"=="nobuild"       set nobuild=1&goto arg-ok
 if /i "%1"=="nosign"        set nosign=1&goto arg-ok
 if /i "%1"=="nosnapshot"    set nosnapshot=1&goto arg-ok
 if /i "%1"=="noetw"         set noetw=1&goto arg-ok
+if /i "%1"=="noperfctr"     set noperfctr=1&goto arg-ok
 if /i "%1"=="licensertf"    set licensertf=1&goto arg-ok
 if /i "%1"=="test-uv"       set test=test-uv&goto arg-ok
 if /i "%1"=="test-internet" set test=test-internet&goto arg-ok
@@ -74,13 +78,14 @@ if "%config%"=="Debug" set debug_arg=--debug
 if "%target_arch%"=="x64" set msiplatform=x64
 if defined nosnapshot set nosnapshot_arg=--without-snapshot
 if defined noetw set noetw_arg=--without-etw& set noetw_msi_arg=/p:NoETW=1
+if defined noperfctr set noperfctr_arg=--without-perfctr& set noperfctr_msi_arg=/p:NoPerfCtr=1
 
 :project-gen
 @rem Skip project generation if requested.
 if defined noprojgen goto msbuild
 
 @rem Generate the VS project.
-python configure %debug_arg% %nosnapshot_arg% %noetw_arg% --dest-cpu=%target_arch%
+python configure %debug_arg% %nosnapshot_arg% %noetw_arg% %noperfctr_arg% --dest-cpu=%target_arch%
 if errorlevel 1 goto create-msvs-files-failed
 if not exist node.sln goto create-msvs-files-failed
 echo Project files generated.
@@ -125,7 +130,7 @@ if not defined msi goto run
 python "%~dp0tools\getnodeversion.py" > "%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