Node.js  v8.x
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine
node_win32_etw_provider.cc
Go to the documentation of this file.
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22 #include "node_dtrace.h"
24 #include "node_etw_provider.h"
26 
27 namespace node {
28 
29 using v8::JitCodeEvent;
30 using v8::V8;
31 
32 HMODULE advapi;
33 REGHANDLE node_provider;
34 EventRegisterFunc event_register;
35 EventUnregisterFunc event_unregister;
36 EventWriteFunc event_write;
38 static uv_async_t dispatch_etw_events_change_async;
39 
40 struct v8tags {
41  char prefix[32 - sizeof(size_t)];
42  size_t prelen;
43 };
44 
45 // The v8 CODE_ADDED event name has a prefix indicating the type of event.
46 // Many of these are internal to v8.
47 // The trace_codes array specifies which types are written.
48 struct v8tags trace_codes[] = {
49 #define MAKE_V8TAG(s) { s, sizeof(s) - 1 }
50  MAKE_V8TAG("LazyCompile:"),
51  MAKE_V8TAG("Script:"),
52  MAKE_V8TAG("Function:"),
53  MAKE_V8TAG("RegExp:"),
54  MAKE_V8TAG("Eval:")
55 #undef MAKE_V8TAG
56 };
57 
58 /* Below are some code prefixes which are not being written.
59  * "Builtin:"
60  * "Stub:"
61  * "CallIC:"
62  * "LoadIC:"
63  * "KeyedLoadIC:"
64  * "StoreIC:"
65  * "KeyedStoreIC:"
66  * "CallPreMonomorphic:"
67  * "CallInitialize:"
68  * "CallMiss:"
69  * "CallMegamorphic:"
70  */
71 
72 // v8 sometimes puts a '*' or '~' in front of the name.
73 #define V8_MARKER1 '*'
74 #define V8_MARKER2 '~'
75 
76 
77 // If prefix is not in filtered list return -1,
78 // else return length of prefix and marker.
79 int FilterCodeEvents(const char* name, size_t len) {
80  for (size_t i = 0; i < arraysize(trace_codes); i++) {
81  size_t prelen = trace_codes[i].prelen;
82  if (prelen < len) {
83  if (strncmp(name, trace_codes[i].prefix, prelen) == 0) {
84  if (name[prelen] == V8_MARKER1 || name[prelen] == V8_MARKER2)
85  prelen++;
86  return prelen;
87  }
88  }
89  }
90  return -1;
91 }
92 
93 
94 // callback from V8 module passes symbol and address info for stack walk
95 void CodeAddressNotification(const JitCodeEvent* jevent) {
96  int pre_offset = 0;
97  if (NODE_V8SYMBOL_ENABLED()) {
98  switch (jevent->type) {
99  case JitCodeEvent::CODE_ADDED:
100  pre_offset = FilterCodeEvents(jevent->name.str, jevent->name.len);
101  if (pre_offset >= 0) {
102  // skip over prefix and marker
103  NODE_V8SYMBOL_ADD(jevent->name.str + pre_offset,
104  jevent->name.len - pre_offset,
105  jevent->code_start,
106  jevent->code_len);
107  }
108  break;
109  case JitCodeEvent::CODE_REMOVED:
110  NODE_V8SYMBOL_REMOVE(jevent->code_start, 0);
111  break;
112  case JitCodeEvent::CODE_MOVED:
113  NODE_V8SYMBOL_MOVE(jevent->code_start, jevent->new_code_start);
114  break;
115  default:
116  break;
117  }
118  }
119 }
120 
121 
122 // Call v8 to enable or disable code event callbacks.
123 // Must be on default thread to do this.
124 // Note: It is possible to call v8 from ETW thread, but then
125 // event callbacks are received in the same thread. Attempts
126 // to write ETW events in this thread will fail.
127 void etw_events_change_async(uv_async_t* handle) {
128  if (events_enabled > 0) {
129  NODE_V8SYMBOL_RESET();
130  v8::Isolate::GetCurrent()->SetJitCodeEventHandler(
131  v8::kJitCodeEventEnumExisting,
133  } else {
134  v8::Isolate::GetCurrent()->SetJitCodeEventHandler(
135  v8::kJitCodeEventDefault,
136  nullptr);
137  }
138 }
139 
140 
141 // This callback is called by ETW when consumers of our provider
142 // are enabled or disabled.
143 // The callback is dispatched on ETW thread.
144 // Before calling into V8 to enable code events, switch to default thread.
146  LPCGUID SourceId,
147  ULONG IsEnabled,
148  UCHAR Level,
149  ULONGLONG MatchAnyKeyword,
150  ULONGLONG MatchAllKeywords,
151  PEVENT_FILTER_DESCRIPTOR FilterData,
152  PVOID CallbackContext) {
153  if (IsEnabled) {
154  events_enabled++;
155  if (events_enabled == 1) {
156  uv_async_send(&dispatch_etw_events_change_async);
157  }
158  } else {
159  events_enabled--;
160  if (events_enabled == 0) {
161  uv_async_send(&dispatch_etw_events_change_async);
162  }
163  }
164 }
165 
166 
167 void init_etw() {
168  events_enabled = 0;
169 
170  advapi = LoadLibraryW(L"advapi32.dll");
171  if (advapi) {
172  event_register = (EventRegisterFunc)
173  GetProcAddress(advapi, "EventRegister");
174  event_unregister = (EventUnregisterFunc)
175  GetProcAddress(advapi, "EventUnregister");
176  event_write = (EventWriteFunc)GetProcAddress(advapi, "EventWrite");
177 
178  // create async object used to invoke main thread from callback
179  CHECK_EQ(0, uv_async_init(uv_default_loop(),
180  &dispatch_etw_events_change_async,
182  uv_unref(reinterpret_cast<uv_handle_t*>(&dispatch_etw_events_change_async));
183 
184  if (event_register) {
185  DWORD status = event_register(&NODE_ETW_PROVIDER,
187  nullptr,
188  &node_provider);
189  CHECK_EQ(status, ERROR_SUCCESS);
190  }
191  }
192 }
193 
194 
195 void shutdown_etw() {
196  if (advapi && event_unregister && node_provider) {
197  event_unregister(node_provider);
198  node_provider = 0;
199  }
200 
201  events_enabled = 0;
202  v8::Isolate::GetCurrent()->SetJitCodeEventHandler(
203  v8::kJitCodeEventDefault,
204  nullptr);
205 
206  if (advapi) {
207  FreeLibrary(advapi);
208  advapi = nullptr;
209  }
210 }
211 
212 } // namespace node
EventUnregisterFunc event_unregister
EventWriteFunc event_write
#define V8_MARKER2
int len
Definition: cares_wrap.cc:485
void etw_events_change_async(uv_async_t *handle)
#define MAKE_V8TAG(s)
EventRegisterFunc event_register
int FilterCodeEvents(const char *name, size_t len)
int status
Definition: cares_wrap.cc:479
void CodeAddressNotification(const JitCodeEvent *jevent)
#define V8_MARKER1
char prefix[32 - sizeof(size_t)]
void NTAPI etw_events_enable_callback(LPCGUID SourceId, ULONG IsEnabled, UCHAR Level, ULONGLONG MatchAnyKeyword, ULONGLONG MatchAllKeywords, PEVENT_FILTER_DESCRIPTOR FilterData, PVOID CallbackContext)
struct v8tags trace_codes[]
REGHANDLE node_provider