diff --git a/configure b/configure index 8633d48c31d..31d7f82fc68 100755 --- a/configure +++ b/configure @@ -115,6 +115,16 @@ parser.add_option("--without-dtrace", dest="without_dtrace", help="Build without DTrace") +parser.add_option("--with-etw", + action="store_true", + dest="with_etw", + help="Build with ETW (default is true on Windows)") + +parser.add_option("--without-etw", + action="store_true", + dest="without_etw", + help="Build without ETW") + # CHECKME does this still work with recent releases of V8? parser.add_option("--gdb", action="store_true", @@ -273,6 +283,15 @@ def configure_node(o): o['variables']['node_use_dtrace'] = 'false' + # 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': + raise Exception('ETW is only supported on Windows.') + else: + o['variables']['node_use_etw'] = 'false' + + def configure_libz(o): o['variables']['node_shared_zlib'] = b(options.shared_zlib) diff --git a/node.gyp b/node.gyp index 6ab64187cd2..2abdcae1473 100644 --- a/node.gyp +++ b/node.gyp @@ -5,6 +5,7 @@ # See http://codereview.chromium.org/8159015 'werror': '', 'node_use_dtrace%': 'false', + 'node_use_etw%': 'false', 'node_shared_v8%': 'false', 'node_shared_zlib%': 'false', 'node_use_openssl%': 'true', @@ -163,7 +164,18 @@ } ] ], } ], - + [ 'node_use_etw=="true"', { + 'defines': [ 'HAVE_ETW=1' ], + 'dependencies': [ 'node_etw' ], + 'sources': [ + 'src/node_win32_etw_provider.h', + 'src/node_win32_etw_provider-inl.h', + 'src/node_win32_etw_provider.cc', + 'src/node_dtrace.cc', + '<(SHARED_INTERMEDIATE_DIR)/node_etw_provider.h', + '<(SHARED_INTERMEDIATE_DIR)/node_etw_provider.rc', + ] + } ], [ 'node_shared_v8=="true"', { 'sources': [ '<(node_shared_v8_includes)/v8.h', @@ -228,7 +240,23 @@ }, }, }, - + # generate ETW header and resource files + { + 'target_name': 'node_etw', + 'type': 'none', + 'conditions': [ + [ 'node_use_etw=="true"', { + 'actions': [ + { + 'action_name': 'node_etw', + 'inputs': [ 'src/res/node_etw_provider.man' ], + 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)' ], + 'action': [ 'mc <@(_inputs) -h <@(_outputs) -r <@(_outputs)' ] + } + ] + } ] + ] + }, { 'target_name': 'node_js2c', 'type': 'none', @@ -251,7 +279,7 @@ # action? 'conditions': [ - [ 'node_use_dtrace=="true"', { + [ 'node_use_dtrace=="true" or node_use_etw=="true"', { 'action': [ 'python', 'tools/js2c.py', diff --git a/src/node.cc b/src/node.cc index 5db4d86c287..d478f06e76a 100644 --- a/src/node.cc +++ b/src/node.cc @@ -26,10 +26,14 @@ #include "uv.h" #include "v8-debug.h" -#ifdef HAVE_DTRACE +#if defined HAVE_DTRACE || defined HAVE_ETW # include "node_dtrace.h" #endif +#ifdef HAVE_ETW +# include "node_win32_etw_provider.h" +#endif + #include #include #include @@ -2325,7 +2329,7 @@ void Load(Handle process_l) { Local global = v8::Context::GetCurrent()->Global(); Local args[1] = { Local::New(process_l) }; -#ifdef HAVE_DTRACE +#if defined HAVE_DTRACE || defined HAVE_ETW InitDTrace(global); #endif @@ -2894,6 +2898,10 @@ int Start(int argc, char *argv[]) { // watchers, it blocks. uv_run(uv_default_loop()); +#ifdef HAVE_ETW + shutdown_etw(); +#endif + EmitExit(process_l); RunAtExit(); diff --git a/src/node_dtrace.cc b/src/node_dtrace.cc index 01ea7b87fd9..e560999c52f 100644 --- a/src/node_dtrace.cc +++ b/src/node_dtrace.cc @@ -24,6 +24,9 @@ #ifdef HAVE_DTRACE #include "node_provider.h" +#elif HAVE_ETW +#include "node_win32_etw_provider.h" +#include "node_win32_etw_provider-inl.h" #else #define NODE_HTTP_SERVER_REQUEST(arg0, arg1) #define NODE_HTTP_SERVER_REQUEST_ENABLED() (0) @@ -315,7 +318,11 @@ void InitDTrace(Handle target) { target->Set(String::NewSymbol(tab[i].name), tab[i].templ->GetFunction()); } -#ifdef HAVE_DTRACE +#ifdef HAVE_ETW + init_etw(); +#endif + +#if defined HAVE_DTRACE || defined HAVE_ETW v8::V8::AddGCPrologueCallback((GCPrologueCallback)dtrace_gc_start); v8::V8::AddGCEpilogueCallback((GCEpilogueCallback)dtrace_gc_done); #endif diff --git a/src/node_win32_etw_provider-inl.h b/src/node_win32_etw_provider-inl.h new file mode 100644 index 00000000000..70b04acba1e --- /dev/null +++ b/src/node_win32_etw_provider-inl.h @@ -0,0 +1,141 @@ +// 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_ETW_INL_H_ +#define SRC_ETW_INL_H_ + +#include "node_win32_etw_provider.h" +#include "node_etw_provider.h" + +namespace node { + +using namespace v8; + +// From node_win32_etw_provider.cc +extern REGHANDLE node_provider; +extern EventWriteFunc event_write; +extern int events_enabled; + +#define ETW_WRITE_STRING_DATA(data_descriptor, data) \ + EventDataDescCreate(data_descriptor, \ + data, \ + (strlen(data) + 1) * sizeof(char)); + +#define ETW_WRITE_INT32_DATA(data_descriptor, data) \ + EventDataDescCreate(data_descriptor, data, sizeof(int32_t)); + +#define ETW_WRITE_NET_CONNECTION(descriptors, conn) \ + ETW_WRITE_INT32_DATA(descriptors, &conn->fd); \ + ETW_WRITE_INT32_DATA(descriptors + 1, &conn->port); \ + ETW_WRITE_STRING_DATA(descriptors + 2, conn->remote); \ + ETW_WRITE_INT32_DATA(descriptors + 3, &conn->buffered); + +#define ETW_WRITE_HTTP_SERVER_REQUEST(descriptors, req) \ + ETW_WRITE_STRING_DATA(descriptors, req->url); \ + ETW_WRITE_STRING_DATA(descriptors + 1, req->method); \ + ETW_WRITE_STRING_DATA(descriptors + 2, req->forwardedFor); + +#define ETW_WRITE_HTTP_CLIENT_REQUEST(descriptors, req) \ + ETW_WRITE_STRING_DATA(descriptors, req->url); \ + ETW_WRITE_STRING_DATA(descriptors + 1, req->method); + +#define ETW_WRITE_GC(descriptors, type, flags) \ + ETW_WRITE_INT32_DATA(descriptors, &type); \ + ETW_WRITE_INT32_DATA(descriptors + 1, &flags); + +#define ETW_WRITE_EVENT(eventDescriptor, dataDescriptors) \ + DWORD status = event_write(node_provider, \ + &eventDescriptor, \ + sizeof(dataDescriptors)/sizeof(*dataDescriptors),\ + dataDescriptors); \ + assert(status == ERROR_SUCCESS); + + +void NODE_HTTP_SERVER_REQUEST(node_dtrace_http_server_request_t* req, + node_dtrace_connection_t* conn) { + EVENT_DATA_DESCRIPTOR descriptors[7]; + ETW_WRITE_HTTP_SERVER_REQUEST(descriptors, req); + ETW_WRITE_NET_CONNECTION(descriptors + 3, conn); + ETW_WRITE_EVENT(NODE_HTTP_SERVER_REQUEST_EVENT, descriptors); +} + + +void NODE_HTTP_SERVER_RESPONSE(node_dtrace_connection_t* conn) { + EVENT_DATA_DESCRIPTOR descriptors[4]; + ETW_WRITE_NET_CONNECTION(descriptors, conn); + ETW_WRITE_EVENT(NODE_HTTP_SERVER_RESPONSE_EVENT, descriptors); +} + + +void NODE_HTTP_CLIENT_REQUEST(node_dtrace_http_client_request_t* req, + node_dtrace_connection_t* conn) { + EVENT_DATA_DESCRIPTOR descriptors[6]; + ETW_WRITE_HTTP_CLIENT_REQUEST(descriptors, req); + ETW_WRITE_NET_CONNECTION(descriptors + 2, conn); + ETW_WRITE_EVENT(NODE_HTTP_CLIENT_REQUEST_EVENT, descriptors); +} + + +void NODE_HTTP_CLIENT_RESPONSE(node_dtrace_connection_t* conn) { + EVENT_DATA_DESCRIPTOR descriptors[4]; + ETW_WRITE_NET_CONNECTION(descriptors, conn); + ETW_WRITE_EVENT(NODE_HTTP_CLIENT_RESPONSE_EVENT, descriptors); +} + + +void NODE_NET_SERVER_CONNECTION(node_dtrace_connection_t* conn) { + EVENT_DATA_DESCRIPTOR descriptors[4]; + ETW_WRITE_NET_CONNECTION(descriptors, conn); + ETW_WRITE_EVENT(NODE_NET_SERVER_CONNECTION_EVENT, descriptors); +} + + +void NODE_NET_STREAM_END(node_dtrace_connection_t* conn) { + EVENT_DATA_DESCRIPTOR descriptors[4]; + ETW_WRITE_NET_CONNECTION(descriptors, conn); + ETW_WRITE_EVENT(NODE_NET_STREAM_END_EVENT, descriptors); +} + + +void NODE_GC_START(GCType type, GCCallbackFlags flags) { + EVENT_DATA_DESCRIPTOR descriptors[2]; + ETW_WRITE_GC(descriptors, type, flags); + ETW_WRITE_EVENT(NODE_GC_START_EVENT, descriptors); +} + + +void NODE_GC_DONE(GCType type, GCCallbackFlags flags) { + EVENT_DATA_DESCRIPTOR descriptors[2]; + ETW_WRITE_GC(descriptors, type, flags); + ETW_WRITE_EVENT(NODE_GC_DONE_EVENT, descriptors); +} + + +bool NODE_HTTP_SERVER_REQUEST_ENABLED() { return events_enabled > 0; } +bool NODE_HTTP_SERVER_RESPONSE_ENABLED() { return events_enabled > 0; } +bool NODE_HTTP_CLIENT_REQUEST_ENABLED() { return events_enabled > 0; } +bool NODE_HTTP_CLIENT_RESPONSE_ENABLED() { return events_enabled > 0; } +bool NODE_NET_SERVER_CONNECTION_ENABLED() { return events_enabled > 0; } +bool NODE_NET_STREAM_END_ENABLED() { return events_enabled > 0; } +bool NODE_NET_SOCKET_READ_ENABLED() { return events_enabled > 0; } +bool NODE_NET_SOCKET_WRITE_ENABLED() { return events_enabled > 0; } +} +#endif // SRC_ETW_INL_H_ \ No newline at end of file diff --git a/src/node_win32_etw_provider.cc b/src/node_win32_etw_provider.cc new file mode 100644 index 00000000000..78f465c4669 --- /dev/null +++ b/src/node_win32_etw_provider.cc @@ -0,0 +1,91 @@ +// 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_dtrace.h" +#include "node_win32_etw_provider.h" +#include "node_etw_provider.h" + +namespace node { + +using namespace v8; + +HMODULE advapi; +REGHANDLE node_provider; +EventRegisterFunc event_register; +EventUnregisterFunc event_unregister; +EventWriteFunc event_write; +int events_enabled; + +// This callback is called by ETW when consumers of our provider +// are enabled or disabled. +// The callback is dispatched on ETW thread. +void NTAPI etw_events_enable_callback( + LPCGUID SourceId, + ULONG IsEnabled, + UCHAR Level, + ULONGLONG MatchAnyKeyword, + ULONGLONG MatchAllKeywords, + PEVENT_FILTER_DESCRIPTOR FilterData, + PVOID CallbackContext) { + if (IsEnabled) { + events_enabled++; + } else { + events_enabled--; + } +} + + +void init_etw() { + events_enabled = 0; + + advapi = LoadLibrary("advapi32.dll"); + if (advapi) { + event_register = (EventRegisterFunc) + GetProcAddress(advapi, "EventRegister"); + event_unregister = (EventUnregisterFunc) + GetProcAddress(advapi, "EventUnregister"); + event_write = (EventWriteFunc)GetProcAddress(advapi, "EventWrite"); + + if (event_register) { + DWORD status = event_register(&NODE_ETW_PROVIDER, + etw_events_enable_callback, + NULL, + &node_provider); + assert(status == ERROR_SUCCESS); + } + } +} + + +void shutdown_etw() { + if (advapi && event_unregister && node_provider) { + event_unregister(node_provider); + node_provider = 0; + } + + events_enabled = 0; + + if (advapi) { + FreeLibrary(advapi); + advapi = NULL; + } +} +} \ No newline at end of file diff --git a/src/node_win32_etw_provider.h b/src/node_win32_etw_provider.h new file mode 100644 index 00000000000..adfff37e568 --- /dev/null +++ b/src/node_win32_etw_provider.h @@ -0,0 +1,82 @@ +// 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_ETW_H_ +#define SRC_ETW_H_ + +#include +#include "node_dtrace.h" + +namespace node { + +using namespace v8; + +#if defined(_MSC_VER) +# define INLINE __forceinline +#else +# define INLINE inline +#endif + +typedef ULONG (NTAPI *EventRegisterFunc)( + LPCGUID ProviderId, + PENABLECALLBACK EnableCallback, + PVOID CallbackContext, + PREGHANDLE RegHandle +); + +typedef ULONG (NTAPI *EventUnregisterFunc)( + REGHANDLE RegHandle +); + +typedef ULONG (NTAPI *EventWriteFunc)( + REGHANDLE RegHandle, + PCEVENT_DESCRIPTOR EventDescriptor, + ULONG UserDataCount, + PEVENT_DATA_DESCRIPTOR UserData +); + +void init_etw(); +void shutdown_etw(); + +INLINE void NODE_HTTP_SERVER_REQUEST(node_dtrace_http_server_request_t* req, + node_dtrace_connection_t* conn); +INLINE void NODE_HTTP_SERVER_RESPONSE(node_dtrace_connection_t* conn); +INLINE void NODE_HTTP_CLIENT_REQUEST(node_dtrace_http_client_request_t* req, + node_dtrace_connection_t* conn); +INLINE void NODE_HTTP_CLIENT_RESPONSE(node_dtrace_connection_t* conn); +INLINE void NODE_NET_SERVER_CONNECTION(node_dtrace_connection_t* conn); +INLINE void NODE_NET_STREAM_END(node_dtrace_connection_t* conn); +INLINE void NODE_GC_START(GCType type, GCCallbackFlags flags); +INLINE void NODE_GC_DONE(GCType type, GCCallbackFlags flags); + +INLINE bool NODE_HTTP_SERVER_REQUEST_ENABLED(); +INLINE bool NODE_HTTP_SERVER_RESPONSE_ENABLED(); +INLINE bool NODE_HTTP_CLIENT_REQUEST_ENABLED(); +INLINE bool NODE_HTTP_CLIENT_RESPONSE_ENABLED(); +INLINE bool NODE_NET_SERVER_CONNECTION_ENABLED(); +INLINE bool NODE_NET_STREAM_END_ENABLED(); +INLINE bool NODE_NET_SOCKET_READ_ENABLED(); +INLINE bool NODE_NET_SOCKET_WRITE_ENABLED(); + +#define NODE_NET_SOCKET_READ(arg0, arg1) +#define NODE_NET_SOCKET_WRITE(arg0, arg1) +} +#endif // SRC_ETW_H_ \ No newline at end of file diff --git a/src/res/node_etw_provider.man b/src/res/node_etw_provider.man new file mode 100644 index 00000000000..2d96a20c31a --- /dev/null +++ b/src/res/node_etw_provider.man @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/msvs/msi/nodemsi.wixproj b/tools/msvs/msi/nodemsi.wixproj index a7f71de779e..7cc35c07e65 100644 --- a/tools/msvs/msi/nodemsi.wixproj +++ b/tools/msvs/msi/nodemsi.wixproj @@ -16,22 +16,22 @@ ..\..\..\$(Configuration)\ obj\$(Configuration)\ - Debug;ProductVersion=$(NodeVersion);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFilesFolder + Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFilesFolder ..\..\..\$(Configuration)\ obj\$(Configuration)\ - Debug;ProductVersion=$(NodeVersion);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFilesFolder + Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFilesFolder ..\..\..\$(Configuration)\ obj\$(Configuration)\ - Debug;ProductVersion=$(NodeVersion);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFiles64Folder + Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFiles64Folder ..\..\..\$(Configuration)\ obj\$(Configuration)\ - Debug;ProductVersion=$(NodeVersion);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFiles64Folder + Debug;ProductVersion=$(NodeVersion);NoETW=$(NoETW);NPMSourceDir=..\..\..\deps\npm\;ProgramFilesFolderId=ProgramFiles64Folder diff --git a/tools/msvs/msi/product.wxs b/tools/msvs/msi/product.wxs index 94eeed25c7b..b14c65e7d59 100755 --- a/tools/msvs/msi/product.wxs +++ b/tools/msvs/msi/product.wxs @@ -40,6 +40,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% /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% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo if errorlevel 1 goto exit if defined nosign goto run