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 | #include "common.h" |
6 | #include "clrtypes.h" |
7 | #include "safemath.h" |
8 | #include "eventpipe.h" |
9 | #include "eventpipebuffermanager.h" |
10 | #include "eventpipeconfiguration.h" |
11 | #include "eventpipeevent.h" |
12 | #include "eventpipeeventsource.h" |
13 | #include "eventpipefile.h" |
14 | #include "eventpipeprovider.h" |
15 | #include "eventpipesession.h" |
16 | #include "eventpipejsonfile.h" |
17 | #include "eventtracebase.h" |
18 | #include "sampleprofiler.h" |
19 | #include "win32threadpool.h" |
20 | |
21 | #ifdef FEATURE_PAL |
22 | #include "pal.h" |
23 | #endif // FEATURE_PAL |
24 | |
25 | #ifdef FEATURE_PERFTRACING |
26 | |
27 | CrstStatic EventPipe::s_configCrst; |
28 | bool EventPipe::s_tracingInitialized = false; |
29 | EventPipeConfiguration* EventPipe::s_pConfig = NULL; |
30 | EventPipeSession* EventPipe::s_pSession = NULL; |
31 | EventPipeBufferManager* EventPipe::s_pBufferManager = NULL; |
32 | LPCWSTR EventPipe::s_pOutputPath = NULL; |
33 | EventPipeFile* EventPipe::s_pFile = NULL; |
34 | EventPipeEventSource* EventPipe::s_pEventSource = NULL; |
35 | LPCWSTR EventPipe::s_pCommandLine = NULL; |
36 | unsigned long EventPipe::s_nextFileIndex; |
37 | HANDLE EventPipe::s_fileSwitchTimerHandle = NULL; |
38 | ULONGLONG EventPipe::s_lastFileSwitchTime = 0; |
39 | |
40 | #ifdef FEATURE_PAL |
41 | // This function is auto-generated from /src/scripts/genEventPipe.py |
42 | extern "C" void InitProvidersAndEvents(); |
43 | #else |
44 | void InitProvidersAndEvents(); |
45 | #endif |
46 | |
47 | EventPipeEventPayload::EventPipeEventPayload(BYTE *pData, unsigned int length) |
48 | { |
49 | CONTRACTL |
50 | { |
51 | NOTHROW; |
52 | GC_NOTRIGGER; |
53 | MODE_ANY; |
54 | } |
55 | CONTRACTL_END; |
56 | |
57 | m_pData = pData; |
58 | m_pEventData = NULL; |
59 | m_eventDataCount = 0; |
60 | m_allocatedData = false; |
61 | |
62 | m_size = length; |
63 | } |
64 | |
65 | EventPipeEventPayload::EventPipeEventPayload(EventData *pEventData, unsigned int eventDataCount) |
66 | { |
67 | CONTRACTL |
68 | { |
69 | NOTHROW; |
70 | GC_NOTRIGGER; |
71 | MODE_ANY; |
72 | } |
73 | CONTRACTL_END; |
74 | |
75 | m_pData = NULL; |
76 | m_pEventData = pEventData; |
77 | m_eventDataCount = eventDataCount; |
78 | m_allocatedData = false; |
79 | |
80 | S_UINT32 tmp_size = S_UINT32(0); |
81 | for (unsigned int i=0; i<m_eventDataCount; i++) |
82 | { |
83 | tmp_size += S_UINT32(m_pEventData[i].Size); |
84 | } |
85 | |
86 | if (tmp_size.IsOverflow()) |
87 | { |
88 | // If there is an overflow, drop the data and create an empty payload |
89 | m_pEventData = NULL; |
90 | m_eventDataCount = 0; |
91 | m_size = 0; |
92 | } |
93 | else |
94 | { |
95 | m_size = tmp_size.Value(); |
96 | } |
97 | } |
98 | |
99 | EventPipeEventPayload::~EventPipeEventPayload() |
100 | { |
101 | CONTRACTL |
102 | { |
103 | NOTHROW; |
104 | GC_NOTRIGGER; |
105 | MODE_ANY; |
106 | } |
107 | CONTRACTL_END; |
108 | |
109 | if(m_allocatedData && m_pData != NULL) |
110 | { |
111 | delete[] m_pData; |
112 | m_pData = NULL; |
113 | } |
114 | } |
115 | |
116 | void EventPipeEventPayload::Flatten() |
117 | { |
118 | CONTRACTL |
119 | { |
120 | NOTHROW; |
121 | GC_NOTRIGGER; |
122 | MODE_ANY; |
123 | } |
124 | CONTRACTL_END; |
125 | |
126 | if(m_size > 0) |
127 | { |
128 | if (!IsFlattened()) |
129 | { |
130 | BYTE* tmp_pData = new (nothrow) BYTE[m_size]; |
131 | if (tmp_pData != NULL) |
132 | { |
133 | m_allocatedData = true; |
134 | CopyData(tmp_pData); |
135 | m_pData = tmp_pData; |
136 | } |
137 | } |
138 | } |
139 | } |
140 | |
141 | void EventPipeEventPayload::CopyData(BYTE *pDst) |
142 | { |
143 | CONTRACTL |
144 | { |
145 | NOTHROW; |
146 | GC_NOTRIGGER; |
147 | MODE_ANY; |
148 | } |
149 | CONTRACTL_END; |
150 | |
151 | if(m_size > 0) |
152 | { |
153 | if(IsFlattened()) |
154 | { |
155 | memcpy(pDst, m_pData, m_size); |
156 | } |
157 | |
158 | else if(m_pEventData != NULL) |
159 | { |
160 | unsigned int offset = 0; |
161 | for(unsigned int i=0; i<m_eventDataCount; i++) |
162 | { |
163 | memcpy(pDst + offset, (BYTE*) m_pEventData[i].Ptr, m_pEventData[i].Size); |
164 | offset += m_pEventData[i].Size; |
165 | } |
166 | } |
167 | } |
168 | } |
169 | |
170 | BYTE* EventPipeEventPayload::GetFlatData() |
171 | { |
172 | CONTRACTL |
173 | { |
174 | NOTHROW; |
175 | GC_NOTRIGGER; |
176 | MODE_ANY; |
177 | } |
178 | CONTRACTL_END; |
179 | |
180 | if (!IsFlattened()) |
181 | { |
182 | Flatten(); |
183 | } |
184 | return m_pData; |
185 | } |
186 | |
187 | void EventPipe::Initialize() |
188 | { |
189 | STANDARD_VM_CONTRACT; |
190 | |
191 | s_tracingInitialized = s_configCrst.InitNoThrow( |
192 | CrstEventPipe, |
193 | (CrstFlags)(CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN | CRST_HOST_BREAKABLE)); |
194 | |
195 | s_pConfig = new EventPipeConfiguration(); |
196 | s_pConfig->Initialize(); |
197 | |
198 | s_pBufferManager = new EventPipeBufferManager(); |
199 | |
200 | s_pEventSource = new EventPipeEventSource(); |
201 | |
202 | // This calls into auto-generated code to initialize the runtime providers |
203 | // and events so that the EventPipe configuration lock isn't taken at runtime |
204 | InitProvidersAndEvents(); |
205 | } |
206 | |
207 | void EventPipe::Shutdown() |
208 | { |
209 | CONTRACTL |
210 | { |
211 | NOTHROW; |
212 | GC_TRIGGERS; |
213 | MODE_ANY; |
214 | } |
215 | CONTRACTL_END; |
216 | |
217 | // Mark tracing as no longer initialized. |
218 | s_tracingInitialized = false; |
219 | |
220 | // We are shutting down, so if disabling EventPipe throws, we need to move along anyway. |
221 | EX_TRY |
222 | { |
223 | Disable((EventPipeSessionID)s_pSession); |
224 | } |
225 | EX_CATCH { } |
226 | EX_END_CATCH(SwallowAllExceptions); |
227 | |
228 | // Save pointers to the configuration and buffer manager. |
229 | EventPipeConfiguration *pConfig = s_pConfig; |
230 | EventPipeBufferManager *pBufferManager = s_pBufferManager; |
231 | |
232 | // Set the static pointers to NULL so that the rest of the EventPipe knows that they are no longer available. |
233 | // Flush process write buffers to make sure other threads can see the change. |
234 | s_pConfig = NULL; |
235 | s_pBufferManager = NULL; |
236 | FlushProcessWriteBuffers(); |
237 | |
238 | // Free resources. |
239 | delete(pConfig); |
240 | delete(pBufferManager); |
241 | delete(s_pEventSource); |
242 | s_pEventSource = NULL; |
243 | delete(s_pOutputPath); |
244 | s_pOutputPath = NULL; |
245 | |
246 | // On Windows, this is just a pointer to the return value from |
247 | // GetCommandLineW(), so don't attempt to free it. |
248 | #ifdef FEATURE_PAL |
249 | delete[](s_pCommandLine); |
250 | s_pCommandLine = NULL; |
251 | #endif |
252 | } |
253 | |
254 | EventPipeSessionID EventPipe::Enable( |
255 | LPCWSTR strOutputPath, |
256 | unsigned int circularBufferSizeInMB, |
257 | EventPipeProviderConfiguration *pProviders, |
258 | int numProviders, |
259 | UINT64 multiFileTraceLengthInSeconds) |
260 | { |
261 | CONTRACTL |
262 | { |
263 | THROWS; |
264 | GC_TRIGGERS; |
265 | MODE_ANY; |
266 | } |
267 | CONTRACTL_END; |
268 | |
269 | // Create a new session. |
270 | EventPipeSession *pSession = s_pConfig->CreateSession( |
271 | (strOutputPath != NULL) ? EventPipeSessionType::File : EventPipeSessionType::Streaming, |
272 | circularBufferSizeInMB, |
273 | pProviders, |
274 | static_cast<unsigned int>(numProviders), |
275 | multiFileTraceLengthInSeconds); |
276 | |
277 | // Enable the session. |
278 | return Enable(strOutputPath, pSession); |
279 | } |
280 | |
281 | EventPipeSessionID EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pSession) |
282 | { |
283 | CONTRACTL |
284 | { |
285 | THROWS; |
286 | GC_TRIGGERS; |
287 | MODE_ANY; |
288 | PRECONDITION(pSession != NULL); |
289 | } |
290 | CONTRACTL_END; |
291 | |
292 | // If tracing is not initialized or is already enabled, bail here. |
293 | if(!s_tracingInitialized || s_pConfig == NULL || s_pConfig->Enabled()) |
294 | { |
295 | return 0; |
296 | } |
297 | |
298 | // If the state or arguments are invalid, bail here. |
299 | if(pSession == NULL || !pSession->IsValid()) |
300 | { |
301 | return 0; |
302 | } |
303 | |
304 | // Enable the EventPipe EventSource. |
305 | s_pEventSource->Enable(pSession); |
306 | |
307 | // Take the lock before enabling tracing. |
308 | CrstHolder _crst(GetLock()); |
309 | |
310 | // Initialize the next file index. |
311 | s_nextFileIndex = 1; |
312 | |
313 | // Initialize the last file switch time. |
314 | s_lastFileSwitchTime = CLRGetTickCount64(); |
315 | |
316 | // Create the event pipe file. |
317 | // A NULL output path means that we should not write the results to a file. |
318 | // This is used in the EventListener streaming case. |
319 | if (strOutputPath != NULL) |
320 | { |
321 | // Save the output file path. |
322 | SString outputPath(strOutputPath); |
323 | SIZE_T outputPathLen = outputPath.GetCount(); |
324 | WCHAR *pOutputPath = new WCHAR[outputPathLen + 1]; |
325 | wcsncpy(pOutputPath, outputPath.GetUnicode(), outputPathLen); |
326 | pOutputPath[outputPathLen] = '\0'; |
327 | s_pOutputPath = pOutputPath; |
328 | |
329 | SString nextTraceFilePath; |
330 | GetNextFilePath(pSession, nextTraceFilePath); |
331 | |
332 | s_pFile = new EventPipeFile(nextTraceFilePath); |
333 | } |
334 | |
335 | // Save the session. |
336 | s_pSession = pSession; |
337 | |
338 | // Enable tracing. |
339 | s_pConfig->Enable(s_pSession); |
340 | |
341 | // Enable the sample profiler |
342 | SampleProfiler::Enable(); |
343 | |
344 | // Enable the file switch timer if needed. |
345 | if(s_pSession->GetMultiFileTraceLengthInSeconds() > 0) |
346 | { |
347 | CreateFileSwitchTimer(); |
348 | } |
349 | |
350 | // Return the session ID. |
351 | return (EventPipeSessionID)s_pSession; |
352 | } |
353 | |
354 | void EventPipe::Disable(EventPipeSessionID id) |
355 | { |
356 | CONTRACTL |
357 | { |
358 | THROWS; |
359 | GC_TRIGGERS; |
360 | MODE_ANY; |
361 | } |
362 | CONTRACTL_END; |
363 | |
364 | // Only perform the disable operation if the session ID |
365 | // matches the current active session. |
366 | if(id != (EventPipeSessionID)s_pSession) |
367 | { |
368 | return; |
369 | } |
370 | |
371 | // Don't block GC during clean-up. |
372 | GCX_PREEMP(); |
373 | |
374 | // Take the lock before disabling tracing. |
375 | CrstHolder _crst(GetLock()); |
376 | |
377 | if(s_pConfig != NULL && s_pConfig->Enabled()) |
378 | { |
379 | // Disable the profiler. |
380 | SampleProfiler::Disable(); |
381 | |
382 | // Log the process information event. |
383 | s_pEventSource->SendProcessInfo(s_pCommandLine); |
384 | |
385 | // Log the runtime information event. |
386 | ETW::InfoLog::RuntimeInformation(ETW::InfoLog::InfoStructs::Normal); |
387 | |
388 | // Disable tracing. |
389 | s_pConfig->Disable(s_pSession); |
390 | |
391 | // Delete the session. |
392 | s_pConfig->DeleteSession(s_pSession); |
393 | s_pSession = NULL; |
394 | |
395 | // Delete the file switch timer. |
396 | DeleteFileSwitchTimer(); |
397 | |
398 | // Flush all write buffers to make sure that all threads see the change. |
399 | FlushProcessWriteBuffers(); |
400 | |
401 | // Write to the file. |
402 | if(s_pFile != NULL) |
403 | { |
404 | LARGE_INTEGER disableTimeStamp; |
405 | QueryPerformanceCounter(&disableTimeStamp); |
406 | s_pBufferManager->WriteAllBuffersToFile(s_pFile, disableTimeStamp); |
407 | |
408 | if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0) |
409 | { |
410 | // Before closing the file, do rundown. |
411 | const unsigned int numRundownProviders = 2; |
412 | EventPipeProviderConfiguration rundownProviders[] = |
413 | { |
414 | { W("Microsoft-Windows-DotNETRuntime" ), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL }, // Public provider. |
415 | { W("Microsoft-Windows-DotNETRuntimeRundown" ), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL } // Rundown provider. |
416 | }; |
417 | // The circular buffer size doesn't matter because all events are written synchronously during rundown. |
418 | s_pSession = s_pConfig->CreateSession(EventPipeSessionType::File, 1 /* circularBufferSizeInMB */, rundownProviders, numRundownProviders); |
419 | s_pConfig->EnableRundown(s_pSession); |
420 | |
421 | // Ask the runtime to emit rundown events. |
422 | if(g_fEEStarted && !g_fEEShutDown) |
423 | { |
424 | ETW::EnumerationLog::EndRundown(); |
425 | } |
426 | |
427 | // Disable the event pipe now that rundown is complete. |
428 | s_pConfig->Disable(s_pSession); |
429 | |
430 | // Delete the rundown session. |
431 | s_pConfig->DeleteSession(s_pSession); |
432 | s_pSession = NULL; |
433 | } |
434 | |
435 | delete(s_pFile); |
436 | s_pFile = NULL; |
437 | } |
438 | |
439 | // De-allocate buffers. |
440 | s_pBufferManager->DeAllocateBuffers(); |
441 | |
442 | // Delete deferred providers. |
443 | // Providers can't be deleted during tracing because they may be needed when serializing the file. |
444 | s_pConfig->DeleteDeferredProviders(); |
445 | } |
446 | } |
447 | |
448 | void EventPipe::CreateFileSwitchTimer() |
449 | { |
450 | CONTRACTL |
451 | { |
452 | THROWS; |
453 | GC_TRIGGERS; |
454 | MODE_ANY; |
455 | PRECONDITION(GetLock()->OwnedByCurrentThread()); |
456 | } |
457 | CONTRACTL_END |
458 | |
459 | NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new(nothrow) ThreadpoolMgr::TimerInfoContext(); |
460 | if (timerContextHolder == NULL) |
461 | { |
462 | return; |
463 | } |
464 | timerContextHolder->TimerId = 0; |
465 | |
466 | bool success = false; |
467 | _ASSERTE(s_fileSwitchTimerHandle == NULL); |
468 | EX_TRY |
469 | { |
470 | if (ThreadpoolMgr::CreateTimerQueueTimer( |
471 | &s_fileSwitchTimerHandle, |
472 | SwitchToNextFileTimerCallback, |
473 | timerContextHolder, |
474 | FileSwitchTimerPeriodMS, |
475 | FileSwitchTimerPeriodMS, |
476 | 0 /* flags */)) |
477 | { |
478 | _ASSERTE(s_fileSwitchTimerHandle != NULL); |
479 | success = true; |
480 | } |
481 | } |
482 | EX_CATCH |
483 | { |
484 | } |
485 | EX_END_CATCH(RethrowTerminalExceptions); |
486 | if (!success) |
487 | { |
488 | _ASSERTE(s_fileSwitchTimerHandle == NULL); |
489 | return; |
490 | } |
491 | |
492 | timerContextHolder.SuppressRelease(); // the timer context is automatically deleted by the timer infrastructure |
493 | } |
494 | |
495 | void EventPipe::DeleteFileSwitchTimer() |
496 | { |
497 | CONTRACTL |
498 | { |
499 | THROWS; |
500 | GC_TRIGGERS; |
501 | MODE_ANY; |
502 | PRECONDITION(GetLock()->OwnedByCurrentThread()); |
503 | } |
504 | CONTRACTL_END |
505 | |
506 | if((s_fileSwitchTimerHandle != NULL) && (ThreadpoolMgr::DeleteTimerQueueTimer(s_fileSwitchTimerHandle, NULL))) |
507 | { |
508 | s_fileSwitchTimerHandle = NULL; |
509 | } |
510 | } |
511 | |
512 | void WINAPI EventPipe::SwitchToNextFileTimerCallback(PVOID parameter, BOOLEAN timerFired) |
513 | { |
514 | CONTRACTL |
515 | { |
516 | THROWS; |
517 | GC_TRIGGERS; |
518 | MODE_ANY; |
519 | PRECONDITION(timerFired); |
520 | } |
521 | CONTRACTL_END; |
522 | |
523 | |
524 | // Take the lock control lock to make sure that tracing isn't disabled during this operation. |
525 | CrstHolder _crst(GetLock()); |
526 | |
527 | // Make sure that we should actually switch files. |
528 | UINT64 multiFileTraceLengthInSeconds = s_pSession->GetMultiFileTraceLengthInSeconds(); |
529 | if(!Enabled() || s_pSession->GetSessionType() != EventPipeSessionType::File || multiFileTraceLengthInSeconds == 0) |
530 | { |
531 | return; |
532 | } |
533 | |
534 | GCX_PREEMP(); |
535 | |
536 | if(CLRGetTickCount64() > (s_lastFileSwitchTime + (multiFileTraceLengthInSeconds * 1000))) |
537 | { |
538 | SwitchToNextFile(); |
539 | s_lastFileSwitchTime = CLRGetTickCount64(); |
540 | } |
541 | |
542 | } |
543 | |
544 | void EventPipe::SwitchToNextFile() |
545 | { |
546 | CONTRACTL |
547 | { |
548 | THROWS; |
549 | GC_TRIGGERS; |
550 | MODE_PREEMPTIVE; |
551 | PRECONDITION(s_pSession != NULL); |
552 | PRECONDITION(GetLock()->OwnedByCurrentThread()); |
553 | } |
554 | CONTRACTL_END |
555 | |
556 | // Get the current time stamp. |
557 | // WriteAllBuffersToFile will use this to ensure that no events after the current timestamp are written into the file. |
558 | LARGE_INTEGER stopTimeStamp; |
559 | QueryPerformanceCounter(&stopTimeStamp); |
560 | s_pBufferManager->WriteAllBuffersToFile(s_pFile, stopTimeStamp); |
561 | |
562 | // Open the new file. |
563 | SString nextTraceFilePath; |
564 | GetNextFilePath(s_pSession, nextTraceFilePath); |
565 | EventPipeFile* pFile = new (nothrow) EventPipeFile(nextTraceFilePath); |
566 | if(pFile == NULL) |
567 | { |
568 | return; |
569 | } |
570 | |
571 | // Close the previous file. |
572 | delete(s_pFile); |
573 | |
574 | // Swap in the new file. |
575 | s_pFile = pFile; |
576 | } |
577 | |
578 | void EventPipe::GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFilePath) |
579 | { |
580 | CONTRACTL |
581 | { |
582 | THROWS; |
583 | GC_TRIGGERS; |
584 | MODE_ANY; |
585 | PRECONDITION(pSession != NULL); |
586 | PRECONDITION(GetLock()->OwnedByCurrentThread()); |
587 | } |
588 | CONTRACTL_END; |
589 | |
590 | // Set the full path to the requested trace file as the next file path. |
591 | nextTraceFilePath.Set(s_pOutputPath); |
592 | |
593 | // If multiple files have been requested, then add a sequence number to the trace file name. |
594 | UINT64 multiFileTraceLengthInSeconds = pSession->GetMultiFileTraceLengthInSeconds(); |
595 | if(multiFileTraceLengthInSeconds > 0) |
596 | { |
597 | // Remove the ".netperf" file extension if it exists. |
598 | SString::Iterator netPerfExtension = nextTraceFilePath.End(); |
599 | if(nextTraceFilePath.FindBack(netPerfExtension, W(".netperf" ))) |
600 | { |
601 | nextTraceFilePath.Truncate(netPerfExtension); |
602 | } |
603 | |
604 | // Add the sequence number and the ".netperf" file extension. |
605 | WCHAR strNextIndex[21]; |
606 | swprintf_s(strNextIndex, 21, W(".%u.netperf" ), s_nextFileIndex++); |
607 | nextTraceFilePath.Append(strNextIndex); |
608 | } |
609 | } |
610 | |
611 | EventPipeSession* EventPipe::GetSession(EventPipeSessionID id) |
612 | { |
613 | LIMITED_METHOD_CONTRACT; |
614 | |
615 | EventPipeSession *pSession = NULL; |
616 | if((EventPipeSessionID)s_pSession == id) |
617 | { |
618 | pSession = s_pSession; |
619 | } |
620 | return pSession; |
621 | } |
622 | |
623 | bool EventPipe::Enabled() |
624 | { |
625 | LIMITED_METHOD_CONTRACT; |
626 | |
627 | bool enabled = false; |
628 | if(s_pConfig != NULL) |
629 | { |
630 | enabled = s_pConfig->Enabled(); |
631 | } |
632 | |
633 | return enabled; |
634 | } |
635 | |
636 | EventPipeProvider* EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData) |
637 | { |
638 | CONTRACTL |
639 | { |
640 | THROWS; |
641 | GC_TRIGGERS; |
642 | MODE_ANY; |
643 | } |
644 | CONTRACTL_END; |
645 | |
646 | EventPipeProvider *pProvider = NULL; |
647 | if (s_pConfig != NULL) |
648 | { |
649 | pProvider = s_pConfig->CreateProvider(providerName, pCallbackFunction, pCallbackData); |
650 | } |
651 | |
652 | return pProvider; |
653 | |
654 | } |
655 | |
656 | EventPipeProvider* EventPipe::GetProvider(const SString &providerName) |
657 | { |
658 | CONTRACTL |
659 | { |
660 | THROWS; |
661 | GC_NOTRIGGER; |
662 | MODE_ANY; |
663 | } |
664 | CONTRACTL_END; |
665 | |
666 | EventPipeProvider *pProvider = NULL; |
667 | if (s_pConfig != NULL) |
668 | { |
669 | pProvider = s_pConfig->GetProvider(providerName); |
670 | } |
671 | |
672 | return pProvider; |
673 | } |
674 | |
675 | void EventPipe::DeleteProvider(EventPipeProvider *pProvider) |
676 | { |
677 | CONTRACTL |
678 | { |
679 | THROWS; |
680 | GC_TRIGGERS; |
681 | MODE_ANY; |
682 | } |
683 | CONTRACTL_END; |
684 | |
685 | // Take the lock to make sure that we don't have a race |
686 | // between disabling tracing and deleting a provider |
687 | // where we hold a provider after tracing has been disabled. |
688 | CrstHolder _crst(GetLock()); |
689 | |
690 | if(pProvider != NULL) |
691 | { |
692 | if(Enabled()) |
693 | { |
694 | // Save the provider until the end of the tracing session. |
695 | pProvider->SetDeleteDeferred(); |
696 | } |
697 | else |
698 | { |
699 | // Delete the provider now. |
700 | if (s_pConfig != NULL) |
701 | { |
702 | s_pConfig->DeleteProvider(pProvider); |
703 | } |
704 | } |
705 | } |
706 | } |
707 | |
708 | void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId, LPCGUID pRelatedActivityId) |
709 | { |
710 | CONTRACTL |
711 | { |
712 | NOTHROW; |
713 | GC_NOTRIGGER; |
714 | MODE_ANY; |
715 | } |
716 | CONTRACTL_END; |
717 | |
718 | EventPipeEventPayload payload(pData, length); |
719 | EventPipe::WriteEventInternal(event, payload, pActivityId, pRelatedActivityId); |
720 | } |
721 | |
722 | void EventPipe::WriteEvent(EventPipeEvent &event, EventData *pEventData, unsigned int eventDataCount, LPCGUID pActivityId, LPCGUID pRelatedActivityId) |
723 | { |
724 | CONTRACTL |
725 | { |
726 | NOTHROW; |
727 | GC_NOTRIGGER; |
728 | MODE_ANY; |
729 | } |
730 | CONTRACTL_END; |
731 | |
732 | EventPipeEventPayload payload(pEventData, eventDataCount); |
733 | EventPipe::WriteEventInternal(event, payload, pActivityId, pRelatedActivityId); |
734 | } |
735 | |
736 | void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId) |
737 | { |
738 | CONTRACTL |
739 | { |
740 | NOTHROW; |
741 | GC_NOTRIGGER; |
742 | MODE_ANY; |
743 | } |
744 | CONTRACTL_END; |
745 | |
746 | // Exit early if the event is not enabled. |
747 | if(!event.IsEnabled()) |
748 | { |
749 | return; |
750 | } |
751 | |
752 | // Get the current thread; |
753 | Thread *pThread = GetThread(); |
754 | if(pThread == NULL) |
755 | { |
756 | // We can't write an event without the thread object. |
757 | return; |
758 | } |
759 | |
760 | if(s_pConfig == NULL) |
761 | { |
762 | // We can't procede without a configuration |
763 | return; |
764 | } |
765 | _ASSERTE(s_pSession != NULL); |
766 | |
767 | // If the activity id isn't specified, pull it from the current thread. |
768 | if(pActivityId == NULL) |
769 | { |
770 | pActivityId = pThread->GetActivityId(); |
771 | } |
772 | |
773 | if(!s_pConfig->RundownEnabled() && s_pBufferManager != NULL) |
774 | { |
775 | s_pBufferManager->WriteEvent(pThread, *s_pSession, event, payload, pActivityId, pRelatedActivityId); |
776 | } |
777 | else if(s_pConfig->RundownEnabled()) |
778 | { |
779 | // It is possible that some events that are enabled on rundown can be emitted from other threads. |
780 | // We're not interested in these events and they can cause corrupted trace files because rundown |
781 | // events are written synchronously and not under lock. |
782 | // If we encounter an event that did not originate on the thread that is doing rundown, ignore it. |
783 | if(!s_pConfig->IsRundownThread(pThread)) |
784 | { |
785 | return; |
786 | } |
787 | |
788 | BYTE *pData = payload.GetFlatData(); |
789 | if (pData != NULL) |
790 | { |
791 | // Write synchronously to the file. |
792 | // We're under lock and blocking the disabling thread. |
793 | // This copy occurs here (rather than at file write) because |
794 | // A) The FastSerializer API would need to change if we waited |
795 | // B) It is unclear there is a benefit to multiple file write calls |
796 | // as opposed a a buffer copy here |
797 | EventPipeEventInstance instance( |
798 | *s_pSession, |
799 | event, |
800 | pThread->GetOSThreadId(), |
801 | pData, |
802 | payload.GetSize(), |
803 | pActivityId, |
804 | pRelatedActivityId); |
805 | |
806 | if(s_pFile != NULL) |
807 | { |
808 | // EventPipeFile::WriteEvent needs to allocate a metadata event |
809 | // and can therefore throw. In this context we will silently |
810 | // fail rather than disrupt the caller |
811 | EX_TRY |
812 | { |
813 | s_pFile->WriteEvent(instance); |
814 | } |
815 | EX_CATCH { } |
816 | EX_END_CATCH(SwallowAllExceptions); |
817 | } |
818 | } |
819 | } |
820 | } |
821 | |
822 | void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData, unsigned int length) |
823 | { |
824 | CONTRACTL |
825 | { |
826 | NOTHROW; |
827 | GC_TRIGGERS; |
828 | MODE_PREEMPTIVE; |
829 | } |
830 | CONTRACTL_END; |
831 | |
832 | EventPipeEventPayload payload(pData, length); |
833 | |
834 | // Write the event to the thread's buffer. |
835 | if(s_pBufferManager != NULL) |
836 | { |
837 | // Specify the sampling thread as the "current thread", so that we select the right buffer. |
838 | // Specify the target thread so that the event gets properly attributed. |
839 | s_pBufferManager->WriteEvent(pSamplingThread, *s_pSession, *pEvent, payload, NULL /* pActivityId */, NULL /* pRelatedActivityId */, pTargetThread, &stackContents); |
840 | } |
841 | } |
842 | |
843 | bool EventPipe::WalkManagedStackForCurrentThread(StackContents &stackContents) |
844 | { |
845 | CONTRACTL |
846 | { |
847 | NOTHROW; |
848 | GC_NOTRIGGER; |
849 | MODE_ANY; |
850 | } |
851 | CONTRACTL_END; |
852 | |
853 | Thread *pThread = GetThread(); |
854 | if(pThread != NULL) |
855 | { |
856 | return WalkManagedStackForThread(pThread, stackContents); |
857 | } |
858 | |
859 | return false; |
860 | } |
861 | |
862 | bool EventPipe::WalkManagedStackForThread(Thread *pThread, StackContents &stackContents) |
863 | { |
864 | CONTRACTL |
865 | { |
866 | NOTHROW; |
867 | GC_NOTRIGGER; |
868 | MODE_ANY; |
869 | PRECONDITION(pThread != NULL); |
870 | } |
871 | CONTRACTL_END; |
872 | |
873 | // Calling into StackWalkFrames in preemptive mode violates the host contract, |
874 | // but this contract is not used on CoreCLR. |
875 | CONTRACT_VIOLATION( HostViolation ); |
876 | |
877 | stackContents.Reset(); |
878 | |
879 | StackWalkAction swaRet = pThread->StackWalkFrames( |
880 | (PSTACKWALKFRAMESCALLBACK) &StackWalkCallback, |
881 | &stackContents, |
882 | ALLOW_ASYNC_STACK_WALK | FUNCTIONSONLY | HANDLESKIPPEDFRAMES | ALLOW_INVALID_OBJECTS); |
883 | |
884 | return ((swaRet == SWA_DONE) || (swaRet == SWA_CONTINUE)); |
885 | } |
886 | |
887 | StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pData) |
888 | { |
889 | CONTRACTL |
890 | { |
891 | NOTHROW; |
892 | GC_NOTRIGGER; |
893 | MODE_ANY; |
894 | PRECONDITION(pCf != NULL); |
895 | PRECONDITION(pData != NULL); |
896 | } |
897 | CONTRACTL_END; |
898 | |
899 | // Get the IP. |
900 | UINT_PTR controlPC = (UINT_PTR)pCf->GetRegisterSet()->ControlPC; |
901 | if(controlPC == 0) |
902 | { |
903 | if(pData->GetLength() == 0) |
904 | { |
905 | // This happens for pinvoke stubs on the top of the stack. |
906 | return SWA_CONTINUE; |
907 | } |
908 | } |
909 | |
910 | _ASSERTE(controlPC != 0); |
911 | |
912 | // Add the IP to the captured stack. |
913 | pData->Append( |
914 | controlPC, |
915 | pCf->GetFunction() |
916 | ); |
917 | |
918 | // Continue the stack walk. |
919 | return SWA_CONTINUE; |
920 | } |
921 | |
922 | EventPipeConfiguration* EventPipe::GetConfiguration() |
923 | { |
924 | LIMITED_METHOD_CONTRACT; |
925 | |
926 | return s_pConfig; |
927 | } |
928 | |
929 | CrstStatic* EventPipe::GetLock() |
930 | { |
931 | LIMITED_METHOD_CONTRACT; |
932 | |
933 | return &s_configCrst; |
934 | } |
935 | |
936 | void EventPipe::SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv) |
937 | { |
938 | CONTRACTL |
939 | { |
940 | THROWS; |
941 | GC_TRIGGERS; |
942 | MODE_COOPERATIVE; |
943 | PRECONDITION(pwzAssemblyPath != NULL); |
944 | PRECONDITION(argc <= 0 || argv != NULL); |
945 | } |
946 | CONTRACTL_END; |
947 | |
948 | // Get the command line. |
949 | LPCWSTR osCommandLine = GetCommandLineW(); |
950 | |
951 | #ifndef FEATURE_PAL |
952 | // On Windows, osCommandLine contains the executable and all arguments. |
953 | s_pCommandLine = osCommandLine; |
954 | #else |
955 | // On UNIX, the PAL doesn't have the command line arguments, so we must build the command line. |
956 | // osCommandLine contains the full path to the executable. |
957 | SString commandLine(osCommandLine); |
958 | commandLine.Append((WCHAR)' '); |
959 | commandLine.Append(pwzAssemblyPath); |
960 | |
961 | for(int i=0; i<argc; i++) |
962 | { |
963 | commandLine.Append((WCHAR)' '); |
964 | commandLine.Append(argv[i]); |
965 | } |
966 | |
967 | // Allocate a new string for the command line. |
968 | SIZE_T commandLineLen = commandLine.GetCount(); |
969 | WCHAR *pCommandLine = new WCHAR[commandLineLen + 1]; |
970 | wcsncpy(pCommandLine, commandLine.GetUnicode(), commandLineLen); |
971 | pCommandLine[commandLineLen] = '\0'; |
972 | |
973 | s_pCommandLine = pCommandLine; |
974 | #endif |
975 | } |
976 | |
977 | EventPipeEventInstance* EventPipe::GetNextEvent() |
978 | { |
979 | CONTRACTL |
980 | { |
981 | THROWS; |
982 | GC_TRIGGERS; |
983 | MODE_PREEMPTIVE; |
984 | } |
985 | CONTRACTL_END; |
986 | |
987 | EventPipeEventInstance *pInstance = NULL; |
988 | |
989 | // Only fetch the next event if a tracing session exists. |
990 | // The buffer manager is not disposed until the process is shutdown. |
991 | if (s_pSession != NULL) |
992 | { |
993 | pInstance = s_pBufferManager->GetNextEvent(); |
994 | } |
995 | |
996 | return pInstance; |
997 | } |
998 | |
999 | UINT64 QCALLTYPE EventPipeInternal::Enable( |
1000 | __in_z LPCWSTR outputFile, |
1001 | UINT32 circularBufferSizeInMB, |
1002 | INT64 profilerSamplingRateInNanoseconds, |
1003 | EventPipeProviderConfiguration *pProviders, |
1004 | INT32 numProviders, |
1005 | UINT64 multiFileTraceLengthInSeconds) |
1006 | { |
1007 | QCALL_CONTRACT; |
1008 | |
1009 | UINT64 sessionID = 0; |
1010 | |
1011 | BEGIN_QCALL; |
1012 | SampleProfiler::SetSamplingRate((unsigned long)profilerSamplingRateInNanoseconds); |
1013 | sessionID = EventPipe::Enable(outputFile, circularBufferSizeInMB, pProviders, numProviders, multiFileTraceLengthInSeconds); |
1014 | END_QCALL; |
1015 | |
1016 | return sessionID; |
1017 | } |
1018 | |
1019 | void QCALLTYPE EventPipeInternal::Disable(UINT64 sessionID) |
1020 | { |
1021 | QCALL_CONTRACT; |
1022 | |
1023 | BEGIN_QCALL; |
1024 | EventPipe::Disable(sessionID); |
1025 | END_QCALL; |
1026 | } |
1027 | |
1028 | bool QCALLTYPE EventPipeInternal::GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo) |
1029 | { |
1030 | QCALL_CONTRACT; |
1031 | |
1032 | bool retVal = false; |
1033 | BEGIN_QCALL; |
1034 | |
1035 | if(pSessionInfo != NULL) |
1036 | { |
1037 | EventPipeSession *pSession = EventPipe::GetSession(sessionID); |
1038 | if(pSession != NULL) |
1039 | { |
1040 | pSessionInfo->StartTimeAsUTCFileTime = pSession->GetStartTime(); |
1041 | pSessionInfo->StartTimeStamp.QuadPart = pSession->GetStartTimeStamp().QuadPart; |
1042 | QueryPerformanceFrequency(&pSessionInfo->TimeStampFrequency); |
1043 | retVal = true; |
1044 | } |
1045 | } |
1046 | |
1047 | END_QCALL; |
1048 | return retVal; |
1049 | } |
1050 | |
1051 | INT_PTR QCALLTYPE EventPipeInternal::CreateProvider( |
1052 | __in_z LPCWSTR providerName, |
1053 | EventPipeCallback pCallbackFunc) |
1054 | { |
1055 | QCALL_CONTRACT; |
1056 | |
1057 | EventPipeProvider *pProvider = NULL; |
1058 | |
1059 | BEGIN_QCALL; |
1060 | |
1061 | pProvider = EventPipe::CreateProvider(providerName, pCallbackFunc, NULL); |
1062 | |
1063 | END_QCALL; |
1064 | |
1065 | return reinterpret_cast<INT_PTR>(pProvider); |
1066 | } |
1067 | |
1068 | INT_PTR QCALLTYPE EventPipeInternal::DefineEvent( |
1069 | INT_PTR provHandle, |
1070 | UINT32 eventID, |
1071 | __int64 keywords, |
1072 | UINT32 eventVersion, |
1073 | UINT32 level, |
1074 | void *pMetadata, |
1075 | UINT32 metadataLength) |
1076 | { |
1077 | QCALL_CONTRACT; |
1078 | |
1079 | EventPipeEvent *pEvent = NULL; |
1080 | |
1081 | BEGIN_QCALL; |
1082 | |
1083 | _ASSERTE(provHandle != NULL); |
1084 | EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider *>(provHandle); |
1085 | pEvent = pProvider->AddEvent(eventID, keywords, eventVersion, (EventPipeEventLevel)level, (BYTE *)pMetadata, metadataLength); |
1086 | _ASSERTE(pEvent != NULL); |
1087 | |
1088 | END_QCALL; |
1089 | |
1090 | return reinterpret_cast<INT_PTR>(pEvent); |
1091 | } |
1092 | |
1093 | INT_PTR QCALLTYPE EventPipeInternal::GetProvider( |
1094 | __in_z LPCWSTR providerName) |
1095 | { |
1096 | QCALL_CONTRACT; |
1097 | |
1098 | EventPipeProvider *pProvider = NULL; |
1099 | |
1100 | BEGIN_QCALL; |
1101 | |
1102 | pProvider = EventPipe::GetProvider(providerName); |
1103 | |
1104 | END_QCALL; |
1105 | |
1106 | return reinterpret_cast<INT_PTR>(pProvider); |
1107 | } |
1108 | |
1109 | void QCALLTYPE EventPipeInternal::DeleteProvider( |
1110 | INT_PTR provHandle) |
1111 | { |
1112 | QCALL_CONTRACT; |
1113 | BEGIN_QCALL; |
1114 | |
1115 | if(provHandle != NULL) |
1116 | { |
1117 | EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider*>(provHandle); |
1118 | EventPipe::DeleteProvider(pProvider); |
1119 | } |
1120 | |
1121 | END_QCALL; |
1122 | } |
1123 | |
1124 | int QCALLTYPE EventPipeInternal::EventActivityIdControl( |
1125 | uint controlCode, |
1126 | GUID *pActivityId) |
1127 | { |
1128 | |
1129 | QCALL_CONTRACT; |
1130 | |
1131 | int retVal = 0; |
1132 | |
1133 | BEGIN_QCALL; |
1134 | |
1135 | Thread *pThread = GetThread(); |
1136 | if(pThread == NULL || pActivityId == NULL) |
1137 | { |
1138 | retVal = 1; |
1139 | } |
1140 | else |
1141 | { |
1142 | ActivityControlCode activityControlCode = (ActivityControlCode)controlCode; |
1143 | GUID currentActivityId; |
1144 | switch(activityControlCode) |
1145 | { |
1146 | case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_ID: |
1147 | |
1148 | *pActivityId = *pThread->GetActivityId(); |
1149 | break; |
1150 | |
1151 | case ActivityControlCode::EVENT_ACTIVITY_CONTROL_SET_ID: |
1152 | |
1153 | pThread->SetActivityId(pActivityId); |
1154 | break; |
1155 | |
1156 | case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_ID: |
1157 | |
1158 | CoCreateGuid(pActivityId); |
1159 | break; |
1160 | |
1161 | case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_SET_ID: |
1162 | |
1163 | currentActivityId = *pThread->GetActivityId(); |
1164 | pThread->SetActivityId(pActivityId); |
1165 | *pActivityId = currentActivityId; |
1166 | |
1167 | break; |
1168 | |
1169 | case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_SET_ID: |
1170 | |
1171 | *pActivityId = *pThread->GetActivityId(); |
1172 | CoCreateGuid(¤tActivityId); |
1173 | pThread->SetActivityId(¤tActivityId); |
1174 | break; |
1175 | |
1176 | default: |
1177 | retVal = 1; |
1178 | }; |
1179 | } |
1180 | |
1181 | END_QCALL; |
1182 | return retVal; |
1183 | } |
1184 | |
1185 | void QCALLTYPE EventPipeInternal::WriteEvent( |
1186 | INT_PTR eventHandle, |
1187 | UINT32 eventID, |
1188 | void *pData, |
1189 | UINT32 length, |
1190 | LPCGUID pActivityId, |
1191 | LPCGUID pRelatedActivityId) |
1192 | { |
1193 | QCALL_CONTRACT; |
1194 | BEGIN_QCALL; |
1195 | |
1196 | _ASSERTE(eventHandle != NULL); |
1197 | EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle); |
1198 | EventPipe::WriteEvent(*pEvent, (BYTE *)pData, length, pActivityId, pRelatedActivityId); |
1199 | |
1200 | END_QCALL; |
1201 | } |
1202 | |
1203 | void QCALLTYPE EventPipeInternal::WriteEventData( |
1204 | INT_PTR eventHandle, |
1205 | UINT32 eventID, |
1206 | EventData *pEventData, |
1207 | UINT32 eventDataCount, |
1208 | LPCGUID pActivityId, |
1209 | LPCGUID pRelatedActivityId) |
1210 | { |
1211 | QCALL_CONTRACT; |
1212 | BEGIN_QCALL; |
1213 | |
1214 | _ASSERTE(eventHandle != NULL); |
1215 | EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle); |
1216 | EventPipe::WriteEvent(*pEvent, pEventData, eventDataCount, pActivityId, pRelatedActivityId); |
1217 | |
1218 | END_QCALL; |
1219 | } |
1220 | |
1221 | bool QCALLTYPE EventPipeInternal::GetNextEvent( |
1222 | EventPipeEventInstanceData *pInstance) |
1223 | { |
1224 | QCALL_CONTRACT; |
1225 | |
1226 | EventPipeEventInstance *pNextInstance = NULL; |
1227 | BEGIN_QCALL; |
1228 | |
1229 | _ASSERTE(pInstance != NULL); |
1230 | |
1231 | pNextInstance = EventPipe::GetNextEvent(); |
1232 | if (pNextInstance) |
1233 | { |
1234 | pInstance->ProviderID = pNextInstance->GetEvent()->GetProvider(); |
1235 | pInstance->EventID = pNextInstance->GetEvent()->GetEventID(); |
1236 | pInstance->ThreadID = pNextInstance->GetThreadId(); |
1237 | pInstance->TimeStamp.QuadPart = pNextInstance->GetTimeStamp()->QuadPart; |
1238 | pInstance->ActivityId = *pNextInstance->GetActivityId(); |
1239 | pInstance->RelatedActivityId = *pNextInstance->GetRelatedActivityId(); |
1240 | pInstance->Payload = pNextInstance->GetData(); |
1241 | pInstance->PayloadLength = pNextInstance->GetDataLength(); |
1242 | } |
1243 | |
1244 | END_QCALL; |
1245 | return pNextInstance != NULL; |
1246 | } |
1247 | |
1248 | #endif // FEATURE_PERFTRACING |
1249 | |