Indicate if thread time events are in managed or native code. (dotnet/coreclr#11892)
authorBrian Robbins <brianrob@microsoft.com>
Wed, 24 May 2017 21:43:54 +0000 (14:43 -0700)
committerGitHub <noreply@github.com>
Wed, 24 May 2017 21:43:54 +0000 (14:43 -0700)
Commit migrated from https://github.com/dotnet/coreclr/commit/cd88c6919e75617fad6a6c6e772ee16f09d53ac2

src/coreclr/src/vm/eventpipe.cpp
src/coreclr/src/vm/eventpipe.h
src/coreclr/src/vm/eventpipeeventinstance.cpp
src/coreclr/src/vm/eventpipeeventinstance.h
src/coreclr/src/vm/sampleprofiler.cpp
src/coreclr/src/vm/sampleprofiler.h
src/coreclr/src/vm/threads.h
src/coreclr/src/vm/threadsuspend.cpp

index e7bef23..65af646 100644 (file)
@@ -357,7 +357,7 @@ void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int leng
 #endif // _DEBUG
 }
 
-void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, Thread *pTargetThread, StackContents &stackContents)
+void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData, unsigned int length)
 {
     CONTRACTL
     {
@@ -372,7 +372,7 @@ void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, Thread *pTarget
     {
         // Specify the sampling thread as the "current thread", so that we select the right buffer.
         // Specify the target thread so that the event gets properly attributed.
-        if(!s_pBufferManager->WriteEvent(pSamplingThread, *SampleProfiler::s_pThreadTimeEvent, NULL, 0, pTargetThread, &stackContents))
+        if(!s_pBufferManager->WriteEvent(pSamplingThread, *pEvent, pData, length, pTargetThread, &stackContents))
         {
             // This is used in DEBUG to make sure that we don't log an event synchronously that we didn't log to the buffer.
             return;
@@ -384,7 +384,7 @@ void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, Thread *pTarget
         GCX_PREEMP();
 
         // Create an instance for the synchronous path.
-        SampleProfilerEventInstance instance(pTargetThread);
+        SampleProfilerEventInstance instance(*pEvent, pTargetThread, pData, length);
         stackContents.CopyTo(instance.GetStack());
 
         // Write to the EventPipeFile.
index 092508f..0860080 100644 (file)
@@ -191,7 +191,7 @@ class EventPipe
         static void WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length);
 
         // Write out a sample profile event.
-        static void WriteSampleProfileEvent(Thread *pSamplingThread, Thread *pTargetThread, StackContents &stackContents);
+        static void WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData = NULL, unsigned int length = 0);
         
         // Get the managed call stack for the current thread.
         static bool WalkManagedStackForCurrentThread(StackContents &stackContents);
index 4527ed5..9372cec 100644 (file)
@@ -198,8 +198,8 @@ bool EventPipeEventInstance::EnsureConsistency()
 }
 #endif // _DEBUG
 
-SampleProfilerEventInstance::SampleProfilerEventInstance(Thread *pThread)
-    :EventPipeEventInstance(*SampleProfiler::s_pThreadTimeEvent, pThread->GetOSThreadId(), NULL, 0)
+SampleProfilerEventInstance::SampleProfilerEventInstance(EventPipeEvent &event, Thread *pThread, BYTE *pData, unsigned int length)
+    :EventPipeEventInstance(event, pThread->GetOSThreadId(), pData, length)
 {
     LIMITED_METHOD_CONTRACT;
 }
index 7e0ea47..f54a894 100644 (file)
@@ -80,7 +80,7 @@ class SampleProfilerEventInstance : public EventPipeEventInstance
 
 public:
 
-    SampleProfilerEventInstance(Thread *pThread);
+    SampleProfilerEventInstance(EventPipeEvent &event, Thread *pThread, BYTE *pData, unsigned int length);
 };
 
 #endif // FEATURE_PERFTRACING
index bec757f..e472157 100644 (file)
@@ -16,6 +16,8 @@ Thread* SampleProfiler::s_pSamplingThread = NULL;
 const GUID SampleProfiler::s_providerID = {0x3c530d44,0x97ae,0x513a,{0x1e,0x6d,0x78,0x3e,0x8f,0x8e,0x03,0xa9}}; // {3c530d44-97ae-513a-1e6d-783e8f8e03a9}
 EventPipeProvider* SampleProfiler::s_pEventPipeProvider = NULL;
 EventPipeEvent* SampleProfiler::s_pThreadTimeEvent = NULL;
+BYTE* SampleProfiler::s_pPayloadExternal = NULL;
+BYTE* SampleProfiler::s_pPayloadManaged = NULL;
 CLREventStatic SampleProfiler::s_threadShutdownEvent;
 long SampleProfiler::s_samplingRateInNs = 1000000; // 1ms
 
