From cd88c6919e75617fad6a6c6e772ee16f09d53ac2 Mon Sep 17 00:00:00 2001 From: Brian Robbins Date: Wed, 24 May 2017 14:43:54 -0700 Subject: [PATCH] Indicate if thread time events are in managed or native code. (#11892) --- src/vm/eventpipe.cpp | 6 +++--- src/vm/eventpipe.h | 2 +- src/vm/eventpipeeventinstance.cpp | 4 ++-- src/vm/eventpipeeventinstance.h | 2 +- src/vm/sampleprofiler.cpp | 26 +++++++++++++++++++++++++- src/vm/sampleprofiler.h | 14 ++++++++++++++ src/vm/threads.h | 21 +++++++++++++++++++++ src/vm/threadsuspend.cpp | 5 +++++ 8 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp index e7bef23..65af646 100644 --- a/src/vm/eventpipe.cpp +++ b/src/vm/eventpipe.cpp @@ -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. diff --git a/src/vm/eventpipe.h b/src/vm/eventpipe.h index 092508f..0860080 100644 --- a/src/vm/eventpipe.h +++ b/src/vm/eventpipe.h @@ -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); diff --git a/src/vm/eventpipeeventinstance.cpp b/src/vm/eventpipeeventinstance.cpp index 4527ed5..9372cec 100644 --- a/src/vm/eventpipeeventinstance.cpp +++ b/src/vm/eventpipeeventinstance.cpp @@ -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; } diff --git a/src/vm/eventpipeeventinstance.h b/src/vm/eventpipeeventinstance.h index 7e0ea47..f54a894 100644 --- a/src/vm/eventpipeeventinstance.h +++ b/src/vm/eventpipeeventinstance.h @@ -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 diff --git a/src/vm/sampleprofiler.cpp b/src/vm/sampleprofiler.cpp index bec757f..e472157 100644 --- a/src/vm/sampleprofiler.cpp +++ b/src/vm/sampleprofiler.cpp @@ -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(SampleProfilerSampleType::External); + + s_pPayloadManaged = new BYTE[sizeof(unsigned int)]; + *((unsigned int *)s_pPayloadManaged) = static_cast(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(); } } diff --git a/src/vm/sampleprofiler.h b/src/vm/sampleprofiler.h index 1992e1c..02eb6b3 100644 --- a/src/vm/sampleprofiler.h +++ b/src/vm/sampleprofiler.h @@ -10,6 +10,13 @@ #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; diff --git a/src/vm/threads.h b/src/vm/threads.h index 2c0ce93..a53a4a1 100644 --- a/src/vm/threads.h +++ b/src/vm/threads.h @@ -5289,6 +5289,10 @@ private: // Whether or not the thread is currently writing an event. Volatile 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 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 diff --git a/src/vm/threadsuspend.cpp b/src/vm/threadsuspend.cpp index 6549594..36eb9f3 100644 --- a/src/vm/threadsuspend.cpp +++ b/src/vm/threadsuspend.cpp @@ -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 -- 2.7.4