Merge pull request #14619 from briansull/emitter-cleanup
[platform/upstream/coreclr.git] / src / vm / sampleprofiler.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 "eventpipebuffermanager.h"
7 #include "eventpipeeventinstance.h"
8 #include "sampleprofiler.h"
9 #include "hosting.h"
10 #include "threadsuspend.h"
11
12 #ifdef FEATURE_PERFTRACING
13
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
23
24 void SampleProfiler::Enable()
25 {
26     CONTRACTL
27     {
28         THROWS;
29         GC_TRIGGERS;
30         MODE_ANY;
31         PRECONDITION(s_pSamplingThread == NULL);
32         // Synchronization of multiple callers occurs in EventPipe::Enable.
33         PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
34     }
35     CONTRACTL_END;
36
37     if(s_pEventPipeProvider == NULL)
38     {
39         s_pEventPipeProvider = EventPipe::CreateProvider(SL(s_providerName));
40         s_pThreadTimeEvent = s_pEventPipeProvider->AddEvent(
41             0, /* eventID */
42             0, /* keywords */
43             0, /* eventVersion */
44             EventPipeEventLevel::Informational,
45             false /* NeedStack */);
46     }
47
48     if(s_pPayloadExternal == NULL)
49     {
50         s_pPayloadExternal = new BYTE[sizeof(unsigned int)];
51         *((unsigned int *)s_pPayloadExternal) = static_cast<unsigned int>(SampleProfilerSampleType::External);
52
53         s_pPayloadManaged = new BYTE[sizeof(unsigned int)];
54         *((unsigned int *)s_pPayloadManaged) = static_cast<unsigned int>(SampleProfilerSampleType::Managed);
55     }
56
57     s_profilingEnabled = true;
58     s_pSamplingThread = SetupUnstartedThread();
59     if(s_pSamplingThread->CreateNewThread(0, ThreadProc, NULL))
60     {
61         // Start the sampling thread.
62         s_pSamplingThread->SetBackground(TRUE);
63         s_pSamplingThread->StartThread();
64     }
65     else
66     {
67         _ASSERT(!"Unable to create sample profiler thread.");
68     }
69 }
70
71 void SampleProfiler::Disable()
72 {
73     CONTRACTL
74     {
75         THROWS;
76         GC_TRIGGERS;
77         MODE_ANY;
78         // Synchronization of multiple callers occurs in EventPipe::Disable.
79         PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
80     }
81     CONTRACTL_END;
82
83     // Bail early if profiling is not enabled.
84     if(!s_profilingEnabled)
85     {
86         return;
87     }
88
89     // Reset the event before shutdown.
90     s_threadShutdownEvent.Reset();
91
92     // The sampling thread will watch this value and exit
93     // when profiling is disabled.
94     s_profilingEnabled = false;
95
96     // Wait for the sampling thread to clean itself up.
97     s_threadShutdownEvent.Wait(0, FALSE /* bAlertable */);
98 }
99
100 void SampleProfiler::SetSamplingRate(long nanoseconds)
101 {
102     LIMITED_METHOD_CONTRACT;
103     s_samplingRateInNs = nanoseconds;
104 }
105
106 DWORD WINAPI SampleProfiler::ThreadProc(void *args)
107 {
108     CONTRACTL
109     {
110         NOTHROW;
111         GC_TRIGGERS;
112         MODE_PREEMPTIVE;
113         PRECONDITION(s_pSamplingThread != NULL);
114     }
115     CONTRACTL_END;
116
117     // Complete thread initialization and start the profiling loop.
118     if(s_pSamplingThread->HasStarted())
119     {
120         // Switch to pre-emptive mode so that this thread doesn't starve the GC.
121         GCX_PREEMP();
122
123         while(s_profilingEnabled)
124         {
125             // Check to see if we can suspend managed execution.
126             if(ThreadSuspend::SysIsSuspendInProgress() || (ThreadSuspend::GetSuspensionThread() != 0))
127             {
128                 // Skip the current sample.
129                 PAL_nanosleep(s_samplingRateInNs);
130                 continue;
131             }
132
133             // Actually suspend managed execution.
134             ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_REASON::SUSPEND_OTHER);
135
136             // Walk all managed threads and capture stacks.
137             WalkManagedThreads();
138
139             // Resume managed execution.
140             ThreadSuspend::RestartEE(FALSE /* bFinishedGC */, TRUE /* SuspendSucceeded */);
141
142             // Wait until it's time to sample again.
143             PAL_nanosleep(s_samplingRateInNs);
144         }
145     }
146
147     // Destroy the sampling thread when it is done running.
148     DestroyThread(s_pSamplingThread);
149     s_pSamplingThread = NULL;
150
151     // Signal Disable() that the thread has been destroyed.
152     s_threadShutdownEvent.Set();
153  
154     return S_OK;
155 }
156
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()
160 {
161     CONTRACTL
162     {
163         NOTHROW;
164         GC_TRIGGERS;
165         MODE_PREEMPTIVE;
166     }
167     CONTRACTL_END;
168
169     Thread *pTargetThread = NULL;
170
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)
174     {
175         StackContents stackContents;
176
177         // Walk the stack and write it out as an event.
178         if(EventPipe::WalkManagedStackForThread(pTargetThread, stackContents) && !stackContents.IsEmpty())
179         {
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())
185             {
186                 pPayload = s_pPayloadManaged;
187             }
188
189             // Write the sample.
190             EventPipe::WriteSampleProfileEvent(s_pSamplingThread, s_pThreadTimeEvent, pTargetThread, stackContents, pPayload, c_payloadSize);
191         }
192
193         // Reset the GC mode.
194         pTargetThread->ClearGCModeOnSuspension();
195     }
196 }
197
198 #endif // FEATURE_PERFTRACING