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.
8 #include "diagnosticsipc.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"
27 #ifdef FEATURE_PERFTRACING
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;
41 // This function is auto-generated from /src/scripts/genEventPipe.py
42 extern "C" void InitProvidersAndEvents();
44 void InitProvidersAndEvents();
47 EventPipeEventPayload::EventPipeEventPayload(EventData *pEventData, unsigned int eventDataCount)
58 m_pEventData = pEventData;
59 m_eventDataCount = eventDataCount;
60 m_allocatedData = false;
62 S_UINT32 tmp_size = S_UINT32(0);
63 for (unsigned int i = 0; i < m_eventDataCount; i++)
65 tmp_size += S_UINT32(m_pEventData[i].Size);
68 if (tmp_size.IsOverflow())
70 // If there is an overflow, drop the data and create an empty payload
77 m_size = tmp_size.Value();
81 EventPipeEventPayload::~EventPipeEventPayload()
91 if (m_allocatedData && m_pData != NULL)
98 void EventPipeEventPayload::Flatten()
112 BYTE *tmp_pData = new (nothrow) BYTE[m_size];
113 if (tmp_pData != NULL)
115 m_allocatedData = true;
123 void EventPipeEventPayload::CopyData(BYTE *pDst)
137 memcpy(pDst, m_pData, m_size);
140 else if (m_pEventData != NULL)
142 unsigned int offset = 0;
143 for (unsigned int i = 0; i < m_eventDataCount; i++)
145 memcpy(pDst + offset, (BYTE *)m_pEventData[i].Ptr, m_pEventData[i].Size);
146 offset += m_pEventData[i].Size;
152 BYTE *EventPipeEventPayload::GetFlatData()
169 void EventPipe::Initialize()
171 STANDARD_VM_CONTRACT;
173 s_tracingInitialized = s_configCrst.InitNoThrow(
175 (CrstFlags)(CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN | CRST_HOST_BREAKABLE));
177 s_pConfig = new EventPipeConfiguration();
178 s_pConfig->Initialize();
180 s_pBufferManager = new EventPipeBufferManager();
182 s_pEventSource = new EventPipeEventSource();
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();
189 void EventPipe::Shutdown()
199 // Mark tracing as no longer initialized.
200 s_tracingInitialized = false;
202 // We are shutting down, so if disabling EventPipe throws, we need to move along anyway.
205 Disable((EventPipeSessionID)s_pSession);
208 EX_END_CATCH(SwallowAllExceptions);
210 // Save pointers to the configuration and buffer manager.
211 EventPipeConfiguration *pConfig = s_pConfig;
212 EventPipeBufferManager *pBufferManager = s_pBufferManager;
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.
217 s_pBufferManager = NULL;
218 FlushProcessWriteBuffers();
222 delete pBufferManager;
223 delete s_pEventSource;
224 s_pEventSource = NULL;
226 // On Windows, this is just a pointer to the return value from
227 // GetCommandLineW(), so don't attempt to free it.
229 delete[] s_pCommandLine;
230 s_pCommandLine = NULL;
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)
248 PRECONDITION(circularBufferSizeInMB > 0);
249 PRECONDITION(profilerSamplingRateInNanoseconds > 0);
250 PRECONDITION(numProviders > 0 && pProviders != nullptr);
254 // Take the lock before enabling tracing.
255 CrstHolder _crst(GetLock());
257 // Create a new session.
258 SampleProfiler::SetSamplingRate(static_cast<unsigned long>(profilerSamplingRateInNanoseconds));
259 EventPipeSession *pSession = s_pConfig->CreateSession(
261 circularBufferSizeInMB,
265 EventPipeSessionID sessionId = Enable(strOutputPath, pSession, sessionType, pStream);
269 EventPipeSessionID EventPipe::Enable(
270 LPCWSTR strOutputPath,
271 EventPipeSession *const pSession,
272 EventPipeSessionType sessionType,
273 IpcStream *const pStream)
280 PRECONDITION(pSession != nullptr);
281 PRECONDITION(GetLock()->OwnedByCurrentThread());
285 // If the state or arguments are invalid, bail here.
286 if (pSession == nullptr || !pSession->IsValid())
288 if (sessionType == EventPipeSessionType::File && strOutputPath == nullptr)
290 if (sessionType == EventPipeSessionType::IpcStream && pStream == nullptr)
293 // If tracing is not initialized or is already enabled, bail here.
294 if (!s_tracingInitialized || s_pConfig == nullptr || s_pConfig->Enabled())
297 // Enable the EventPipe EventSource.
298 s_pEventSource->Enable(pSession);
301 s_pSession = pSession;
302 EventPipeSessionID sessionId = reinterpret_cast<EventPipeSessionID>(s_pSession);
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.
309 case EventPipeSessionType::File:
310 if (strOutputPath != nullptr)
311 s_pFile = new EventPipeFile(new FileStreamWriter(SString(strOutputPath)));
314 case EventPipeSessionType::IpcStream:
315 s_pFile = new EventPipeFile(new IpcStreamWriter(sessionId, pStream));
316 CreateFlushTimerCallback();
325 s_pConfig->Enable(s_pSession);
327 // Enable the sample profiler
328 SampleProfiler::Enable();
330 // Return the session ID.
334 void EventPipe::Disable(EventPipeSessionID id)
344 // Only perform the disable operation if the session ID
345 // matches the current active session.
346 if (id != (EventPipeSessionID)s_pSession)
349 // Don't block GC during clean-up.
352 // Take the lock before disabling tracing.
353 CrstHolder _crst(GetLock());
354 DisableInternal(reinterpret_cast<EventPipeSessionID>(s_pSession));
357 void EventPipe::DisableInternal(EventPipeSessionID id)
364 PRECONDITION(GetLock()->OwnedByCurrentThread());
368 if (s_pConfig != NULL && s_pConfig->Enabled())
370 // Disable the profiler.
371 SampleProfiler::Disable();
373 // Log the process information event.
374 s_pEventSource->SendProcessInfo(s_pCommandLine);
376 // Log the runtime information event.
377 ETW::InfoLog::RuntimeInformation(ETW::InfoLog::InfoStructs::Normal);
380 s_pConfig->Disable(s_pSession);
382 // Delete the session.
383 s_pConfig->DeleteSession(s_pSession);
386 // Delete the file switch timer.
387 DeleteFlushTimerCallback();
389 // Flush all write buffers to make sure that all threads see the change.
390 FlushProcessWriteBuffers();
392 // Write to the file.
393 if (s_pFile != nullptr)
395 LARGE_INTEGER disableTimeStamp;
396 QueryPerformanceCounter(&disableTimeStamp);
397 s_pBufferManager->WriteAllBuffersToFile(s_pFile, disableTimeStamp);
399 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0)
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.
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 */,
412 sizeof(RundownProviders) / sizeof(EventPipeProviderConfiguration));
413 s_pConfig->EnableRundown(s_pSession);
415 // Ask the runtime to emit rundown events.
416 if (g_fEEStarted && !g_fEEShutDown)
417 ETW::EnumerationLog::EndRundown();
419 // Disable the event pipe now that rundown is complete.
420 s_pConfig->Disable(s_pSession);
422 // Delete the rundown session.
423 s_pConfig->DeleteSession(s_pSession);
431 // De-allocate buffers.
432 s_pBufferManager->DeAllocateBuffers();
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();
440 void EventPipe::CreateFlushTimerCallback()
447 PRECONDITION(GetLock()->OwnedByCurrentThread());
451 if (s_pFile == nullptr)
454 NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new (nothrow) ThreadpoolMgr::TimerInfoContext();
455 if (timerContextHolder == NULL)
458 timerContextHolder->TimerId = 0;
460 // Initialize the last file switch time.
461 s_lastFlushSwitchTime = CLRGetTickCount64();
463 bool success = false;
464 _ASSERTE(s_fileSwitchTimerHandle == NULL);
467 if (ThreadpoolMgr::CreateTimerQueueTimer(
468 &s_fileSwitchTimerHandle,
471 100, // DueTime (msec)
472 100, // Period (msec)
475 _ASSERTE(s_fileSwitchTimerHandle != NULL);
482 EX_END_CATCH(RethrowTerminalExceptions);
486 _ASSERTE(s_fileSwitchTimerHandle == NULL);
490 timerContextHolder.SuppressRelease(); // the timer context is automatically deleted by the timer infrastructure
493 void EventPipe::DeleteFlushTimerCallback()
500 PRECONDITION(GetLock()->OwnedByCurrentThread());
504 if ((s_fileSwitchTimerHandle != NULL) && (ThreadpoolMgr::DeleteTimerQueueTimer(s_fileSwitchTimerHandle, NULL)))
505 s_fileSwitchTimerHandle = NULL;
508 void WINAPI EventPipe::FlushTimer(PVOID parameter, BOOLEAN timerFired)
515 PRECONDITION(timerFired);
521 // Take the lock control lock to make sure that tracing isn't disabled during this operation.
522 CrstHolder _crst(GetLock());
524 if (s_pSession == nullptr || s_pFile == nullptr)
527 // Make sure that we should actually switch files.
528 if (!Enabled() || s_pSession->GetSessionType() != EventPipeSessionType::IpcStream)
531 if (CLRGetTickCount64() > (s_lastFlushSwitchTime + 100))
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);
540 s_lastFlushSwitchTime = CLRGetTickCount64();
543 if (s_pFile->HasErrors())
547 DisableInternal(reinterpret_cast<EventPipeSessionID>(s_pSession));
550 EX_END_CATCH(SwallowAllExceptions);
554 EventPipeSession *EventPipe::GetSession(EventPipeSessionID id)
556 LIMITED_METHOD_CONTRACT;
558 EventPipeSession *pSession = NULL;
559 if ((EventPipeSessionID)s_pSession == id)
561 pSession = s_pSession;
566 bool EventPipe::Enabled()
568 LIMITED_METHOD_CONTRACT;
570 bool enabled = false;
571 if (s_pConfig != NULL)
573 enabled = s_pConfig->Enabled();
579 EventPipeProvider *EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
589 EventPipeProvider *pProvider = NULL;
590 if (s_pConfig != NULL)
592 pProvider = s_pConfig->CreateProvider(providerName, pCallbackFunction, pCallbackData);
598 EventPipeProvider *EventPipe::GetProvider(const SString &providerName)
608 EventPipeProvider *pProvider = NULL;
609 if (s_pConfig != NULL)
611 pProvider = s_pConfig->GetProvider(providerName);
617 void EventPipe::DeleteProvider(EventPipeProvider *pProvider)
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());
632 if (pProvider != NULL)
636 // Save the provider until the end of the tracing session.
637 pProvider->SetDeleteDeferred();
641 // Delete the provider now.
642 if (s_pConfig != NULL)
644 s_pConfig->DeleteProvider(pProvider);
650 void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId, LPCGUID pRelatedActivityId)
660 EventPipeEventPayload payload(pData, length);
661 EventPipe::WriteEventInternal(event, payload, pActivityId, pRelatedActivityId);
664 void EventPipe::WriteEvent(EventPipeEvent &event, EventData *pEventData, unsigned int eventDataCount, LPCGUID pActivityId, LPCGUID pRelatedActivityId)
674 EventPipeEventPayload payload(pEventData, eventDataCount);
675 EventPipe::WriteEventInternal(event, payload, pActivityId, pRelatedActivityId);
678 void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId)
688 // Exit early if the event is not enabled.
689 if (!event.IsEnabled())
694 // Get the current thread;
695 Thread *pThread = GetThread();
697 if (s_pConfig == NULL)
699 // We can't procede without a configuration
702 _ASSERTE(s_pSession != NULL);
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)
708 pActivityId = pThread->GetActivityId();
711 if (!s_pConfig->RundownEnabled() && s_pBufferManager != NULL)
713 s_pBufferManager->WriteEvent(pThread, *s_pSession, event, payload, pActivityId, pRelatedActivityId);
715 else if (s_pConfig->RundownEnabled())
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))
726 BYTE *pData = payload.GetFlatData();
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(
738 pThread->GetOSThreadId(),
743 instance.EnsureStack(*s_pSession);
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
752 s_pFile->WriteEvent(instance);
755 EX_END_CATCH(SwallowAllExceptions);
761 void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData, unsigned int length)
771 EventPipeEventPayload payload(pData, length);
773 // Write the event to the thread's buffer.
774 if (s_pBufferManager != NULL)
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);
782 bool EventPipe::WalkManagedStackForCurrentThread(StackContents &stackContents)
792 Thread *pThread = GetThread();
795 return WalkManagedStackForThread(pThread, stackContents);
801 bool EventPipe::WalkManagedStackForThread(Thread *pThread, StackContents &stackContents)
808 PRECONDITION(pThread != NULL);
812 // Calling into StackWalkFrames in preemptive mode violates the host contract,
813 // but this contract is not used on CoreCLR.
814 CONTRACT_VIOLATION(HostViolation);
816 stackContents.Reset();
818 StackWalkAction swaRet = pThread->StackWalkFrames(
819 (PSTACKWALKFRAMESCALLBACK)&StackWalkCallback,
821 ALLOW_ASYNC_STACK_WALK | FUNCTIONSONLY | HANDLESKIPPEDFRAMES | ALLOW_INVALID_OBJECTS);
823 return ((swaRet == SWA_DONE) || (swaRet == SWA_CONTINUE));
826 StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pData)
833 PRECONDITION(pCf != NULL);
834 PRECONDITION(pData != NULL);
839 UINT_PTR controlPC = (UINT_PTR)pCf->GetRegisterSet()->ControlPC;
842 if (pData->GetLength() == 0)
844 // This happens for pinvoke stubs on the top of the stack.
849 _ASSERTE(controlPC != 0);
851 // Add the IP to the captured stack.
856 // Continue the stack walk.
860 void EventPipe::SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv)
867 PRECONDITION(pwzAssemblyPath != NULL);
868 PRECONDITION(argc <= 0 || argv != NULL);
872 // Get the command line.
873 LPCWSTR osCommandLine = GetCommandLineW();
876 // On Windows, osCommandLine contains the executable and all arguments.
877 s_pCommandLine = osCommandLine;
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);
885 for (int i = 0; i < argc; i++)
887 commandLine.Append((WCHAR)' ');
888 commandLine.Append(argv[i]);
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';
897 s_pCommandLine = pCommandLine;
901 EventPipeEventInstance *EventPipe::GetNextEvent()
911 EventPipeEventInstance *pInstance = NULL;
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)
917 pInstance = s_pBufferManager->GetNextEvent();
923 #endif // FEATURE_PERFTRACING