summaryrefslogtreecommitdiff
path: root/src/node_win32_etw_provider.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/node_win32_etw_provider.cc')
-rw-r--r--src/node_win32_etw_provider.cc114
1 files changed, 114 insertions, 0 deletions
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);