If IPC connections fails, then remove active session. (#24082)
[platform/upstream/coreclr.git] / src / vm / eventpipe.cpp
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 "diagnosticsipc.h"
9 #include "eventpipe.h"
10 #include "eventpipebuffermanager.h"
11 #include "eventpipeconfiguration.h"
12 #include "eventpipesessionprovider.h"
13 #include "eventpipeevent.h"
14 #include "eventpipeeventsource.h"
15 #include "eventpipefile.h"
16 #include "eventpipeprovider.h"
17 #include "eventpipesession.h"
18 #include "eventpipejsonfile.h"
19 #include "eventtracebase.h"
20 #include "sampleprofiler.h"
21 #include "win32threadpool.h"
22
23 #ifdef FEATURE_PAL
24 #include "pal.h"
25 #endif // FEATURE_PAL
26
27 #ifdef FEATURE_PERFTRACING
28
29 CrstStatic EventPipe::s_configCrst;
30 bool EventPipe::s_tracingInitialized = false;
31 EventPipeConfiguration *EventPipe::s_pConfig = NULL;
32 EventPipeSession *EventPipe::s_pSession = NULL;
33 EventPipeBufferManager *EventPipe::s_pBufferManager = NULL;
34 EventPipeFile *EventPipe::s_pFile = NULL;
35 EventPipeEventSource *EventPipe::s_pEventSource = NULL;
36 LPCWSTR EventPipe::s_pCommandLine = NULL;
37 HANDLE EventPipe::s_fileSwitchTimerHandle = NULL;
38 ULONGLONG EventPipe::s_lastFlushSwitchTime = 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(EventData *pEventData, unsigned int eventDataCount)
48 {
49     CONTRACTL
50     {
51         NOTHROW;
52         GC_NOTRIGGER;
53         MODE_ANY;
54     }
55     CONTRACTL_END;
56
57     m_pData = NULL;
58     m_pEventData = pEventData;
59     m_eventDataCount = eventDataCount;
60     m_allocatedData = false;
61
62     S_UINT32 tmp_size = S_UINT32(0);
63     for (unsigned int i = 0; i < m_eventDataCount; i++)
64     {
65         tmp_size += S_UINT32(m_pEventData[i].Size);
66     }
67
68     if (tmp_size.IsOverflow())
69     {
70         // If there is an overflow, drop the data and create an empty payload
71         m_pEventData = NULL;
72         m_eventDataCount = 0;
73         m_size = 0;
74     }
75     else
76     {
77         m_size = tmp_size.Value();
78     }
79 }
80
81 EventPipeEventPayload::~EventPipeEventPayload()
82 {
83     CONTRACTL
84     {
85         NOTHROW;
86         GC_NOTRIGGER;
87         MODE_ANY;
88     }
89     CONTRACTL_END;
90
91     if (m_allocatedData && m_pData != NULL)
92     {
93         delete[] m_pData;
94         m_pData = NULL;
95     }
96 }
97
98 void EventPipeEventPayload::Flatten()
99 {
100     CONTRACTL
101     {
102         NOTHROW;
103         GC_NOTRIGGER;
104         MODE_ANY;
105     }
106     CONTRACTL_END;
107
108     if (m_size > 0)
109     {
110         if (!IsFlattened())
111         {
112             BYTE *tmp_pData = new (nothrow) BYTE[m_size];
113             if (tmp_pData != NULL)
114             {
115                 m_allocatedData = true;
116                 CopyData(tmp_pData);
117                 m_pData = tmp_pData;
118             }
119         }
120     }
121 }
122
123 void EventPipeEventPayload::CopyData(BYTE *pDst)
124 {
125     CONTRACTL
126     {
127         NOTHROW;
128         GC_NOTRIGGER;
129         MODE_ANY;
130     }
131     CONTRACTL_END;
132
133     if (m_size > 0)
134     {
135         if (IsFlattened())
136         {
137             memcpy(pDst, m_pData, m_size);
138         }
139
140         else if (m_pEventData != NULL)
141         {
142             unsigned int offset = 0;
143             for (unsigned int i = 0; i < m_eventDataCount; i++)
144             {
145                 memcpy(pDst + offset, (BYTE *)m_pEventData[i].Ptr, m_pEventData[i].Size);
146                 offset += m_pEventData[i].Size;
147             }
148         }
149     }
150 }
151
152 BYTE *EventPipeEventPayload::GetFlatData()
153 {
154     CONTRACTL
155     {
156         NOTHROW;
157         GC_NOTRIGGER;
158         MODE_ANY;
159     }
160     CONTRACTL_END;
161
162     if (!IsFlattened())
163     {
164         Flatten();
165     }
166     return m_pData;
167 }
168
169 void EventPipe::Initialize()
170 {
171     STANDARD_VM_CONTRACT;
172
173     s_tracingInitialized = s_configCrst.InitNoThrow(
174         CrstEventPipe,
175         (CrstFlags)(CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN | CRST_HOST_BREAKABLE));
176
177     s_pConfig = new EventPipeConfiguration();
178     s_pConfig->Initialize();
179
180     s_pBufferManager = new EventPipeBufferManager();
181
182     s_pEventSource = new EventPipeEventSource();
183
184     // This calls into auto-generated code to initialize the runtime providers
185     // and events so that the EventPipe configuration lock isn't taken at runtime
186     InitProvidersAndEvents();
187 }
188
189 void EventPipe::Shutdown()
190 {
191     CONTRACTL
192     {
193         NOTHROW;
194         GC_TRIGGERS;
195         MODE_ANY;
196     }
197     CONTRACTL_END;
198
199     // Mark tracing as no longer initialized.
200     s_tracingInitialized = false;
201
202     // We are shutting down, so if disabling EventPipe throws, we need to move along anyway.
203     EX_TRY
204     {
205         Disable((EventPipeSessionID)s_pSession);
206     }
207     EX_CATCH {}
208     EX_END_CATCH(SwallowAllExceptions);
209
210     // Save pointers to the configuration and buffer manager.
211     EventPipeConfiguration *pConfig = s_pConfig;
212     EventPipeBufferManager *pBufferManager = s_pBufferManager;
213
214     // Set the static pointers to NULL so that the rest of the EventPipe knows that they are no longer available.
215     // Flush process write buffers to make sure other threads can see the change.
216     s_pConfig = NULL;
217     s_pBufferManager = NULL;
218     FlushProcessWriteBuffers();
219
220     // Free resources.
221     delete pConfig;
222     delete pBufferManager;
223     delete s_pEventSource;
224     s_pEventSource = NULL;
225
226     // On Windows, this is just a pointer to the return value from
227     // GetCommandLineW(), so don't attempt to free it.
228 #ifdef FEATURE_PAL
229     delete[] s_pCommandLine;
230     s_pCommandLine = NULL;
231 #endif
232 }
233
234 EventPipeSessionID EventPipe::Enable(
235     LPCWSTR strOutputPath,
236     uint32_t circularBufferSizeInMB,
237     uint64_t profilerSamplingRateInNanoseconds,
238     const EventPipeProviderConfiguration *pProviders,
239     uint32_t numProviders,
240     EventPipeSessionType sessionType,
241     IpcStream *const pStream)
242 {
243     CONTRACTL
244     {
245         THROWS;
246         GC_TRIGGERS;
247         MODE_ANY;
248         PRECONDITION(circularBufferSizeInMB > 0);
249         PRECONDITION(profilerSamplingRateInNanoseconds > 0);
250         PRECONDITION(numProviders > 0 && pProviders != nullptr);
251     }
252     CONTRACTL_END;
253
254     // Take the lock before enabling tracing.
255     CrstHolder _crst(GetLock());
256
257     // Create a new session.
258     SampleProfiler::SetSamplingRate(static_cast<unsigned long>(profilerSamplingRateInNanoseconds));
259     EventPipeSession *pSession = s_pConfig->CreateSession(
260         sessionType,
261         circularBufferSizeInMB,
262         pProviders,
263         numProviders);
264
265     EventPipeSessionID sessionId = Enable(strOutputPath, pSession, sessionType, pStream);
266     return sessionId;
267 }
268
269 EventPipeSessionID EventPipe::Enable(
270     LPCWSTR strOutputPath,
271     EventPipeSession *const pSession,
272     EventPipeSessionType sessionType,
273     IpcStream *const pStream)
274 {
275     CONTRACTL
276     {
277         THROWS;
278         GC_TRIGGERS;
279         MODE_ANY;
280         PRECONDITION(pSession != nullptr);
281         PRECONDITION(GetLock()->OwnedByCurrentThread());
282     }
283     CONTRACTL_END;
284
285     // If the state or arguments are invalid, bail here.
286     if (pSession == nullptr || !pSession->IsValid())
287         return 0;
288     if (sessionType == EventPipeSessionType::File && strOutputPath == nullptr)
289         return 0;
290     if (sessionType == EventPipeSessionType::IpcStream && pStream == nullptr)
291         return 0;
292
293     // If tracing is not initialized or is already enabled, bail here.
294     if (!s_tracingInitialized || s_pConfig == nullptr || s_pConfig->Enabled())
295         return 0;
296
297     // Enable the EventPipe EventSource.
298     s_pEventSource->Enable(pSession);
299
300     // Save the session.
301     s_pSession = pSession;
302     EventPipeSessionID sessionId = reinterpret_cast<EventPipeSessionID>(s_pSession);
303
304     // Create the event pipe file.
305     // A NULL output path means that we should not write the results to a file.
306     // This is used in the EventListener streaming case.
307     switch (sessionType)
308     {
309         case EventPipeSessionType::File:
310             if (strOutputPath != nullptr)
311                 s_pFile = new EventPipeFile(new FileStreamWriter(SString(strOutputPath)));
312             break;
313
314         case EventPipeSessionType::IpcStream:
315             s_pFile = new EventPipeFile(new IpcStreamWriter(sessionId, pStream));
316             CreateFlushTimerCallback();
317             break;
318
319         default:
320             s_pFile = nullptr;
321             break;
322     }
323
324     // Enable tracing.
325     s_pConfig->Enable(s_pSession);
326
327     // Enable the sample profiler
328     SampleProfiler::Enable();
329
330     // Return the session ID.
331     return sessionId;
332 }
333
334 void EventPipe::Disable(EventPipeSessionID id)
335 {
336     CONTRACTL
337     {
338         THROWS;
339         GC_TRIGGERS;
340         MODE_ANY;
341     }
342     CONTRACTL_END;
343
344     // Only perform the disable operation if the session ID
345     // matches the current active session.
346     if (id != (EventPipeSessionID)s_pSession)
347         return;
348
349     // Don't block GC during clean-up.
350     GCX_PREEMP();
351
352     // Take the lock before disabling tracing.
353     CrstHolder _crst(GetLock());
354     DisableInternal(reinterpret_cast<EventPipeSessionID>(s_pSession));
355 }
356
357 void EventPipe::DisableInternal(EventPipeSessionID id)
358 {
359     CONTRACTL
360     {
361         THROWS;
362         GC_TRIGGERS;
363         MODE_ANY;
364         PRECONDITION(GetLock()->OwnedByCurrentThread());
365     }
366     CONTRACTL_END;
367
368     if (s_pConfig != NULL && s_pConfig->Enabled())
369     {
370         // Disable the profiler.
371         SampleProfiler::Disable();
372
373         // Log the process information event.
374         s_pEventSource->SendProcessInfo(s_pCommandLine);
375
376         // Log the runtime information event.
377         ETW::InfoLog::RuntimeInformation(ETW::InfoLog::InfoStructs::Normal);
378
379         // Disable tracing.
380         s_pConfig->Disable(s_pSession);
381
382         // Delete the session.
383         s_pConfig->DeleteSession(s_pSession);
384         s_pSession = NULL;
385
386         // Delete the file switch timer.
387         DeleteFlushTimerCallback();
388
389         // Flush all write buffers to make sure that all threads see the change.
390         FlushProcessWriteBuffers();
391
392         // Write to the file.
393         if (s_pFile != nullptr)
394         {
395             LARGE_INTEGER disableTimeStamp;
396             QueryPerformanceCounter(&disableTimeStamp);
397             s_pBufferManager->WriteAllBuffersToFile(s_pFile, disableTimeStamp);
398
399             if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0)
400             {
401                 // Before closing the file, do rundown.
402                 const EventPipeProviderConfiguration RundownProviders[] = {
403                     {W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL},       // Public provider.
404                     {W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL} // Rundown provider.
405                 };
406
407                 // The circular buffer size doesn't matter because all events are written synchronously during rundown.
408                 s_pSession = s_pConfig->CreateSession(
409                     EventPipeSessionType::File,
410                     1 /* circularBufferSizeInMB */,
411                     RundownProviders,
412                     sizeof(RundownProviders) / sizeof(EventPipeProviderConfiguration));
413                 s_pConfig->EnableRundown(s_pSession);
414
415                 // Ask the runtime to emit rundown events.
416                 if (g_fEEStarted && !g_fEEShutDown)
417                     ETW::EnumerationLog::EndRundown();
418
419                 // Disable the event pipe now that rundown is complete.
420                 s_pConfig->Disable(s_pSession);
421
422                 // Delete the rundown session.
423                 s_pConfig->DeleteSession(s_pSession);
424                 s_pSession = NULL;
425             }
426
427             delete s_pFile;
428             s_pFile = nullptr;
429         }
430
431         // De-allocate buffers.
432         s_pBufferManager->DeAllocateBuffers();
433
434         // Delete deferred providers.
435         // Providers can't be deleted during tracing because they may be needed when serializing the file.
436         s_pConfig->DeleteDeferredProviders();
437     }
438 }
439
440 void EventPipe::CreateFlushTimerCallback()
441 {
442     CONTRACTL
443     {
444         THROWS;
445         GC_TRIGGERS;
446         MODE_ANY;
447         PRECONDITION(GetLock()->OwnedByCurrentThread());
448     }
449     CONTRACTL_END
450
451     if (s_pFile == nullptr)
452         return;
453
454     NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new (nothrow) ThreadpoolMgr::TimerInfoContext();
455     if (timerContextHolder == NULL)
456         return;
457
458     timerContextHolder->TimerId = 0;
459
460     // Initialize the last file switch time.
461     s_lastFlushSwitchTime = CLRGetTickCount64();
462
463     bool success = false;
464     _ASSERTE(s_fileSwitchTimerHandle == NULL);
465     EX_TRY
466     {
467         if (ThreadpoolMgr::CreateTimerQueueTimer(
468                 &s_fileSwitchTimerHandle,
469                 FlushTimer,
470                 timerContextHolder,
471                 100, // DueTime (msec)
472                 100, // Period (msec)
473                 0 /* flags */))
474         {
475             _ASSERTE(s_fileSwitchTimerHandle != NULL);
476             success = true;
477         }
478     }
479     EX_CATCH
480     {
481     }
482     EX_END_CATCH(RethrowTerminalExceptions);
483
484     if (!success)
485     {
486         _ASSERTE(s_fileSwitchTimerHandle == NULL);
487         return;
488     }
489
490     timerContextHolder.SuppressRelease(); // the timer context is automatically deleted by the timer infrastructure
491 }
492
493 void EventPipe::DeleteFlushTimerCallback()
494 {
495     CONTRACTL
496     {
497         THROWS;
498         GC_TRIGGERS;
499         MODE_ANY;
500         PRECONDITION(GetLock()->OwnedByCurrentThread());
501     }
502     CONTRACTL_END
503
504     if ((s_fileSwitchTimerHandle != NULL) && (ThreadpoolMgr::DeleteTimerQueueTimer(s_fileSwitchTimerHandle, NULL)))
505         s_fileSwitchTimerHandle = NULL;
506 }
507
508 void WINAPI EventPipe::FlushTimer(PVOID parameter, BOOLEAN timerFired)
509 {
510     CONTRACTL
511     {
512         THROWS;
513         GC_TRIGGERS;
514         MODE_ANY;
515         PRECONDITION(timerFired);
516     }
517     CONTRACTL_END;
518
519     GCX_PREEMP();
520
521     // Take the lock control lock to make sure that tracing isn't disabled during this operation.
522     CrstHolder _crst(GetLock());
523
524     if (s_pSession == nullptr || s_pFile == nullptr)
525         return;
526
527     // Make sure that we should actually switch files.
528     if (!Enabled() || s_pSession->GetSessionType() != EventPipeSessionType::IpcStream)
529         return;
530
531     if (CLRGetTickCount64() > (s_lastFlushSwitchTime + 100))
532     {
533         // Get the current time stamp.
534         // WriteAllBuffersToFile will use this to ensure that no events after
535         // the current timestamp are written into the file.
536         LARGE_INTEGER stopTimeStamp;
537         QueryPerformanceCounter(&stopTimeStamp);
538         s_pBufferManager->WriteAllBuffersToFile(s_pFile, stopTimeStamp);
539
540         s_lastFlushSwitchTime = CLRGetTickCount64();
541     }
542
543     if (s_pFile->HasErrors())
544     {
545         EX_TRY
546         {
547             DisableInternal(reinterpret_cast<EventPipeSessionID>(s_pSession));
548         }
549         EX_CATCH {}
550         EX_END_CATCH(SwallowAllExceptions);
551     }
552 }
553
554 EventPipeSession *EventPipe::GetSession(EventPipeSessionID id)
555 {
556     LIMITED_METHOD_CONTRACT;
557
558     EventPipeSession *pSession = NULL;
559     if ((EventPipeSessionID)s_pSession == id)
560     {
561         pSession = s_pSession;
562     }
563     return pSession;
564 }
565
566 bool EventPipe::Enabled()
567 {
568     LIMITED_METHOD_CONTRACT;
569
570     bool enabled = false;
571     if (s_pConfig != NULL)
572     {
573         enabled = s_pConfig->Enabled();
574     }
575
576     return enabled;
577 }
578
579 EventPipeProvider *EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
580 {
581     CONTRACTL
582     {
583         THROWS;
584         GC_TRIGGERS;
585         MODE_ANY;
586     }
587     CONTRACTL_END;
588
589     EventPipeProvider *pProvider = NULL;
590     if (s_pConfig != NULL)
591     {
592         pProvider = s_pConfig->CreateProvider(providerName, pCallbackFunction, pCallbackData);
593     }
594
595     return pProvider;
596 }
597
598 EventPipeProvider *EventPipe::GetProvider(const SString &providerName)
599 {
600     CONTRACTL
601     {
602         THROWS;
603         GC_NOTRIGGER;
604         MODE_ANY;
605     }
606     CONTRACTL_END;
607
608     EventPipeProvider *pProvider = NULL;
609     if (s_pConfig != NULL)
610     {
611         pProvider = s_pConfig->GetProvider(providerName);
612     }
613
614     return pProvider;
615 }
616
617 void EventPipe::DeleteProvider(EventPipeProvider *pProvider)
618 {
619     CONTRACTL
620     {
621         THROWS;
622         GC_TRIGGERS;
623         MODE_ANY;
624     }
625     CONTRACTL_END;
626
627     // Take the lock to make sure that we don't have a race
628     // between disabling tracing and deleting a provider
629     // where we hold a provider after tracing has been disabled.
630     CrstHolder _crst(GetLock());
631
632     if (pProvider != NULL)
633     {
634         if (Enabled())
635         {
636             // Save the provider until the end of the tracing session.
637             pProvider->SetDeleteDeferred();
638         }
639         else
640         {
641             // Delete the provider now.
642             if (s_pConfig != NULL)
643             {
644                 s_pConfig->DeleteProvider(pProvider);
645             }
646         }
647     }
648 }
649
650 void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId, LPCGUID pRelatedActivityId)
651 {
652     CONTRACTL
653     {
654         NOTHROW;
655         GC_NOTRIGGER;
656         MODE_ANY;
657     }
658     CONTRACTL_END;
659
660     EventPipeEventPayload payload(pData, length);
661     EventPipe::WriteEventInternal(event, payload, pActivityId, pRelatedActivityId);
662 }
663
664 void EventPipe::WriteEvent(EventPipeEvent &event, EventData *pEventData, unsigned int eventDataCount, LPCGUID pActivityId, LPCGUID pRelatedActivityId)
665 {
666     CONTRACTL
667     {
668         NOTHROW;
669         GC_NOTRIGGER;
670         MODE_ANY;
671     }
672     CONTRACTL_END;
673
674     EventPipeEventPayload payload(pEventData, eventDataCount);
675     EventPipe::WriteEventInternal(event, payload, pActivityId, pRelatedActivityId);
676 }
677
678 void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId)
679 {
680     CONTRACTL
681     {
682         NOTHROW;
683         GC_NOTRIGGER;
684         MODE_ANY;
685     }
686     CONTRACTL_END;
687
688     // Exit early if the event is not enabled.
689     if (!event.IsEnabled())
690     {
691         return;
692     }
693
694     // Get the current thread;
695     Thread *pThread = GetThread();
696
697     if (s_pConfig == NULL)
698     {
699         // We can't procede without a configuration
700         return;
701     }
702     _ASSERTE(s_pSession != NULL);
703
704     // If the activity id isn't specified AND we are in a managed thread, pull it from the current thread.
705     // If pThread is NULL (we aren't in writing from a managed thread) then pActivityId can be NULL
706     if (pActivityId == NULL && pThread != NULL)
707     {
708         pActivityId = pThread->GetActivityId();
709     }
710
711     if (!s_pConfig->RundownEnabled() && s_pBufferManager != NULL)
712     {
713         s_pBufferManager->WriteEvent(pThread, *s_pSession, event, payload, pActivityId, pRelatedActivityId);
714     }
715     else if (s_pConfig->RundownEnabled())
716     {
717         // It is possible that some events that are enabled on rundown can be emitted from other threads.
718         // We're not interested in these events and they can cause corrupted trace files because rundown
719         // events are written synchronously and not under lock.
720         // If we encounter an event that did not originate on the thread that is doing rundown, ignore it.
721         if (pThread == NULL || !s_pConfig->IsRundownThread(pThread))
722         {
723             return;
724         }
725
726         BYTE *pData = payload.GetFlatData();
727         if (pData != NULL)
728         {
729             // Write synchronously to the file.
730             // We're under lock and blocking the disabling thread.
731             // This copy occurs here (rather than at file write) because
732             // A) The FastSerializer API would need to change if we waited
733             // B) It is unclear there is a benefit to multiple file write calls
734             //    as opposed a a buffer copy here
735             EventPipeEventInstance instance(
736                 *s_pSession,
737                 event,
738                 pThread->GetOSThreadId(),
739                 pData,
740                 payload.GetSize(),
741                 pActivityId,
742                 pRelatedActivityId);
743             instance.EnsureStack(*s_pSession);
744
745             if (s_pFile != NULL)
746             {
747                 // EventPipeFile::WriteEvent needs to allocate a metadata event
748                 // and can therefore throw. In this context we will silently
749                 // fail rather than disrupt the caller
750                 EX_TRY
751                 {
752                     s_pFile->WriteEvent(instance);
753                 }
754                 EX_CATCH {}
755                 EX_END_CATCH(SwallowAllExceptions);
756             }
757         }
758     }
759 }
760
761 void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData, unsigned int length)
762 {
763     CONTRACTL
764     {
765         NOTHROW;
766         GC_TRIGGERS;
767         MODE_PREEMPTIVE;
768     }
769     CONTRACTL_END;
770
771     EventPipeEventPayload payload(pData, length);
772
773     // Write the event to the thread's buffer.
774     if (s_pBufferManager != NULL)
775     {
776         // Specify the sampling thread as the "current thread", so that we select the right buffer.
777         // Specify the target thread so that the event gets properly attributed.
778         s_pBufferManager->WriteEvent(pSamplingThread, *s_pSession, *pEvent, payload, NULL /* pActivityId */, NULL /* pRelatedActivityId */, pTargetThread, &stackContents);
779     }
780 }
781
782 bool EventPipe::WalkManagedStackForCurrentThread(StackContents &stackContents)
783 {
784     CONTRACTL
785     {
786         NOTHROW;
787         GC_NOTRIGGER;
788         MODE_ANY;
789     }
790     CONTRACTL_END;
791
792     Thread *pThread = GetThread();
793     if (pThread != NULL)
794     {
795         return WalkManagedStackForThread(pThread, stackContents);
796     }
797
798     return false;
799 }
800
801 bool EventPipe::WalkManagedStackForThread(Thread *pThread, StackContents &stackContents)
802 {
803     CONTRACTL
804     {
805         NOTHROW;
806         GC_NOTRIGGER;
807         MODE_ANY;
808         PRECONDITION(pThread != NULL);
809     }
810     CONTRACTL_END;
811
812     // Calling into StackWalkFrames in preemptive mode violates the host contract,
813     // but this contract is not used on CoreCLR.
814     CONTRACT_VIOLATION(HostViolation);
815
816     stackContents.Reset();
817
818     StackWalkAction swaRet = pThread->StackWalkFrames(
819         (PSTACKWALKFRAMESCALLBACK)&StackWalkCallback,
820         &stackContents,
821         ALLOW_ASYNC_STACK_WALK | FUNCTIONSONLY | HANDLESKIPPEDFRAMES | ALLOW_INVALID_OBJECTS);
822
823     return ((swaRet == SWA_DONE) || (swaRet == SWA_CONTINUE));
824 }
825
826 StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pData)
827 {
828     CONTRACTL
829     {
830         NOTHROW;
831         GC_NOTRIGGER;
832         MODE_ANY;
833         PRECONDITION(pCf != NULL);
834         PRECONDITION(pData != NULL);
835     }
836     CONTRACTL_END;
837
838     // Get the IP.
839     UINT_PTR controlPC = (UINT_PTR)pCf->GetRegisterSet()->ControlPC;
840     if (controlPC == 0)
841     {
842         if (pData->GetLength() == 0)
843         {
844             // This happens for pinvoke stubs on the top of the stack.
845             return SWA_CONTINUE;
846         }
847     }
848
849     _ASSERTE(controlPC != 0);
850
851     // Add the IP to the captured stack.
852     pData->Append(
853         controlPC,
854         pCf->GetFunction());
855
856     // Continue the stack walk.
857     return SWA_CONTINUE;
858 }
859
860 void EventPipe::SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv)
861 {
862     CONTRACTL
863     {
864         THROWS;
865         GC_TRIGGERS;
866         MODE_COOPERATIVE;
867         PRECONDITION(pwzAssemblyPath != NULL);
868         PRECONDITION(argc <= 0 || argv != NULL);
869     }
870     CONTRACTL_END;
871
872     // Get the command line.
873     LPCWSTR osCommandLine = GetCommandLineW();
874
875 #ifndef FEATURE_PAL
876     // On Windows, osCommandLine contains the executable and all arguments.
877     s_pCommandLine = osCommandLine;
878 #else
879     // On UNIX, the PAL doesn't have the command line arguments, so we must build the command line.
880     // osCommandLine contains the full path to the executable.
881     SString commandLine(osCommandLine);
882     commandLine.Append((WCHAR)' ');
883     commandLine.Append(pwzAssemblyPath);
884
885     for (int i = 0; i < argc; i++)
886     {
887         commandLine.Append((WCHAR)' ');
888         commandLine.Append(argv[i]);
889     }
890
891     // Allocate a new string for the command line.
892     SIZE_T commandLineLen = commandLine.GetCount();
893     WCHAR *pCommandLine = new WCHAR[commandLineLen + 1];
894     wcsncpy(pCommandLine, commandLine.GetUnicode(), commandLineLen);
895     pCommandLine[commandLineLen] = '\0';
896
897     s_pCommandLine = pCommandLine;
898 #endif
899 }
900
901 EventPipeEventInstance *EventPipe::GetNextEvent()
902 {
903     CONTRACTL
904     {
905         THROWS;
906         GC_TRIGGERS;
907         MODE_PREEMPTIVE;
908     }
909     CONTRACTL_END;
910
911     EventPipeEventInstance *pInstance = NULL;
912
913     // Only fetch the next event if a tracing session exists.
914     // The buffer manager is not disposed until the process is shutdown.
915     if (s_pSession != NULL)
916     {
917         pInstance = s_pBufferManager->GetNextEvent();
918     }
919
920     return pInstance;
921 }
922
923 #endif // FEATURE_PERFTRACING