diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/node_win32_etw_provider-inl.h | 120 | ||||
-rw-r--r-- | src/node_win32_etw_provider.cc | 114 | ||||
-rw-r--r-- | src/node_win32_etw_provider.h | 8 | ||||
-rw-r--r-- | src/res/node_etw_provider.man | 77 |
4 files changed, 306 insertions, 13 deletions
diff --git a/src/node_win32_etw_provider-inl.h b/src/node_win32_etw_provider-inl.h index 9d3f4e546f..db59eea571 100644 --- a/src/node_win32_etw_provider-inl.h +++ b/src/node_win32_etw_provider-inl.h @@ -42,6 +42,20 @@ extern int events_enabled; #define ETW_WRITE_INT32_DATA(data_descriptor, data) \ EventDataDescCreate(data_descriptor, data, sizeof(int32_t)); +#define ETW_WRITE_INT64_DATA(data_descriptor, data) \ + EventDataDescCreate(data_descriptor, data, sizeof(int64_t)); + +#define ETW_WRITE_ADDRESS_DATA(data_descriptor, data) \ + EventDataDescCreate(data_descriptor, data, sizeof(intptr_t)); + +#define ETW_WRITE_INT16_DATA(data_descriptor, data) \ + EventDataDescCreate(data_descriptor, data, sizeof(int16_t)); + +#define ETW_WRITE_WSTRING_DATA_LENGTH(data_descriptor, data, data_len_bytes) \ + EventDataDescCreate(data_descriptor, \ + data, \ + data_len_bytes); + #define ETW_WRITE_NET_CONNECTION(descriptors, conn) \ ETW_WRITE_INT32_DATA(descriptors, &conn->fd); \ ETW_WRITE_INT32_DATA(descriptors + 1, &conn->port); \ @@ -61,6 +75,34 @@ extern int events_enabled; ETW_WRITE_INT32_DATA(descriptors, &type); \ ETW_WRITE_INT32_DATA(descriptors + 1, &flags); +#define ETW_WRITE_V8ADDRESSCHANGE(descriptors, addr1, addr2) \ + ETW_WRITE_ADDRESS_DATA(descriptors, &addr1); \ + ETW_WRITE_ADDRESS_DATA(descriptors + 1, &addr2); + +#define ETW_WRITE_JSMETHOD_LOADUNLOAD(descriptors, \ + context, \ + startAddr, \ + size, \ + id, \ + flags, \ + rangeId, \ + sourceId, \ + line, \ + col, \ + name, \ + name_len_bytes) \ + ETW_WRITE_ADDRESS_DATA(descriptors, &context); \ + ETW_WRITE_ADDRESS_DATA(descriptors + 1, &startAddr); \ + ETW_WRITE_INT64_DATA(descriptors + 2, &size); \ + ETW_WRITE_INT32_DATA(descriptors + 3, &id); \ + ETW_WRITE_INT16_DATA(descriptors + 4, &flags); \ + ETW_WRITE_INT16_DATA(descriptors + 5, &rangeId); \ + ETW_WRITE_INT64_DATA(descriptors + 6, &sourceId); \ + ETW_WRITE_INT32_DATA(descriptors + 7, &line); \ + ETW_WRITE_INT32_DATA(descriptors + 8, &col); \ + ETW_WRITE_WSTRING_DATA_LENGTH(descriptors + 9, name, name_len_bytes); + + #define ETW_WRITE_EVENT(eventDescriptor, dataDescriptors) \ DWORD status = event_write(node_provider, \ &eventDescriptor, \ @@ -133,6 +175,83 @@ void NODE_GC_DONE(GCType type, GCCallbackFlags flags) { } +void NODE_V8SYMBOL_REMOVE(const void* addr1, const void* addr2) { + if (events_enabled > 0) { + EVENT_DATA_DESCRIPTOR descriptors[2]; + ETW_WRITE_V8ADDRESSCHANGE(descriptors, addr1, addr2); + ETW_WRITE_EVENT(NODE_V8SYMBOL_REMOVE_EVENT, descriptors); + } +} + + +void NODE_V8SYMBOL_MOVE(const void* addr1, const void* addr2) { + if (events_enabled > 0) { + EVENT_DATA_DESCRIPTOR descriptors[2]; + ETW_WRITE_V8ADDRESSCHANGE(descriptors, addr1, addr2); + ETW_WRITE_EVENT(NODE_V8SYMBOL_MOVE_EVENT, descriptors); + } +} + + +void NODE_V8SYMBOL_RESET() { + if (events_enabled > 0) { + int val = 0; + EVENT_DATA_DESCRIPTOR descriptors[1]; + ETW_WRITE_INT32_DATA(descriptors, &val); + ETW_WRITE_EVENT(NODE_V8SYMBOL_RESET_EVENT, descriptors); + } +} + +#define SETSYMBUF(s) \ + wcscpy(symbuf, s); \ + symbol_len = ARRAY_SIZE(s) - 1; + +void NODE_V8SYMBOL_ADD(LPCSTR symbol, + int symbol_len, + const void* addr1, + int len) { + if (events_enabled > 0) { + wchar_t symbuf[128]; + if (symbol == NULL) { + SETSYMBUF(L"NULL"); + } else { + symbol_len = MultiByteToWideChar(CP_ACP, 0, symbol, symbol_len, symbuf, 128); + if (symbol_len == 0) { + SETSYMBUF(L"Invalid"); + } else { + if (symbol_len > 127) { + symbol_len = 127; + } + symbuf[symbol_len] = L'\0'; + } + } + void* context = NULL; + INT64 size = (INT64)len; + INT32 id = (INT32)addr1; + INT16 flags = 0; + INT16 rangeid = 1; + INT32 col = 1; + INT32 line = 1; + INT64 sourceid = 0; + EVENT_DATA_DESCRIPTOR descriptors[10]; + ETW_WRITE_JSMETHOD_LOADUNLOAD(descriptors, + context, + addr1, + size, + id, + flags, + rangeid, + sourceid, + line, + col, + symbuf, + symbol_len * sizeof(symbuf[0])); + ETW_WRITE_EVENT(MethodLoad, descriptors); + } +} +#undef SETSYMBUF + + 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; } @@ -141,5 +260,6 @@ 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; } +bool NODE_V8SYMBOL_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 index 78f465c466..26769b0e5a 100644 --- a/src/node_win32_etw_provider.cc +++ b/src/node_win32_etw_provider.cc @@ -22,6 +22,7 @@ #include "node_dtrace.h" #include "node_win32_etw_provider.h" #include "node_etw_provider.h" +#include "node_win32_etw_provider-inl.h" namespace node { @@ -33,10 +34,110 @@ EventRegisterFunc event_register; EventUnregisterFunc event_unregister; EventWriteFunc event_write; int events_enabled; +static uv_async_t dispatch_etw_events_change_async; + +struct v8tags { + char prefix[32 - sizeof(size_t)]; + size_t prelen; +}; + +// The v8 CODE_ADDED event name has a prefix indicating the type of event. +// Many of these are internal to v8. +// The trace_codes array specifies which types are written. +struct v8tags trace_codes[] = { +#define MAKE_V8TAG(s) { s, sizeof(s) - 1 } + MAKE_V8TAG("LazyCompile:"), + MAKE_V8TAG("Script:"), + MAKE_V8TAG("Function:"), + MAKE_V8TAG("RegExp:"), + MAKE_V8TAG("Eval:") +#undef MAKE_V8TAG +}; + +/* Below are some code prefixes which are not being written. + * "Builtin:" + * "Stub:" + * "CallIC:" + * "LoadIC:" + * "KeyedLoadIC:" + * "StoreIC:" + * "KeyedStoreIC:" + * "CallPreMonomorphic:" + * "CallInitialize:" + * "CallMiss:" + * "CallMegamorphic:" + */ + +// v8 sometimes puts a '*' or '~' in front of the name. +#define V8_MARKER1 '*' +#define V8_MARKER2 '~' + + +// If prefix is not in filtered list return -1, +// else return length of prefix and marker. +int FilterCodeEvents(const char* name, size_t len) { + for (int i = 0; i < ARRAY_SIZE(trace_codes); i++) { + size_t prelen = trace_codes[i].prelen; + if (prelen < len) { + if (strncmp(name, trace_codes[i].prefix, prelen) == 0) { + if (name[prelen] == V8_MARKER1 || name[prelen] == V8_MARKER2) + prelen++; + return prelen; + } + } + } + return -1; +} + + +// callback from V8 module passes symbol and address info for stack walk +void CodeAddressNotification(const JitCodeEvent* jevent) { + int pre_offset = 0; + if (NODE_V8SYMBOL_ENABLED()) { + switch (jevent->type) { + case JitCodeEvent::CODE_ADDED: + pre_offset = FilterCodeEvents(jevent->name.str, jevent->name.len); + if (pre_offset >= 0) { + // skip over prefix and marker + NODE_V8SYMBOL_ADD(jevent->name.str + pre_offset, + jevent->name.len - pre_offset, + jevent->code_start, + jevent->code_len); + } + break; + case JitCodeEvent::CODE_REMOVED: + NODE_V8SYMBOL_REMOVE(jevent->code_start, 0); + break; + case JitCodeEvent::CODE_MOVED: + NODE_V8SYMBOL_MOVE(jevent->code_start, jevent->new_code_start); + break; + default: + break; + } + } +} + + +// Call v8 to enable or disable code event callbacks. +// Must be on default thread to do this. +// Note: It is possible to call v8 from ETW thread, but then +// event callbacks are received in the same thread. Attempts +// to write ETW events in this thread will fail. +void etw_events_change_async(uv_async_t* handle, int status) { + if (events_enabled > 0) { + NODE_V8SYMBOL_RESET(); + V8::SetJitCodeEventHandler(v8::kJitCodeEventEnumExisting, + CodeAddressNotification); + } else { + V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL); + } +} + // This callback is called by ETW when consumers of our provider // are enabled or disabled. // The callback is dispatched on ETW thread. +// Before calling into V8 to enable code events, switch to default thread. void NTAPI etw_events_enable_callback( LPCGUID SourceId, ULONG IsEnabled, @@ -47,8 +148,14 @@ void NTAPI etw_events_enable_callback( PVOID CallbackContext) { if (IsEnabled) { events_enabled++; + if (events_enabled == 1) { + uv_async_send(&dispatch_etw_events_change_async); + } } else { events_enabled--; + if (events_enabled == 0) { + uv_async_send(&dispatch_etw_events_change_async); + } } } @@ -64,6 +171,12 @@ void init_etw() { GetProcAddress(advapi, "EventUnregister"); event_write = (EventWriteFunc)GetProcAddress(advapi, "EventWrite"); + // create async object used to invoke main thread from callback + uv_async_init(uv_default_loop(), + &dispatch_etw_events_change_async, + etw_events_change_async); + uv_unref((uv_handle_t*) &dispatch_etw_events_change_async); + if (event_register) { DWORD status = event_register(&NODE_ETW_PROVIDER, etw_events_enable_callback, @@ -82,6 +195,7 @@ void shutdown_etw() { } events_enabled = 0; + V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL); if (advapi) { FreeLibrary(advapi); diff --git a/src/node_win32_etw_provider.h b/src/node_win32_etw_provider.h index adfff37e56..cfc2c8658e 100644 --- a/src/node_win32_etw_provider.h +++ b/src/node_win32_etw_provider.h @@ -66,6 +66,13 @@ 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 void NODE_V8SYMBOL_REMOVE(const void* addr1, const void* addr2); +INLINE void NODE_V8SYMBOL_MOVE(const void* addr1, const void* addr2); +INLINE void NODE_V8SYMBOL_RESET(); +INLINE void NODE_V8SYMBOL_ADD(LPCSTR symbol, + int symbol_len, + const void* addr1, + int len); INLINE bool NODE_HTTP_SERVER_REQUEST_ENABLED(); INLINE bool NODE_HTTP_SERVER_RESPONSE_ENABLED(); @@ -75,6 +82,7 @@ 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(); +INLINE bool NODE_V8SYMBOL_ENABLED(); #define NODE_NET_SOCKET_READ(arg0, arg1) #define NODE_NET_SOCKET_WRITE(arg0, arg1) diff --git a/src/res/node_etw_provider.man b/src/res/node_etw_provider.man index 2d96a20c31..efdc26d3de 100644 --- a/src/res/node_etw_provider.man +++ b/src/res/node_etw_provider.man @@ -1,15 +1,25 @@ <instrumentationManifest - xmlns="http://schemas.microsoft.com/win/2004/08/events" + 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> <events> - <provider name="NodeJS-ETW-provider" - guid="{77754E9B-264B-4D8D-B981-E4135C1ECB0C}" - symbol="NODE_ETW_PROVIDER" - resourceFileName="node.exe" + <provider name="NodeJS-ETW-provider" + guid="{77754E9B-264B-4D8D-B981-E4135C1ECB0C}" + symbol="NODE_ETW_PROVIDER" + resourceFileName="node.exe" messageFileName="node.exe"> + <tasks> + <task name="MethodRuntime" value="1" + symbol="JSCRIPT_METHOD_RUNTIME_TASK"> + <opcodes> + <opcode name="MethodLoad" value="10" + symbol="JSCRIPT_METHOD_METHODLOAD_OPCODE"/> + </opcodes> + </task> + </tasks> + <opcodes> <opcode name="NODE_HTTP_SERVER_REQUEST" value="10"/> <opcode name="NODE_HTTP_SERVER_RESPONSE" value="11"/> @@ -19,6 +29,9 @@ <opcode name="NODE_NET_STREAM_END" value="15"/> <opcode name="NODE_GC_START" value="16"/> <opcode name="NODE_GC_DONE" value="17"/> + <opcode name="NODE_V8SYMBOL_REMOVE" value="21"/> + <opcode name="NODE_V8SYMBOL_MOVE" value="22"/> + <opcode name="NODE_V8SYMBOL_RESET" value="23"/> </opcodes> <templates> @@ -52,49 +65,87 @@ <data name="gctype" inType="win:UInt32" /> <data name="gccallbackflags" inType="win:UInt32" /> </template> + + <template tid="V8AddressChange"> + <data name="addr1" inType="win:Pointer" outType="win:HexInt64"/> + <data name="addr2" inType="win:Pointer" outType="win:HexInt64"/> + </template> + + <template tid="MethodLoadUnload"> + <data name="ScriptContextID" inType="win:Pointer" outType="win:HexInt64"/> + <data name="MethodStartAddress" inType="win:Pointer" outType="win:HexInt64" /> + <data name="MethodSize" inType="win:UInt64" /> + <data name="MethodID" inType="win:UInt32" /> + <data name="MethodFlags" inType="win:UInt16" /> + <data name="MethodAddressRangeID" inType="win:UInt16" /> + <data name="SourceID" inType="win:UInt64" /> + <data name="Line" inType="win:UInt32" outType="xs:unsignedInt" /> + <data name="Column" inType="win:UInt32" outType="xs:unsignedInt" /> + <data name="MethodName" inType="win:UnicodeString" outType="xs:string" /> + </template> </templates> <events> - <event value="1" + <event value="1" opcode="NODE_HTTP_SERVER_REQUEST" template="node_http_server_request" symbol="NODE_HTTP_SERVER_REQUEST_EVENT" level="win:Informational"/> - <event value="2" + <event value="2" opcode="NODE_HTTP_SERVER_RESPONSE" template="node_connection" symbol="NODE_HTTP_SERVER_RESPONSE_EVENT" level="win:Informational"/> - <event value="3" + <event value="3" opcode="NODE_HTTP_CLIENT_REQUEST" template="node_http_client_request" symbol="NODE_HTTP_CLIENT_REQUEST_EVENT" level="win:Informational"/> - <event value="4" + <event value="4" opcode="NODE_HTTP_CLIENT_RESPONSE" template="node_connection" symbol="NODE_HTTP_CLIENT_RESPONSE_EVENT" level="win:Informational"/> - <event value="5" + <event value="5" opcode="NODE_NET_SERVER_CONNECTION" template="node_connection" symbol="NODE_NET_SERVER_CONNECTION_EVENT" level="win:Informational"/> - <event value="6" + <event value="6" opcode="NODE_NET_STREAM_END" template="node_connection" symbol="NODE_NET_STREAM_END_EVENT" level="win:Informational"/> - <event value="7" + <event value="7" opcode="NODE_GC_START" template="node_gc" symbol="NODE_GC_START_EVENT" level="win:Informational"/> - <event value="8" + <event value="8" opcode="NODE_GC_DONE" template="node_gc" symbol="NODE_GC_DONE_EVENT" level="win:Informational"/> + <event value="9" + level="win:Informational" + opcode="MethodLoad" + symbol="MethodLoad" + task="MethodRuntime" + template="MethodLoadUnload"/> + <event value="21" + opcode="NODE_V8SYMBOL_REMOVE" + template="V8AddressChange" + symbol="NODE_V8SYMBOL_REMOVE_EVENT" + level="win:Informational" /> + <event value="22" + opcode="NODE_V8SYMBOL_MOVE" + template="V8AddressChange" + symbol="NODE_V8SYMBOL_MOVE_EVENT" + level="win:Informational" /> + <event value="23" + opcode="NODE_V8SYMBOL_RESET" + symbol="NODE_V8SYMBOL_RESET_EVENT" + level="win:Informational" /> </events> </provider> </events> |