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.
6 #include "eventpipebuffermanager.h"
7 #include "eventpipeeventinstance.h"
8 #include "sampleprofiler.h"
10 #include "threadsuspend.h"
12 #ifdef FEATURE_PERFTRACING
14 Volatile<BOOL> SampleProfiler::s_profilingEnabled = false;
15 Thread* SampleProfiler::s_pSamplingThread = NULL;
16 const WCHAR* SampleProfiler::s_providerName = W("Microsoft-DotNETCore-SampleProfiler");
17 EventPipeProvider* SampleProfiler::s_pEventPipeProvider = NULL;
18 EventPipeEvent* SampleProfiler::s_pThreadTimeEvent = NULL;
19 BYTE* SampleProfiler::s_pPayloadExternal = NULL;
20 BYTE* SampleProfiler::s_pPayloadManaged = NULL;
21 CLREventStatic SampleProfiler::s_threadShutdownEvent;
22 long SampleProfiler::s_samplingRateInNs = 1000000; // 1ms
24 void SampleProfiler::Enable()
31 PRECONDITION(s_pSamplingThread == NULL);
32 // Synchronization of multiple callers occurs in EventPipe::Enable.
33 PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
37 if(s_pEventPipeProvider == NULL)
39 s_pEventPipeProvider = EventPipe::CreateProvider(SL(s_providerName));
40 s_pThreadTimeEvent = s_pEventPipeProvider->AddEvent(
44 EventPipeEventLevel::Informational,
45 false /* NeedStack */);
48 if(s_pPayloadExternal == NULL)
50 s_pPayloadExternal = new BYTE[sizeof(unsigned int)];
51 *((unsigned int *)s_pPayloadExternal) = static_cast<unsigned int>(SampleProfilerSampleType::External);
53 s_pPayloadManaged = new BYTE[sizeof(unsigned int)];
54 *((unsigned int *)s_pPayloadManaged) = static_cast<unsigned int>(SampleProfilerSampleType::Managed);
57 s_profilingEnabled = true;
58 s_pSamplingThread = SetupUnstartedThread();
59 if(s_pSamplingThread->CreateNewThread(0, ThreadProc, NULL))
61 // Start the sampling thread.
62 s_pSamplingThread->SetBackground(TRUE);
63 s_pSamplingThread->StartThread();
67 _ASSERT(!"Unable to create sample profiler thread.");
71 void SampleProfiler::Disable()
78 // Synchronization of multiple callers occurs in EventPipe::Disable.
79 PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
83 // Bail early if profiling is not enabled.
84 if(!s_profilingEnabled)
89 // Reset the event before shutdown.
90 s_threadShutdownEvent.Reset();
92 // The sampling thread will watch this value and exit
93 // when profiling is disabled.
94 s_profilingEnabled = false;
96 // Wait for the sampling thread to clean itself up.
97 s_threadShutdownEvent.Wait(0, FALSE /* bAlertable */);
100 void SampleProfiler::SetSamplingRate(long nanoseconds)
102 LIMITED_METHOD_CONTRACT;
103 s_samplingRateInNs = nanoseconds;
106 DWORD WINAPI SampleProfiler::ThreadProc(void *args)
113 PRECONDITION(s_pSamplingThread != NULL);
117 // Complete thread initialization and start the profiling loop.
118 if(s_pSamplingThread->HasStarted())
120 // Switch to pre-emptive mode so that this thread doesn't starve the GC.
123 while(s_profilingEnabled)
125 // Check to see if we can suspend managed execution.
126 if(ThreadSuspend::SysIsSuspendInProgress() || (ThreadSuspend::GetSuspensionThread() != 0))
128 // Skip the current sample.
129 PAL_nanosleep(s_samplingRateInNs);
133 // Actually suspend managed execution.
134 ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_REASON::SUSPEND_OTHER);
136 // Walk all managed threads and capture stacks.
137 WalkManagedThreads();
139 // Resume managed execution.
140 ThreadSuspend::RestartEE(FALSE /* bFinishedGC */, TRUE /* SuspendSucceeded */);
142 // Wait until it's time to sample again.
143 PAL_nanosleep(s_samplingRateInNs);
147 // Destroy the sampling thread when it is done running.
148 DestroyThread(s_pSamplingThread);
149 s_pSamplingThread = NULL;
151 // Signal Disable() that the thread has been destroyed.
152 s_threadShutdownEvent.Set();
157 // The thread store lock must already be held by the thread before this function
158 // is called. ThreadSuspend::SuspendEE acquires the thread store lock.
159 void SampleProfiler::WalkManagedThreads()
169 Thread *pTargetThread = NULL;
171 // Iterate over all managed threads.
172 // Assumes that the ThreadStoreLock is held because we've suspended all threads.
173 while ((pTargetThread = ThreadStore::GetThreadList(pTargetThread)) != NULL)
175 StackContents stackContents;
177 // Walk the stack and write it out as an event.
178 if(EventPipe::WalkManagedStackForThread(pTargetThread, stackContents) && !stackContents.IsEmpty())
180 // Set the payload. If the GC mode on suspension > 0, then the thread was in cooperative mode.
181 // Even though there are some cases where this is not managed code, we assume it is managed code here.
182 // If the GC mode on suspension == 0 then the thread was in preemptive mode, which we qualify as external here.
183 BYTE *pPayload = s_pPayloadExternal;
184 if(pTargetThread->GetGCModeOnSuspension())
186 pPayload = s_pPayloadManaged;
190 EventPipe::WriteSampleProfileEvent(s_pSamplingThread, s_pThreadTimeEvent, pTargetThread, stackContents, pPayload, c_payloadSize);
193 // Reset the GC mode.
194 pTargetThread->ClearGCModeOnSuspension();
198 #endif // FEATURE_PERFTRACING