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());
355 if (s_pConfig != NULL && s_pConfig->Enabled())
357 // Disable the profiler.
358 SampleProfiler::Disable();
360 // Log the process information event.
361 s_pEventSource->SendProcessInfo(s_pCommandLine);
363 // Log the runtime information event.
364 ETW::InfoLog::RuntimeInformation(ETW::InfoLog::InfoStructs::Normal);
367 s_pConfig->Disable(s_pSession);
369 // Delete the session.
370 s_pConfig->DeleteSession(s_pSession);
373 // Delete the file switch timer.
374 DeleteFlushTimerCallback();
376 // Flush all write buffers to make sure that all threads see the change.
377 FlushProcessWriteBuffers();
379 // Write to the file.
380 if (s_pFile != nullptr)
382 LARGE_INTEGER disableTimeStamp;
383 QueryPerformanceCounter(&disableTimeStamp);
384 s_pBufferManager->WriteAllBuffersToFile(s_pFile, disableTimeStamp);
386 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0)
388 // Before closing the file, do rundown.
389 const EventPipeProviderConfiguration RundownProviders[] = {
390 {W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL}, // Public provider.
391 {W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL} // Rundown provider.
394 // The circular buffer size doesn't matter because all events are written synchronously during rundown.
395 s_pSession = s_pConfig->CreateSession(
396 EventPipeSessionType::File,
397 1 /* circularBufferSizeInMB */,
399 sizeof(RundownProviders) / sizeof(EventPipeProviderConfiguration));
400 s_pConfig->EnableRundown(s_pSession);
402 // Ask the runtime to emit rundown events.
403 if (g_fEEStarted && !g_fEEShutDown)
404 ETW::EnumerationLog::EndRundown();
406 // Disable the event pipe now that rundown is complete.
407 s_pConfig->Disable(s_pSession);
409 // Delete the rundown session.
410 s_pConfig->DeleteSession(s_pSession);
418 // De-allocate buffers.
419 s_pBufferManager->DeAllocateBuffers();
421 // Delete deferred providers.
422 // Providers can't be deleted during tracing because they may be needed when serializing the file.
423 s_pConfig->DeleteDeferredProviders();
427 void EventPipe::CreateFlushTimerCallback()
434 PRECONDITION(GetLock()->OwnedByCurrentThread());
438 if (s_pFile == nullptr)
441 NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new (nothrow) ThreadpoolMgr::TimerInfoContext();
442 if (timerContextHolder == NULL)
445 timerContextHolder->TimerId = 0;
447 // Initialize the last file switch time.
448 s_lastFlushSwitchTime = CLRGetTickCount64();
450 bool success = false;
451 _ASSERTE(s_fileSwitchTimerHandle == NULL);
454 if (ThreadpoolMgr::CreateTimerQueueTimer(
455 &s_fileSwitchTimerHandle,
458 100, // DueTime (msec)
459 100, // Period (msec)
462 _ASSERTE(s_fileSwitchTimerHandle != NULL);
469 EX_END_CATCH(RethrowTerminalExceptions);
473 _ASSERTE(s_fileSwitchTimerHandle == NULL);
477 timerContextHolder.SuppressRelease(); // the timer context is automatically deleted by the timer infrastructure
480 void EventPipe::DeleteFlushTimerCallback()
487 PRECONDITION(GetLock()->OwnedByCurrentThread());
491 if ((s_fileSwitchTimerHandle != NULL) && (ThreadpoolMgr::DeleteTimerQueueTimer(s_fileSwitchTimerHandle, NULL)))
492 s_fileSwitchTimerHandle = NULL;
495 void WINAPI EventPipe::FlushTimer(PVOID parameter, BOOLEAN timerFired)
502 PRECONDITION(timerFired);
506 // Take the lock control lock to make sure that tracing isn't disabled during this operation.
507 CrstHolder _crst(GetLock());
509 if (s_pSession == nullptr || s_pFile == nullptr)
512 // Make sure that we should actually switch files.
513 if (!Enabled() || s_pSession->GetSessionType() != EventPipeSessionType::IpcStream)
518 if (CLRGetTickCount64() > (s_lastFlushSwitchTime + 100))
520 // Get the current time stamp.
521 // WriteAllBuffersToFile will use this to ensure that no events after
522 // the current timestamp are written into the file.
523 LARGE_INTEGER stopTimeStamp;
524 QueryPerformanceCounter(&stopTimeStamp);
525 s_pBufferManager->WriteAllBuffersToFile(s_pFile, stopTimeStamp);
527 s_lastFlushSwitchTime = CLRGetTickCount64();
531 EventPipeSession *EventPipe::GetSession(EventPipeSessionID id)
533 LIMITED_METHOD_CONTRACT;
535 EventPipeSession *pSession = NULL;
536 if ((EventPipeSessionID)s_pSession == id)
538 pSession = s_pSession;
543 bool EventPipe::Enabled()
545 LIMITED_METHOD_CONTRACT;
547 bool enabled = false;
548 if (s_pConfig != NULL)
550 enabled = s_pConfig->Enabled();
556 EventPipeProvider *EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
566 EventPipeProvider *pProvider = NULL;
567 if (s_pConfig != NULL)
569 pProvider = s_pConfig->CreateProvider(providerName, pCallbackFunction, pCallbackData);
575 EventPipeProvider *EventPipe::GetProvider(const SString &providerName)
585 EventPipeProvider *pProvider = NULL;
586 if (s_pConfig != NULL)
588 pProvider = s_pConfig->GetProvider(providerName);
594 void EventPipe::DeleteProvider(EventPipeProvider *pProvider)
604 // Take the lock to make sure that we don't have a race
605 // between disabling tracing and deleting a provider
606 // where we hold a provider after tracing has been disabled.
607 CrstHolder _crst(GetLock());
609 if (pProvider != NULL)
613 // Save the provider until the end of the tracing session.
614 pProvider->SetDeleteDeferred();
618 // Delete the provider now.
619 if (s_pConfig != NULL)
621 s_pConfig->DeleteProvider(pProvider);
627 void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId, LPCGUID pRelatedActivityId)
637 EventPipeEventPayload payload(pData, length);
638 EventPipe::WriteEventInternal(event, payload, pActivityId, pRelatedActivityId);
641 void EventPipe::WriteEvent(EventPipeEvent &event, EventData *pEventData, unsigned int eventDataCount, LPCGUID pActivityId, LPCGUID pRelatedActivityId)
651 EventPipeEventPayload payload(pEventData, eventDataCount);
652 EventPipe::WriteEventInternal(event, payload, pActivityId, pRelatedActivityId);
655 void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId)
665 // Exit early if the event is not enabled.
666 if (!event.IsEnabled())
671 // Get the current thread;
672 Thread *pThread = GetThread();
674 if (s_pConfig == NULL)
676 // We can't procede without a configuration
679 _ASSERTE(s_pSession != NULL);
681 // If the activity id isn't specified AND we are in a managed thread, pull it from the current thread.
682 // If pThread is NULL (we aren't in writing from a managed thread) then pActivityId can be NULL
683 if (pActivityId == NULL && pThread != NULL)
685 pActivityId = pThread->GetActivityId();
688 if (!s_pConfig->RundownEnabled() && s_pBufferManager != NULL)
690 s_pBufferManager->WriteEvent(pThread, *s_pSession, event, payload, pActivityId, pRelatedActivityId);
692 else if (s_pConfig->RundownEnabled())
694 // It is possible that some events that are enabled on rundown can be emitted from other threads.
695 // We're not interested in these events and they can cause corrupted trace files because rundown
696 // events are written synchronously and not under lock.
697 // If we encounter an event that did not originate on the thread that is doing rundown, ignore it.
698 if (pThread == NULL || !s_pConfig->IsRundownThread(pThread))
703 BYTE *pData = payload.GetFlatData();
706 // Write synchronously to the file.
707 // We're under lock and blocking the disabling thread.
708 // This copy occurs here (rather than at file write) because
709 // A) The FastSerializer API would need to change if we waited
710 // B) It is unclear there is a benefit to multiple file write calls
711 // as opposed a a buffer copy here
712 EventPipeEventInstance instance(
715 pThread->GetOSThreadId(),
720 instance.EnsureStack(*s_pSession);
724 // EventPipeFile::WriteEvent needs to allocate a metadata event
725 // and can therefore throw. In this context we will silently
726 // fail rather than disrupt the caller
729 s_pFile->WriteEvent(instance);
732 EX_END_CATCH(SwallowAllExceptions);
738 void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData, unsigned int length)
748 EventPipeEventPayload payload(pData, length);
750 // Write the event to the thread's buffer.
751 if (s_pBufferManager != NULL)
753 // Specify the sampling thread as the "current thread", so that we select the right buffer.
754 // Specify the target thread so that the event gets properly attributed.
755 s_pBufferManager->WriteEvent(pSamplingThread, *s_pSession, *pEvent, payload, NULL /* pActivityId */, NULL /* pRelatedActivityId */, pTargetThread, &stackContents);
759 bool EventPipe::WalkManagedStackForCurrentThread(StackContents &stackContents)
769 Thread *pThread = GetThread();
772 return WalkManagedStackForThread(pThread, stackContents);
778 bool EventPipe::WalkManagedStackForThread(Thread *pThread, StackContents &stackContents)
785 PRECONDITION(pThread != NULL);
789 // Calling into StackWalkFrames in preemptive mode violates the host contract,
790 // but this contract is not used on CoreCLR.
791 CONTRACT_VIOLATION(HostViolation);
793 stackContents.Reset();
795 StackWalkAction swaRet = pThread->StackWalkFrames(
796 (PSTACKWALKFRAMESCALLBACK)&StackWalkCallback,
798 ALLOW_ASYNC_STACK_WALK | FUNCTIONSONLY | HANDLESKIPPEDFRAMES | ALLOW_INVALID_OBJECTS);
800 return ((swaRet == SWA_DONE) || (swaRet == SWA_CONTINUE));
803 StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pData)
810 PRECONDITION(pCf != NULL);
811 PRECONDITION(pData != NULL);
816 UINT_PTR controlPC = (UINT_PTR)pCf->GetRegisterSet()->ControlPC;
819 if (pData->GetLength() == 0)
821 // This happens for pinvoke stubs on the top of the stack.
826 _ASSERTE(controlPC != 0);
828 // Add the IP to the captured stack.
833 // Continue the stack walk.
837 void EventPipe::SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv)
844 PRECONDITION(pwzAssemblyPath != NULL);
845 PRECONDITION(argc <= 0 || argv != NULL);
849 // Get the command line.
850 LPCWSTR osCommandLine = GetCommandLineW();
853 // On Windows, osCommandLine contains the executable and all arguments.
854 s_pCommandLine = osCommandLine;
856 // On UNIX, the PAL doesn't have the command line arguments, so we must build the command line.
857 // osCommandLine contains the full path to the executable.
858 SString commandLine(osCommandLine);
859 commandLine.Append((WCHAR)' ');
860 commandLine.Append(pwzAssemblyPath);
862 for (int i = 0; i < argc; i++)
864 commandLine.Append((WCHAR)' ');
865 commandLine.Append(argv[i]);
868 // Allocate a new string for the command line.
869 SIZE_T commandLineLen = commandLine.GetCount();
870 WCHAR *pCommandLine = new WCHAR[commandLineLen + 1];
871 wcsncpy(pCommandLine, commandLine.GetUnicode(), commandLineLen);
872 pCommandLine[commandLineLen] = '\0';
874 s_pCommandLine = pCommandLine;
878 EventPipeEventInstance *EventPipe::GetNextEvent()
888 EventPipeEventInstance *pInstance = NULL;
890 // Only fetch the next event if a tracing session exists.
891 // The buffer manager is not disposed until the process is shutdown.
892 if (s_pSession != NULL)
894 pInstance = s_pBufferManager->GetNextEvent();
900 #endif // FEATURE_PERFTRACING