• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_win32_etw_provider.h"  // NOLINT(build/include_inline)
23 #include "node_win32_etw_provider-inl.h"
24 
25 #include "node_etw_provider.h"
26 
27 namespace node {
28 
29 using v8::Isolate;
30 using v8::JitCodeEvent;
31 using v8::V8;
32 
33 HMODULE advapi;
34 REGHANDLE node_provider;
35 EventRegisterFunc event_register;
36 EventUnregisterFunc event_unregister;
37 EventWriteFunc event_write;
38 int events_enabled;
39 static uv_async_t dispatch_etw_events_change_async;
40 
41 struct v8tags {
42   char prefix[32 - sizeof(size_t)];
43   size_t prelen;
44 };
45 
46 // The v8 CODE_ADDED event name has a prefix indicating the type of event.
47 // Many of these are internal to v8.
48 // The trace_codes array specifies which types are written.
49 struct v8tags trace_codes[] = {
50 #define MAKE_V8TAG(s) { s, sizeof(s) - 1 }
51   MAKE_V8TAG("LazyCompile:"),
52   MAKE_V8TAG("Script:"),
53   MAKE_V8TAG("Function:"),
54   MAKE_V8TAG("RegExp:"),
55   MAKE_V8TAG("Eval:")
56 #undef MAKE_V8TAG
57 };
58 
59 /* Below are some code prefixes which are not being written.
60  *    "Builtin:"
61  *    "Stub:"
62  *    "CallIC:"
63  *    "LoadIC:"
64  *    "KeyedLoadIC:"
65  *    "StoreIC:"
66  *    "KeyedStoreIC:"
67  *    "CallPreMonomorphic:"
68  *    "CallInitialize:"
69  *    "CallMiss:"
70  *    "CallMegamorphic:"
71  */
72 
73 // v8 sometimes puts a '*' or '~' in front of the name.
74 #define V8_MARKER1 '*'
75 #define V8_MARKER2 '~'
76 
77 
78 // If prefix is not in filtered list return -1,
79 // else return length of prefix and marker.
FilterCodeEvents(const char * name,size_t len)80 int FilterCodeEvents(const char* name, size_t len) {
81   for (size_t i = 0; i < arraysize(trace_codes); i++) {
82     size_t prelen = trace_codes[i].prelen;
83     if (prelen < len) {
84       if (strncmp(name, trace_codes[i].prefix, prelen) == 0) {
85         if (name[prelen] == V8_MARKER1 || name[prelen] == V8_MARKER2)
86           prelen++;
87         return prelen;
88       }
89     }
90   }
91   return -1;
92 }
93 
94 
95 // callback from V8 module passes symbol and address info for stack walk
CodeAddressNotification(const JitCodeEvent * jevent)96 void CodeAddressNotification(const JitCodeEvent* jevent) {
97   int pre_offset = 0;
98   if (NODE_V8SYMBOL_ENABLED()) {
99     switch (jevent->type) {
100     case JitCodeEvent::CODE_ADDED:
101       pre_offset = FilterCodeEvents(jevent->name.str, jevent->name.len);
102       if (pre_offset >= 0) {
103         // skip over prefix and marker
104         NODE_V8SYMBOL_ADD(jevent->name.str + pre_offset,
105                           jevent->name.len - pre_offset,
106                           jevent->code_start,
107                           jevent->code_len);
108       }
109       break;
110     case JitCodeEvent::CODE_REMOVED:
111       NODE_V8SYMBOL_REMOVE(jevent->code_start, nullptr);
112       break;
113     case JitCodeEvent::CODE_MOVED:
114       NODE_V8SYMBOL_MOVE(jevent->code_start, jevent->new_code_start);
115       break;
116     default:
117       break;
118     }
119   }
120 }
121 
122 
123 // Call v8 to enable or disable code event callbacks.
124 // Must be on default thread to do this.
125 // Note: It is possible to call v8 from ETW thread, but then
126 //       event callbacks are received in the same thread. Attempts
127 //       to write ETW events in this thread will fail.
etw_events_change_async(uv_async_t * handle)128 void etw_events_change_async(uv_async_t* handle) {
129   if (events_enabled > 0) {
130     NODE_V8SYMBOL_RESET();
131     Isolate::GetCurrent()->SetJitCodeEventHandler(
132         v8::kJitCodeEventEnumExisting,
133         CodeAddressNotification);
134   } else {
135     Isolate::GetCurrent()->SetJitCodeEventHandler(
136         v8::kJitCodeEventDefault,
137         nullptr);
138   }
139 }
140 
141 
142 // This callback is called by ETW when consumers of our provider
143 // are enabled or disabled.
144 // The callback is dispatched on ETW thread.
145 // Before calling into V8 to enable code events, switch to default thread.
etw_events_enable_callback(LPCGUID SourceId,ULONG IsEnabled,UCHAR Level,ULONGLONG MatchAnyKeyword,ULONGLONG MatchAllKeywords,PEVENT_FILTER_DESCRIPTOR FilterData,PVOID CallbackContext)146 void NTAPI etw_events_enable_callback(
147   LPCGUID SourceId,
148   ULONG IsEnabled,
149   UCHAR Level,
150   ULONGLONG MatchAnyKeyword,
151   ULONGLONG MatchAllKeywords,
152   PEVENT_FILTER_DESCRIPTOR FilterData,
153   PVOID CallbackContext) {
154   if (IsEnabled) {
155     events_enabled++;
156     if (events_enabled == 1) {
157       uv_async_send(&dispatch_etw_events_change_async);
158     }
159   } else {
160     events_enabled--;
161     if (events_enabled == 0) {
162       uv_async_send(&dispatch_etw_events_change_async);
163     }
164   }
165 }
166 
167 
init_etw()168 void init_etw() {
169   events_enabled = 0;
170 
171   advapi = LoadLibraryW(L"advapi32.dll");
172   if (advapi) {
173     event_register = (EventRegisterFunc)
174       GetProcAddress(advapi, "EventRegister");
175     event_unregister = (EventUnregisterFunc)
176       GetProcAddress(advapi, "EventUnregister");
177     event_write = (EventWriteFunc)GetProcAddress(advapi, "EventWrite");
178 
179     // create async object used to invoke main thread from callback
180     CHECK_EQ(0, uv_async_init(uv_default_loop(),
181                               &dispatch_etw_events_change_async,
182                               etw_events_change_async));
183     uv_unref(reinterpret_cast<uv_handle_t*>(&dispatch_etw_events_change_async));
184 
185     if (event_register) {
186       DWORD status = event_register(&NODE_ETW_PROVIDER,
187                                     etw_events_enable_callback,
188                                     nullptr,
189                                     &node_provider);
190       CHECK_EQ(status, ERROR_SUCCESS);
191     }
192   }
193 }
194 
195 
shutdown_etw()196 void shutdown_etw() {
197   if (advapi && event_unregister && node_provider) {
198     event_unregister(node_provider);
199     node_provider = 0;
200   }
201 
202   events_enabled = 0;
203   Isolate::GetCurrent()->SetJitCodeEventHandler(
204       v8::kJitCodeEventDefault,
205       nullptr);
206 
207   if (advapi) {
208     FreeLibrary(advapi);
209     advapi = nullptr;
210   }
211 }
212 
213 }  // namespace node
214