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
27CrstStatic EventPipe::s_configCrst;
28bool EventPipe::s_tracingInitialized = false;
29EventPipeConfiguration* EventPipe::s_pConfig = NULL;
30EventPipeSession* EventPipe::s_pSession = NULL;
31EventPipeBufferManager* EventPipe::s_pBufferManager = NULL;
32LPCWSTR EventPipe::s_pOutputPath = NULL;
33EventPipeFile* EventPipe::s_pFile = NULL;
34EventPipeEventSource* EventPipe::s_pEventSource = NULL;
35LPCWSTR EventPipe::s_pCommandLine = NULL;
36unsigned long EventPipe::s_nextFileIndex;
37HANDLE EventPipe::s_fileSwitchTimerHandle = NULL;
38ULONGLONG EventPipe::s_lastFileSwitchTime = 0;
39
40#ifdef FEATURE_PAL
41// This function is auto-generated from /src/scripts/genEventPipe.py
42extern "C" void InitProvidersAndEvents();
43#else
44void InitProvidersAndEvents();
45#endif
46
47EventPipeEventPayload::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
65EventPipeEventPayload::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
99EventPipeEventPayload::~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
116void 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
141void 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
170BYTE* 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
187void 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
207void 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
254EventPipeSessionID 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
281EventPipeSessionID 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
354void 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
448void 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
495void 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
512void 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
544void 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
578void 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
611EventPipeSession* 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
623bool 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
636EventPipeProvider* 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
656EventPipeProvider* 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
675void 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
708void 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
722void 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
736void 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
822void 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
843bool 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
862bool 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
887StackWalkAction 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
922EventPipeConfiguration* EventPipe::GetConfiguration()
923{
924 LIMITED_METHOD_CONTRACT;
925
926 return s_pConfig;
927}
928
929CrstStatic* EventPipe::GetLock()
930{
931 LIMITED_METHOD_CONTRACT;
932
933 return &s_configCrst;
934}
935
936void 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
977EventPipeEventInstance* 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
999UINT64 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
1019void QCALLTYPE EventPipeInternal::Disable(UINT64 sessionID)
1020{
1021 QCALL_CONTRACT;
1022
1023 BEGIN_QCALL;
1024 EventPipe::Disable(sessionID);
1025 END_QCALL;
1026}
1027
1028bool 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
1051INT_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
1068INT_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
1093INT_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
1109void 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
1124int 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(&currentActivityId);
1173 pThread->SetActivityId(&currentActivityId);
1174 break;
1175
1176 default:
1177 retVal = 1;
1178 };
1179 }
1180
1181 END_QCALL;
1182 return retVal;
1183}
1184
1185void 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
1203void 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
1221bool 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