#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
{
{
// 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;
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.
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);
}
#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;
}
public:
- SampleProfilerEventInstance(Thread *pThread);
+ SampleProfilerEventInstance(EventPipeEvent &event, Thread *pThread, BYTE *pData, unsigned int length);
};
#endif // FEATURE_PERFTRACING
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
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))
// 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();
}
}
#include "common.h"
#include "eventpipe.h"
+enum class SampleProfilerSampleType
+{
+ Error = 0,
+ External = 1,
+ Managed = 2
+};
+
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;
// 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()
{
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
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