1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | #ifndef __EVENTPIPE_H__ |
6 | #define __EVENTPIPE_H__ |
7 | |
8 | #ifdef FEATURE_PERFTRACING |
9 | #include "common.h" |
10 | |
11 | class CrstStatic; |
12 | class CrawlFrame; |
13 | class EventPipeConfiguration; |
14 | class EventPipeEvent; |
15 | class EventPipeEventInstance; |
16 | class EventPipeFile; |
17 | class EventPipeJsonFile; |
18 | class EventPipeBuffer; |
19 | class EventPipeBufferManager; |
20 | class EventPipeEventSource; |
21 | class EventPipeProvider; |
22 | class MethodDesc; |
23 | class SampleProfilerEventInstance; |
24 | struct EventPipeProviderConfiguration; |
25 | class EventPipeSession; |
26 | |
27 | // EVENT_FILTER_DESCRIPTOR (This type does not exist on non-Windows platforms.) |
28 | // https://docs.microsoft.com/en-us/windows/desktop/api/evntprov/ns-evntprov-_event_filter_descriptor |
29 | // The structure supplements the event provider, level, and keyword data that |
30 | // determines which events are reported and traced. The structure gives the |
31 | // event provider greater control over the selection of events for reporting |
32 | // and tracing. |
33 | struct EventFilterDescriptor |
34 | { |
35 | // A pointer to the filter data. |
36 | ULONGLONG Ptr; |
37 | |
38 | // The size of the filter data, in bytes. The maximum size is 1024 bytes. |
39 | ULONG Size; |
40 | |
41 | // The type of filter data. The type is application-defined. An event |
42 | // controller that knows about the provider and knows details about the |
43 | // provider's events can use the Type field to send the provider an |
44 | // arbitrary set of data for use as enhancements to the filtering of events. |
45 | ULONG Type; |
46 | }; |
47 | |
48 | // Define the event pipe callback to match the ETW callback signature. |
49 | typedef void (*EventPipeCallback)( |
50 | LPCGUID SourceID, |
51 | ULONG IsEnabled, |
52 | UCHAR Level, |
53 | ULONGLONG MatchAnyKeywords, |
54 | ULONGLONG MatchAllKeywords, |
55 | EventFilterDescriptor *FilterData, |
56 | void *CallbackContext); |
57 | |
58 | struct EventData |
59 | { |
60 | public: |
61 | UINT64 Ptr; |
62 | unsigned int Size; |
63 | unsigned int Reserved; |
64 | }; |
65 | |
66 | class EventPipeEventPayload |
67 | { |
68 | private: |
69 | BYTE *m_pData; |
70 | EventData *m_pEventData; |
71 | unsigned int m_eventDataCount; |
72 | unsigned int m_size; |
73 | bool m_allocatedData; |
74 | |
75 | // If the data is stored only as an array of EventData objects, create a flat buffer and copy into it |
76 | void Flatten(); |
77 | |
78 | public: |
79 | // Build this payload with a flat buffer inside |
80 | EventPipeEventPayload(BYTE *pData, unsigned int length); |
81 | |
82 | // Build this payload to contain an array of EventData objects |
83 | EventPipeEventPayload(EventData *pEventData, unsigned int eventDataCount); |
84 | |
85 | // If a buffer was allocated internally, delete it |
86 | ~EventPipeEventPayload(); |
87 | |
88 | // Copy the data (whether flat or array of objects) into a flat buffer at pDst |
89 | // Assumes that pDst points to an appropriatly sized buffer |
90 | void CopyData(BYTE *pDst); |
91 | |
92 | // Get the flat formatted data in this payload |
93 | // This method will allocate a buffer if it does not already contain flattened data |
94 | // This method will return NULL on OOM if a buffer needed to be allocated |
95 | BYTE* GetFlatData(); |
96 | |
97 | // Return true is the data is stored in a flat buffer |
98 | bool IsFlattened() const |
99 | { |
100 | LIMITED_METHOD_CONTRACT; |
101 | |
102 | return m_pData != NULL; |
103 | } |
104 | |
105 | // The the size of buffer needed to contain the stored data |
106 | unsigned int GetSize() const |
107 | { |
108 | LIMITED_METHOD_CONTRACT; |
109 | |
110 | return m_size; |
111 | } |
112 | |
113 | EventData* GetEventDataArray() const |
114 | { |
115 | LIMITED_METHOD_CONTRACT; |
116 | |
117 | return m_pEventData; |
118 | } |
119 | }; |
120 | |
121 | class StackContents |
122 | { |
123 | private: |
124 | |
125 | const static unsigned int MAX_STACK_DEPTH = 100; |
126 | |
127 | // Array of IP values from a stack crawl. |
128 | // Top of stack is at index 0. |
129 | UINT_PTR m_stackFrames[MAX_STACK_DEPTH]; |
130 | |
131 | #ifdef _DEBUG |
132 | // Parallel array of MethodDesc pointers. |
133 | // Used for debug-only stack printing. |
134 | MethodDesc* m_methods[MAX_STACK_DEPTH]; |
135 | #endif // _DEBUG |
136 | |
137 | // The next available slot in StackFrames. |
138 | unsigned int m_nextAvailableFrame; |
139 | |
140 | public: |
141 | |
142 | StackContents() |
143 | { |
144 | LIMITED_METHOD_CONTRACT; |
145 | |
146 | Reset(); |
147 | } |
148 | |
149 | void CopyTo(StackContents *pDest) |
150 | { |
151 | LIMITED_METHOD_CONTRACT; |
152 | _ASSERTE(pDest != NULL); |
153 | |
154 | memcpy_s(pDest->m_stackFrames, MAX_STACK_DEPTH * sizeof(UINT_PTR), m_stackFrames, sizeof(UINT_PTR) * m_nextAvailableFrame); |
155 | #ifdef _DEBUG |
156 | memcpy_s(pDest->m_methods, MAX_STACK_DEPTH * sizeof(MethodDesc*), m_methods, sizeof(MethodDesc*) * m_nextAvailableFrame); |
157 | #endif |
158 | pDest->m_nextAvailableFrame = m_nextAvailableFrame; |
159 | } |
160 | |
161 | void Reset() |
162 | { |
163 | LIMITED_METHOD_CONTRACT; |
164 | |
165 | m_nextAvailableFrame = 0; |
166 | } |
167 | |
168 | bool IsEmpty() |
169 | { |
170 | LIMITED_METHOD_CONTRACT; |
171 | |
172 | return (m_nextAvailableFrame == 0); |
173 | } |
174 | |
175 | unsigned int GetLength() |
176 | { |
177 | LIMITED_METHOD_CONTRACT; |
178 | |
179 | return m_nextAvailableFrame; |
180 | } |
181 | |
182 | UINT_PTR GetIP(unsigned int frameIndex) |
183 | { |
184 | LIMITED_METHOD_CONTRACT; |
185 | _ASSERTE(frameIndex < MAX_STACK_DEPTH); |
186 | |
187 | if (frameIndex >= MAX_STACK_DEPTH) |
188 | { |
189 | return 0; |
190 | } |
191 | |
192 | return m_stackFrames[frameIndex]; |
193 | } |
194 | |
195 | #ifdef _DEBUG |
196 | MethodDesc* GetMethod(unsigned int frameIndex) |
197 | { |
198 | LIMITED_METHOD_CONTRACT; |
199 | _ASSERTE(frameIndex < MAX_STACK_DEPTH); |
200 | |
201 | if (frameIndex >= MAX_STACK_DEPTH) |
202 | { |
203 | return NULL; |
204 | } |
205 | |
206 | return m_methods[frameIndex]; |
207 | } |
208 | #endif // _DEBUG |
209 | |
210 | void Append(UINT_PTR controlPC, MethodDesc *pMethod) |
211 | { |
212 | LIMITED_METHOD_CONTRACT; |
213 | |
214 | if(m_nextAvailableFrame < MAX_STACK_DEPTH) |
215 | { |
216 | m_stackFrames[m_nextAvailableFrame] = controlPC; |
217 | #ifdef _DEBUG |
218 | m_methods[m_nextAvailableFrame] = pMethod; |
219 | #endif |
220 | m_nextAvailableFrame++; |
221 | } |
222 | } |
223 | |
224 | BYTE* GetPointer() const |
225 | { |
226 | LIMITED_METHOD_CONTRACT; |
227 | |
228 | return (BYTE*)m_stackFrames; |
229 | } |
230 | |
231 | unsigned int GetSize() const |
232 | { |
233 | LIMITED_METHOD_CONTRACT; |
234 | |
235 | return (m_nextAvailableFrame * sizeof(UINT_PTR)); |
236 | } |
237 | }; |
238 | |
239 | typedef UINT64 EventPipeSessionID; |
240 | |
241 | class EventPipe |
242 | { |
243 | // Declare friends. |
244 | friend class EventPipeConfiguration; |
245 | friend class EventPipeFile; |
246 | friend class EventPipeProvider; |
247 | friend class EventPipeBufferManager; |
248 | friend class SampleProfiler; |
249 | |
250 | public: |
251 | |
252 | // Initialize the event pipe. |
253 | static void Initialize(); |
254 | |
255 | // Shutdown the event pipe. |
256 | static void Shutdown(); |
257 | |
258 | // Enable tracing via the event pipe. |
259 | static EventPipeSessionID Enable( |
260 | LPCWSTR strOutputPath, |
261 | unsigned int circularBufferSizeInMB, |
262 | EventPipeProviderConfiguration *pProviders, |
263 | int numProviders, |
264 | UINT64 multiFileTraceLengthInSeconds); |
265 | |
266 | // Disable tracing via the event pipe. |
267 | static void Disable(EventPipeSessionID id); |
268 | |
269 | // Get the session for the specified session ID. |
270 | static EventPipeSession* GetSession(EventPipeSessionID id); |
271 | |
272 | // Specifies whether or not the event pipe is enabled. |
273 | static bool Enabled(); |
274 | |
275 | // Create a provider. |
276 | static EventPipeProvider* CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL); |
277 | |
278 | // Get a provider. |
279 | static EventPipeProvider* GetProvider(const SString &providerName); |
280 | |
281 | // Delete a provider. |
282 | static void DeleteProvider(EventPipeProvider *pProvider); |
283 | |
284 | // Write out an event from a flat buffer. |
285 | // Data is written as a serialized blob matching the ETW serialization conventions. |
286 | static void WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL); |
287 | |
288 | // Write out an event from an EventData array. |
289 | // Data is written as a serialized blob matching the ETW serialization conventions. |
290 | static void WriteEvent(EventPipeEvent &event, EventData *pEventData, unsigned int eventDataCount, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL); |
291 | |
292 | // Write out a sample profile event. |
293 | static void WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData = NULL, unsigned int length = 0); |
294 | |
295 | // Get the managed call stack for the current thread. |
296 | static bool WalkManagedStackForCurrentThread(StackContents &stackContents); |
297 | |
298 | // Get the managed call stack for the specified thread. |
299 | static bool WalkManagedStackForThread(Thread *pThread, StackContents &stackContents); |
300 | |
301 | // Save the command line for the current process. |
302 | static void SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv); |
303 | |
304 | // Get next event. |
305 | static EventPipeEventInstance* GetNextEvent(); |
306 | |
307 | protected: |
308 | |
309 | // The counterpart to WriteEvent which after the payload is constructed |
310 | static void WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL); |
311 | |
312 | private: |
313 | |
314 | // Enable the specified EventPipe session. |
315 | static EventPipeSessionID Enable(LPCWSTR strOutputPath, EventPipeSession *pSession); |
316 | |
317 | static void CreateFileSwitchTimer(); |
318 | |
319 | static void DeleteFileSwitchTimer(); |
320 | |
321 | // Performs one polling operation to determine if it is necessary to switch to a new file. |
322 | // If the polling operation decides it is time, it will perform the switch. |
323 | // Called directly from the timer when the timer is triggered. |
324 | static void WINAPI SwitchToNextFileTimerCallback(PVOID parameter, BOOLEAN timerFired); |
325 | |
326 | // If event pipe has been configured to write multiple files, switch to the next file. |
327 | static void SwitchToNextFile(); |
328 | |
329 | // Generate the file path for the next trace file. |
330 | // This is used when event pipe has been configured to create multiple trace files with a specified maximum length of time. |
331 | static void GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFilePath); |
332 | |
333 | // Callback function for the stack walker. For each frame walked, this callback is invoked. |
334 | static StackWalkAction StackWalkCallback(CrawlFrame *pCf, StackContents *pData); |
335 | |
336 | // Get the configuration object. |
337 | // This is called directly by the EventPipeProvider constructor to register the new provider. |
338 | static EventPipeConfiguration* GetConfiguration(); |
339 | |
340 | // Get the event pipe configuration lock. |
341 | static CrstStatic* GetLock(); |
342 | |
343 | static CrstStatic s_configCrst; |
344 | static bool s_tracingInitialized; |
345 | static EventPipeConfiguration *s_pConfig; |
346 | static EventPipeSession *s_pSession; |
347 | static EventPipeBufferManager *s_pBufferManager; |
348 | static LPCWSTR s_pOutputPath; |
349 | static unsigned long s_nextFileIndex; |
350 | static EventPipeFile *s_pFile; |
351 | static EventPipeEventSource *s_pEventSource; |
352 | static LPCWSTR s_pCommandLine; |
353 | const static DWORD FileSwitchTimerPeriodMS = 1000; |
354 | static HANDLE s_fileSwitchTimerHandle; |
355 | static ULONGLONG s_lastFileSwitchTime; |
356 | }; |
357 | |
358 | struct EventPipeProviderConfiguration |
359 | { |
360 | |
361 | private: |
362 | |
363 | LPCWSTR m_pProviderName; |
364 | UINT64 m_keywords; |
365 | UINT32 m_loggingLevel; |
366 | LPCWSTR m_pFilterData; |
367 | |
368 | public: |
369 | |
370 | EventPipeProviderConfiguration() |
371 | { |
372 | LIMITED_METHOD_CONTRACT; |
373 | m_pProviderName = NULL; |
374 | m_keywords = NULL; |
375 | m_loggingLevel = 0; |
376 | m_pFilterData = NULL; |
377 | } |
378 | |
379 | EventPipeProviderConfiguration( |
380 | LPCWSTR pProviderName, |
381 | UINT64 keywords, |
382 | UINT32 loggingLevel, |
383 | LPCWSTR pFilterData) |
384 | { |
385 | LIMITED_METHOD_CONTRACT; |
386 | m_pProviderName = pProviderName; |
387 | m_keywords = keywords; |
388 | m_loggingLevel = loggingLevel; |
389 | m_pFilterData = pFilterData; |
390 | } |
391 | |
392 | LPCWSTR GetProviderName() const |
393 | { |
394 | LIMITED_METHOD_CONTRACT; |
395 | return m_pProviderName; |
396 | } |
397 | |
398 | UINT64 GetKeywords() const |
399 | { |
400 | LIMITED_METHOD_CONTRACT; |
401 | return m_keywords; |
402 | } |
403 | |
404 | UINT32 GetLevel() const |
405 | { |
406 | LIMITED_METHOD_CONTRACT; |
407 | return m_loggingLevel; |
408 | } |
409 | |
410 | LPCWSTR GetFilterData() const |
411 | { |
412 | LIMITED_METHOD_CONTRACT; |
413 | return m_pFilterData; |
414 | } |
415 | }; |
416 | |
417 | class EventPipeInternal |
418 | { |
419 | private: |
420 | |
421 | enum class ActivityControlCode |
422 | { |
423 | EVENT_ACTIVITY_CONTROL_GET_ID = 1, |
424 | EVENT_ACTIVITY_CONTROL_SET_ID = 2, |
425 | EVENT_ACTIVITY_CONTROL_CREATE_ID = 3, |
426 | EVENT_ACTIVITY_CONTROL_GET_SET_ID = 4, |
427 | EVENT_ACTIVITY_CONTROL_CREATE_SET_ID = 5 |
428 | }; |
429 | |
430 | struct EventPipeEventInstanceData |
431 | { |
432 | public: |
433 | void *ProviderID; |
434 | unsigned int EventID; |
435 | unsigned int ThreadID; |
436 | LARGE_INTEGER TimeStamp; |
437 | GUID ActivityId; |
438 | GUID RelatedActivityId; |
439 | const BYTE *Payload; |
440 | unsigned int PayloadLength; |
441 | }; |
442 | |
443 | struct EventPipeSessionInfo |
444 | { |
445 | public: |
446 | FILETIME StartTimeAsUTCFileTime; |
447 | LARGE_INTEGER StartTimeStamp; |
448 | LARGE_INTEGER TimeStampFrequency; |
449 | }; |
450 | |
451 | public: |
452 | |
453 | static UINT64 QCALLTYPE Enable( |
454 | __in_z LPCWSTR outputFile, |
455 | UINT32 circularBufferSizeInMB, |
456 | INT64 profilerSamplingRateInNanoseconds, |
457 | EventPipeProviderConfiguration *pProviders, |
458 | INT32 numProviders, |
459 | UINT64 multiFileTraceLengthInSeconds); |
460 | |
461 | static void QCALLTYPE Disable(UINT64 sessionID); |
462 | |
463 | static bool QCALLTYPE GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo); |
464 | |
465 | static INT_PTR QCALLTYPE CreateProvider( |
466 | __in_z LPCWSTR providerName, |
467 | EventPipeCallback pCallbackFunc); |
468 | |
469 | static INT_PTR QCALLTYPE DefineEvent( |
470 | INT_PTR provHandle, |
471 | UINT32 eventID, |
472 | __int64 keywords, |
473 | UINT32 eventVersion, |
474 | UINT32 level, |
475 | void *pMetadata, |
476 | UINT32 metadataLength); |
477 | |
478 | static INT_PTR QCALLTYPE GetProvider( |
479 | __in_z LPCWSTR providerName); |
480 | |
481 | static void QCALLTYPE DeleteProvider( |
482 | INT_PTR provHandle); |
483 | |
484 | static int QCALLTYPE EventActivityIdControl( |
485 | uint controlCode, |
486 | GUID *pActivityId); |
487 | |
488 | static void QCALLTYPE WriteEvent( |
489 | INT_PTR eventHandle, |
490 | UINT32 eventID, |
491 | void *pData, |
492 | UINT32 length, |
493 | LPCGUID pActivityId, LPCGUID pRelatedActivityId); |
494 | |
495 | static void QCALLTYPE WriteEventData( |
496 | INT_PTR eventHandle, |
497 | UINT32 eventID, |
498 | EventData *pEventData, |
499 | UINT32 eventDataCount, |
500 | LPCGUID pActivityId, LPCGUID pRelatedActivityId); |
501 | |
502 | static bool QCALLTYPE GetNextEvent( |
503 | EventPipeEventInstanceData *pInstance); |
504 | }; |
505 | |
506 | #endif // FEATURE_PERFTRACING |
507 | |
508 | #endif // __EVENTPIPE_H__ |
509 | |