@@ -43,6 +45,15 @@ void SampleProfiler::Enable()
             false /* NeedStack */);
     }
 
+    if(s_pPayloadExternal == NULL)
+    {
+        s_pPayloadExternal = new BYTE[sizeof(unsigned int)];
+        *((unsigned int *)s_pPayloadExternal) = static_cast<unsigned int>(SampleProfilerSampleType::External);
+
+        s_pPayloadManaged = new BYTE[sizeof(unsigned int)];
+        *((unsigned int *)s_pPayloadManaged) = static_cast<unsigned int>(SampleProfilerSampleType::Managed);
+    }
+
     s_profilingEnabled = true;
     s_pSamplingThread = SetupUnstartedThread();
     if(s_pSamplingThread->CreateNewThread(0, ThreadProc, NULL))
@@ -166,8 +177,21 @@ void SampleProfiler::WalkManagedThreads()
         // Walk the stack and write it out as an event.
         if(EventPipe::WalkManagedStackForThread(pTargetThread, stackContents) && !stackContents.IsEmpty())
         {
-            EventPipe::WriteSampleProfileEvent(s_pSamplingThread, pTargetThread, stackContents);
+            // Set the payload.  If the GC mode on suspension > 0, then the thread was in cooperative mode.
+            // Even though there are some cases where this is not managed code, we assume it is managed code here.
+            // If the GC mode on suspension == 0 then the thread was in preemptive mode, which we qualify as external here.
+            BYTE *pPayload = s_pPayloadExternal;
+            if(pTargetThread->GetGCModeOnSuspension())
+            {
+                pPayload = s_pPayloadManaged;
+            }
+
+            // Write the sample.
+            EventPipe::WriteSampleProfileEvent(s_pSamplingThread, s_pThreadTimeEvent, pTargetThread, stackContents, pPayload, c_payloadSize);
         }
+
+        // Reset the GC mode.
+        pTargetThread->ClearGCModeOnSuspension();
     }
 }
 
index 1992e1c..02eb6b3 100644 (file)
 #include "common.h"
 #include "eventpipe.h"
 
+enum class SampleProfilerSampleType
+{
+    Error = 0,
+    External = 1,
+    Managed = 2
+};
+
 class SampleProfiler
 {
 
@@ -47,6 +54,13 @@ class SampleProfiler
         static EventPipeProvider *s_pEventPipeProvider;
         static EventPipeEvent *s_pThreadTimeEvent;
 
+        // Event payloads.
+        // External represents a sample in external or native code.
+        // Managed represents a sample in managed code.
+        static BYTE *s_pPayloadExternal;
+        static BYTE *s_pPayloadManaged;
+        static const unsigned int c_payloadSize = sizeof(unsigned int);
+
         // Thread shutdown event for synchronization between Disable() and the sampling thread.
         static CLREventStatic s_threadShutdownEvent;
 
index 2c0ce93..a53a4a1 100644 (file)
@@ -5289,6 +5289,10 @@ private:
     // Whether or not the thread is currently writing an event.
     Volatile<bool> m_eventWriteInProgress;
 
+    // SampleProfiler thread state.  This is set on suspension and cleared before restart.
+    // True if the thread was in cooperative mode.  False if it was in preemptive when the suspension started.
+    Volatile<ULONG> m_gcModeOnSuspension;
+
 public:
     EventPipeBufferList* GetEventPipeBufferList()
     {
@@ -5313,6 +5317,23 @@ public:
         LIMITED_METHOD_CONTRACT;
         m_eventWriteInProgress = value;
     }
+
+    bool GetGCModeOnSuspension()
+    {
+        LIMITED_METHOD_CONTRACT;
+        return m_gcModeOnSuspension;
+    }
+
+    void SaveGCModeOnSuspension()
+    {
+        LIMITED_METHOD_CONTRACT;
+        m_gcModeOnSuspension = m_fPreemptiveGCDisabled;
+    }
+
+    void ClearGCModeOnSuspension()
+    {
+        m_gcModeOnSuspension = 0;
+    }
 #endif // FEATURE_PERFTRACING
 
 #ifdef FEATURE_HIJACK
index 6549594..36eb9f3 100644 (file)
@@ -7514,6 +7514,11 @@ void HandleGCSuspensionForInterruptedThread(CONTEXT *interruptedContext)
     if (pThread->PreemptiveGCDisabled() != TRUE)
         return;
 
+#ifdef FEATURE_PERFTRACING
+    // Mark that the thread is currently in managed code.
+    pThread->SaveGCModeOnSuspension();
+#endif // FEATURE_PERFTRACING
+
     PCODE ip = GetIP(interruptedContext);
 
     // This function can only be called when the interrupted thread is in