EventPipe Circular Buffer Support and Ability to Start/Stop Tracing (#11507)
authorBrian Robbins <brianrob@microsoft.com>
Fri, 12 May 2017 17:51:31 +0000 (10:51 -0700)
committerGitHub <noreply@github.com>
Fri, 12 May 2017 17:51:31 +0000 (10:51 -0700)
26 files changed:
src/mscorlib/System.Private.CoreLib.csproj
src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs [new file with mode: 0644]
src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs
src/vm/CMakeLists.txt
src/vm/ecalllist.h
src/vm/eventpipe.cpp
src/vm/eventpipe.h
src/vm/eventpipebuffer.cpp [new file with mode: 0644]
src/vm/eventpipebuffer.h [new file with mode: 0644]
src/vm/eventpipebuffermanager.cpp [new file with mode: 0644]
src/vm/eventpipebuffermanager.h [new file with mode: 0644]
src/vm/eventpipeconfiguration.cpp
src/vm/eventpipeconfiguration.h
src/vm/eventpipeevent.h
src/vm/eventpipeeventinstance.cpp
src/vm/eventpipeeventinstance.h
src/vm/eventpipefile.cpp
src/vm/eventpipefile.h
src/vm/eventpipejsonfile.cpp
src/vm/eventpipejsonfile.h
src/vm/eventpipeprovider.cpp
src/vm/eventpipeprovider.h
src/vm/sampleprofiler.cpp
src/vm/sampleprofiler.h
src/vm/threads.cpp
src/vm/threads.h

index 3373097..75d5ab8 100644 (file)
     <Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventSource_CoreCLR.cs" />
     <Compile Condition="'$(FeatureXplatEventSource)' == 'true'" Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\XplatEventLogger.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\FrameworkEventSource.cs" />
+    <Compile Condition="'$(FeaturePerfTracing)' == 'true'" Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipe.cs" />
     <Compile Condition="'$(FeaturePerfTracing)' == 'true'" Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipeEventProvider.cs" />
   </ItemGroup>
   <ItemGroup>
     <Win32Resource Condition="'$(GenerateNativeVersionInfo)'=='true'">$(IntermediateOutputPath)\System.Private.CoreLib.res</Win32Resource>
   </PropertyGroup>
   <Import Project="GenerateCompilerResponseFile.targets" />
-</Project>
\ No newline at end of file
+</Project>
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs
new file mode 100644 (file)
index 0000000..4c6778f
--- /dev/null
@@ -0,0 +1,157 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+using Microsoft.Win32;
+
+namespace System.Diagnostics.Tracing
+{
+    [StructLayout(LayoutKind.Sequential)]
+    internal struct EventPipeProviderConfiguration
+    {
+        [MarshalAs(UnmanagedType.LPWStr)]
+        private string m_providerName;
+        private UInt64 m_keywords;
+        private uint m_loggingLevel;
+
+        internal EventPipeProviderConfiguration(
+            string providerName,
+            UInt64 keywords,
+            uint loggingLevel)
+        {
+            if(string.IsNullOrEmpty(providerName))
+            {
+                throw new ArgumentNullException(nameof(providerName));
+            }
+            if(loggingLevel > 5) // 5 == Verbose, the highest value in EventPipeLoggingLevel.
+            {
+                throw new ArgumentOutOfRangeException(nameof(loggingLevel));
+            }
+            m_providerName = providerName;
+            m_keywords = keywords;
+            m_loggingLevel = loggingLevel;
+        }
+
+        internal string ProviderName
+        {
+            get { return m_providerName; }
+        }
+
+        internal UInt64 Keywords
+        {
+            get { return m_keywords; }
+        }
+
+        internal uint LoggingLevel
+        {
+            get { return m_loggingLevel; }
+        }
+    }
+
+    internal sealed class EventPipeConfiguration
+    {
+        private string m_outputFile;
+        private uint m_circularBufferSizeInMB;
+        private List<EventPipeProviderConfiguration> m_providers;
+
+        internal EventPipeConfiguration(
+            string outputFile,
+            uint circularBufferSizeInMB)
+        {
+            if(string.IsNullOrEmpty(outputFile))
+            {
+                throw new ArgumentNullException(nameof(outputFile));
+            }
+            if(circularBufferSizeInMB == 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(circularBufferSizeInMB));
+            }
+            m_outputFile = outputFile;
+            m_circularBufferSizeInMB = circularBufferSizeInMB;
+            m_providers = new List<EventPipeProviderConfiguration>();
+        }
+
+        internal string OutputFile
+        {
+            get { return m_outputFile; }
+        }
+
+        internal uint CircularBufferSizeInMB
+        {
+            get { return m_circularBufferSizeInMB; }
+        }
+
+        internal EventPipeProviderConfiguration[] Providers
+        {
+            get { return m_providers.ToArray(); }
+        }
+
+        internal void EnableProvider(string providerName, UInt64 keywords, uint loggingLevel)
+        {
+            m_providers.Add(new EventPipeProviderConfiguration(
+                providerName,
+                keywords,
+                loggingLevel));
+        }
+    }
+
+    internal static class EventPipe
+    {
+        internal static void Enable(EventPipeConfiguration configuration)
+        {
+            if(configuration == null)
+            {
+                throw new ArgumentNullException(nameof(configuration));
+            }
+
+            EventPipeProviderConfiguration[] providers = configuration.Providers;
+
+            EventPipeInternal.Enable(
+                configuration.OutputFile,
+                configuration.CircularBufferSizeInMB,
+                providers,
+                providers.Length);
+        }
+
+        internal static void Disable()
+        {
+            EventPipeInternal.Disable();
+        }
+    }
+
+    internal static class EventPipeInternal
+    {
+        //
+        // These PInvokes are used by the configuration APIs to interact with EventPipe.
+        //
+        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+        [SuppressUnmanagedCodeSecurity]
+        internal static extern void Enable(string outputFile, uint circularBufferSizeInMB, EventPipeProviderConfiguration[] providers, int numProviders);
+
+        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+        [SuppressUnmanagedCodeSecurity]
+        internal static extern void Disable();
+
+        //
+        // These PInvokes are used by EventSource to interact with the EventPipe.
+        //
+        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+        [SuppressUnmanagedCodeSecurity]
+        internal static extern IntPtr CreateProvider(Guid providerID, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback callbackFunc);
+
+        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+        [SuppressUnmanagedCodeSecurity]
+        internal static extern IntPtr AddEvent(IntPtr provHandle, Int64 keywords, uint eventID, uint eventVersion, uint level, bool needStack);
+
+        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+        [SuppressUnmanagedCodeSecurity]
+        internal static extern void DeleteProvider(IntPtr provHandle);
+
+        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+        [SuppressUnmanagedCodeSecurity]
+        internal static extern unsafe void WriteEvent(IntPtr eventHandle, void* data, uint length);
+    }
+}
index 5917ecc..42eb42a 100644 (file)
@@ -63,24 +63,4 @@ namespace System.Diagnostics.Tracing
             return 0;
         }
     }
-
-    // PInvokes into the runtime used to interact with the EventPipe.
-    internal static class EventPipeInternal
-    {
-        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
-        [SuppressUnmanagedCodeSecurity]
-        internal static extern IntPtr CreateProvider(Guid providerID, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback callbackFunc);
-
-        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
-        [SuppressUnmanagedCodeSecurity]
-        internal static extern IntPtr AddEvent(IntPtr provHandle, Int64 keywords, uint eventID, uint eventVersion, uint level, bool needStack);
-
-        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
-        [SuppressUnmanagedCodeSecurity]
-        internal static extern void DeleteProvider(IntPtr provHandle);
-
-        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
-        [SuppressUnmanagedCodeSecurity]
-        internal static extern unsafe void WriteEvent(IntPtr eventHandle, void* data, uint length);
-    }
 }
index 556eb0e..c610d3c 100644 (file)
@@ -170,6 +170,8 @@ set(VM_SOURCES_WKS
     eventpipefile.cpp
     eventpipejsonfile.cpp
     eventpipeprovider.cpp
+    eventpipebuffer.cpp
+    eventpipebuffermanager.cpp
     eventstore.cpp
     fastserializer.cpp
     fcall.cpp
index ef82daa..c51b4a8 100644 (file)
@@ -1273,6 +1273,8 @@ FCFuncEnd()
 
 #ifdef FEATURE_PERFTRACING
 FCFuncStart(gEventPipeInternalFuncs)
+    QCFuncElement("Enable", EventPipeInternal::Enable)
+    QCFuncElement("Disable", EventPipeInternal::Disable)
     QCFuncElement("CreateProvider", EventPipeInternal::CreateProvider)
     QCFuncElement("AddEvent", EventPipeInternal::AddEvent)
     QCFuncElement("DeleteProvider", EventPipeInternal::DeleteProvider)
index bed4cfd..5660a56 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "common.h"
 #include "eventpipe.h"
+#include "eventpipebuffermanager.h"
 #include "eventpipeconfiguration.h"
 #include "eventpipeevent.h"
 #include "eventpipefile.h"
 CrstStatic EventPipe::s_configCrst;
 bool EventPipe::s_tracingInitialized = false;
 EventPipeConfiguration* EventPipe::s_pConfig = NULL;
+EventPipeBufferManager* EventPipe::s_pBufferManager = NULL;
 EventPipeFile* EventPipe::s_pFile = NULL;
+#ifdef _DEBUG
+EventPipeFile* EventPipe::s_pSyncFile = NULL;
 EventPipeJsonFile* EventPipe::s_pJsonFile = NULL;
+#endif // _DEBUG
+
+#ifdef FEATURE_PAL
+// This function is auto-generated from /src/scripts/genEventPipe.py
+extern "C" void InitProvidersAndEvents();
+#endif
 
 #ifdef FEATURE_PAL
 // This function is auto-generated from /src/scripts/genEventPipe.py
@@ -39,6 +49,8 @@ void EventPipe::Initialize()
     s_pConfig = new EventPipeConfiguration();
     s_pConfig->Initialize();
 
+    s_pBufferManager = new EventPipeBufferManager();
+
 #ifdef FEATURE_PAL
     // This calls into auto-generated code to initialize the runtime providers
     // and events so that the EventPipe configuration lock isn't taken at runtime
@@ -57,9 +69,15 @@ void EventPipe::EnableOnStartup()
     CONTRACTL_END;
 
     // Test COMPLUS variable to enable tracing at start-up.
-    if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_PerformanceTracing) != 0)
+    if((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_PerformanceTracing) & 1) == 1)
     {
-        Enable();
+        SString outputPath;
+        outputPath.Printf("Process-%d.netperf", GetCurrentProcessId());
+        Enable(
+            outputPath.GetUnicode(),
+            1024 /* 1 GB circular buffer */,
+            NULL /* pProviders */,
+            0 /* numProviders */);
     }
 }
 
@@ -74,9 +92,24 @@ void EventPipe::Shutdown()
     CONTRACTL_END;
 
     Disable();
+
+    if(s_pConfig != NULL)
+    {
+        delete(s_pConfig);
+        s_pConfig = NULL;
+    }
+    if(s_pBufferManager != NULL)
+    {
+        delete(s_pBufferManager);
+        s_pBufferManager = NULL;
+    }
 }
 
-void EventPipe::Enable()
+void EventPipe::Enable(
+    LPCWSTR strOutputPath,
+    uint circularBufferSizeInMB,
+    EventPipeProviderConfiguration *pProviders,
+    int numProviders)
 {
     CONTRACTL
     {
@@ -86,7 +119,8 @@ void EventPipe::Enable()
     }
     CONTRACTL_END;
 
-    if(!s_tracingInitialized)
+    // If tracing is not initialized or is already enabled, bail here.
+    if(!s_tracingInitialized || s_pConfig->Enabled())
     {
         return;
     }
@@ -95,26 +129,29 @@ void EventPipe::Enable()
     CrstHolder _crst(GetLock());
 
     // Create the event pipe file.
-    SString eventPipeFileOutputPath;
-    eventPipeFileOutputPath.Printf("Process-%d.netperf", GetCurrentProcessId());
+    SString eventPipeFileOutputPath(strOutputPath);
     s_pFile = new EventPipeFile(eventPipeFileOutputPath);
 
-    if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_PerformanceTracing) == 2)
+#ifdef _DEBUG
+    if((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_PerformanceTracing) & 2) == 2)
     {
-        // File placed in current working directory.
+        // Create a synchronous file.
+        SString eventPipeSyncFileOutputPath;
+        eventPipeSyncFileOutputPath.Printf("Process-%d.sync.netperf", GetCurrentProcessId());
+        s_pSyncFile = new EventPipeFile(eventPipeSyncFileOutputPath);
+
+        // Create a JSON file.
         SString outputFilePath;
         outputFilePath.Printf("Process-%d.PerfView.json", GetCurrentProcessId());
         s_pJsonFile = new EventPipeJsonFile(outputFilePath);
     }
+#endif // _DEBUG
 
     // Enable tracing.
-    s_pConfig->Enable();
+    s_pConfig->Enable(circularBufferSizeInMB, pProviders, numProviders);
 
     // Enable the sample profiler
     SampleProfiler::Enable();
-
-    // TODO: Iterate through the set of providers, enable them as appropriate.
-    // This in-turn will iterate through all of the events and set their isEnabled bits.
 }
 
 void EventPipe::Disable()
@@ -133,28 +170,41 @@ void EventPipe::Disable()
     // Take the lock before disabling tracing.
     CrstHolder _crst(GetLock());
 
-    // Disable the profiler.
-    SampleProfiler::Disable();
+    if(s_pConfig->Enabled())
+    {
+        // Disable the profiler.
+        SampleProfiler::Disable();
 
-    // Disable tracing.
-    s_pConfig->Disable();
+        // Disable tracing.
+        s_pConfig->Disable();
 
-    if(s_pJsonFile != NULL)
-    {
-        delete(s_pJsonFile);
-        s_pJsonFile = NULL;
-    }
+        // Flush all write buffers to make sure that all threads see the change.
+        FlushProcessWriteBuffers();
 
-    if(s_pFile != NULL)
-    {
-        delete(s_pFile);
-        s_pFile = NULL;
-    }
+        // Write to the file.
+        LARGE_INTEGER disableTimeStamp;
+        QueryPerformanceCounter(&disableTimeStamp);
+        s_pBufferManager->WriteAllBuffersToFile(s_pFile, disableTimeStamp);
+        if(s_pFile != NULL)
+        {
+            delete(s_pFile);
+            s_pFile = NULL;
+        }
+#ifdef _DEBUG
+        if(s_pSyncFile != NULL)
+        {
+            delete(s_pSyncFile);
+            s_pSyncFile = NULL;
+        }
+        if(s_pJsonFile != NULL)
+        {
+            delete(s_pJsonFile);
+            s_pJsonFile = NULL;
+        }
+#endif // _DEBUG
 
-    if(s_pConfig != NULL)
-    {
-        delete(s_pConfig);
-        s_pConfig = NULL;
+        // De-allocate buffers.
+        s_pBufferManager->DeAllocateBuffers();
     }
 }
 
@@ -165,6 +215,7 @@ void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int leng
         NOTHROW;
         GC_NOTRIGGER;
         MODE_ANY;
+        PRECONDITION(s_pBufferManager != NULL);
     }
     CONTRACTL_END;
 
@@ -174,29 +225,50 @@ void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int leng
         return;
     }
 
-    DWORD threadID = GetCurrentThreadId();
-
-    // Create an instance of the event.
-    EventPipeEventInstance instance(
-        event,
-        threadID,
-        pData,
-        length);
+    // Get the current thread;
+    Thread *pThread = GetThread();
+    if(pThread == NULL)
+    {
+        // We can't write an event without the thread object.
+        return;
+    }
 
-    // Write to the EventPipeFile.
-    if(s_pFile != NULL)
+    if(s_pBufferManager != NULL)
     {
-        s_pFile->WriteEvent(instance);
+        if(!s_pBufferManager->WriteEvent(pThread, event, pData, length))
+        {
+            // 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;
+        }
     }
 
-    // Write to the EventPipeJsonFile if it exists.
-    if(s_pJsonFile != NULL)
+#ifdef _DEBUG
     {
-        s_pJsonFile->WriteEvent(instance);
+        GCX_PREEMP();
+
+        // Create an instance of the event for the synchronous path.
+        EventPipeEventInstance instance(
+            event,
+            pThread->GetOSThreadId(),
+            pData,
+            length);
+
+        // Write to the EventPipeFile if it exists.
+        if(s_pSyncFile != NULL)
+        {
+            s_pSyncFile->WriteEvent(instance);
+        }
+        // Write to the EventPipeJsonFile if it exists.
+        if(s_pJsonFile != NULL)
+        {
+            s_pJsonFile->WriteEvent(instance);
+        }
     }
+#endif // _DEBUG
 }
 
-void EventPipe::WriteSampleProfileEvent(SampleProfilerEventInstance &instance)
+void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, Thread *pTargetThread, StackContents &stackContents)
 {
     CONTRACTL
     {
@@ -206,17 +278,39 @@ void EventPipe::WriteSampleProfileEvent(SampleProfilerEventInstance &instance)
     }
     CONTRACTL_END;
 
-    // Write to the EventPipeFile.
-    if(s_pFile != NULL)
+    // Write the event to the thread's buffer.
+    if(s_pBufferManager != NULL)
     {
-        s_pFile->WriteEvent(instance);
+        // 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))
+        {
+            // 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;
+        }
     }
 
-    // Write to the EventPipeJsonFile if it exists.
-    if(s_pJsonFile != NULL)
+#ifdef _DEBUG
     {
-        s_pJsonFile->WriteEvent(instance);
+        GCX_PREEMP();
+
+        // Create an instance for the synchronous path.
+        SampleProfilerEventInstance instance(pTargetThread);
+        stackContents.CopyTo(instance.GetStack());
+
+        // Write to the EventPipeFile.
+        if(s_pSyncFile != NULL)
+        {
+            s_pSyncFile->WriteEvent(instance);
+        }
+
+        // Write to the EventPipeJsonFile if it exists.
+        if(s_pJsonFile != NULL)
+        {
+            s_pJsonFile->WriteEvent(instance);
+        }
     }
+#endif // _DEBUG
 }
 
 bool EventPipe::WalkManagedStackForCurrentThread(StackContents &stackContents)
@@ -308,6 +402,28 @@ CrstStatic* EventPipe::GetLock()
     return &s_configCrst;
 }
 
+void QCALLTYPE EventPipeInternal::Enable(
+        __in_z LPCWSTR outputFile,
+        unsigned int circularBufferSizeInMB,
+        EventPipeProviderConfiguration *pProviders,
+        int numProviders)
+{
+    QCALL_CONTRACT;
+
+    BEGIN_QCALL;
+    EventPipe::Enable(outputFile, circularBufferSizeInMB, pProviders, numProviders);
+    END_QCALL;
+}
+
+void QCALLTYPE EventPipeInternal::Disable()
+{
+    QCALL_CONTRACT;
+
+    BEGIN_QCALL;
+    EventPipe::Disable();
+    END_QCALL;
+}
+
 INT_PTR QCALLTYPE EventPipeInternal::CreateProvider(
     GUID providerID,
     EventPipeCallback pCallbackFunc)
index d5b85f7..626c4df 100644 (file)
@@ -15,6 +15,8 @@ class EventPipeConfiguration;
 class EventPipeEvent;
 class EventPipeFile;
 class EventPipeJsonFile;
+class EventPipeBuffer;
+class EventPipeBufferManager;
 class MethodDesc;
 class SampleProfilerEventInstance;
 
@@ -28,9 +30,11 @@ private:
     // Top of stack is at index 0.
     UINT_PTR m_stackFrames[MAX_STACK_DEPTH];
 
+#ifdef _DEBUG
     // Parallel array of MethodDesc pointers.
     // Used for debug-only stack printing.
     MethodDesc* m_methods[MAX_STACK_DEPTH];
+#endif // _DEBUG
 
     // The next available slot in StackFrames.
     unsigned int m_nextAvailableFrame;
@@ -44,6 +48,18 @@ public:
         Reset();
     }
 
+    void CopyTo(StackContents *pDest)
+    {
+        LIMITED_METHOD_CONTRACT;
+        _ASSERTE(pDest != NULL);
+
+        memcpy_s(pDest->m_stackFrames, MAX_STACK_DEPTH * sizeof(UINT_PTR), m_stackFrames, sizeof(UINT_PTR) * m_nextAvailableFrame);
+#ifdef _DEBUG
+        memcpy_s(pDest->m_methods, MAX_STACK_DEPTH * sizeof(MethodDesc*), m_methods, sizeof(MethodDesc*) * m_nextAvailableFrame);
+#endif
+        pDest->m_nextAvailableFrame = m_nextAvailableFrame;
+    }
+
     void Reset()
     {
         LIMITED_METHOD_CONTRACT;
@@ -78,6 +94,7 @@ public:
         return m_stackFrames[frameIndex];
     }
 
+#ifdef _DEBUG
     MethodDesc* GetMethod(unsigned int frameIndex)
     {
         LIMITED_METHOD_CONTRACT;
@@ -90,6 +107,7 @@ public:
 
         return m_methods[frameIndex];
     }
+#endif // _DEBUG
 
     void Append(UINT_PTR controlPC, MethodDesc *pMethod)
     {
@@ -98,7 +116,9 @@ public:
         if(m_nextAvailableFrame < MAX_STACK_DEPTH)
         {
             m_stackFrames[m_nextAvailableFrame] = controlPC;
+#ifdef _DEBUG
             m_methods[m_nextAvailableFrame] = pMethod;
+#endif
             m_nextAvailableFrame++;
         }
     }
@@ -124,6 +144,7 @@ class EventPipe
     friend class EventPipeConfiguration;
     friend class EventPipeFile;
     friend class EventPipeProvider;
+    friend class EventPipeBufferManager;
     friend class SampleProfiler;
 
     public:
@@ -138,7 +159,11 @@ class EventPipe
         static void EnableOnStartup();
 
         // Enable tracing via the event pipe.
-        static void Enable();
+        static void Enable(
+            LPCWSTR strOutputPath,
+            uint circularBufferSizeInMB,
+            EventPipeProviderConfiguration *pProviders,
+            int numProviders);
 
         // Disable tracing via the event pipe.
         static void Disable();
@@ -148,7 +173,7 @@ class EventPipe
         static void WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length);
 
         // Write out a sample profile event.
-        static void WriteSampleProfileEvent(SampleProfilerEventInstance &instance);
+        static void WriteSampleProfileEvent(Thread *pSamplingThread, Thread *pTargetThread, StackContents &stackContents);
         
         // Get the managed call stack for the current thread.
         static bool WalkManagedStackForCurrentThread(StackContents &stackContents);
@@ -171,8 +196,42 @@ class EventPipe
         static CrstStatic s_configCrst;
         static bool s_tracingInitialized;
         static EventPipeConfiguration *s_pConfig;
+        static EventPipeBufferManager *s_pBufferManager;
         static EventPipeFile *s_pFile;
+#ifdef _DEBUG
+        static EventPipeFile *s_pSyncFile;
         static EventPipeJsonFile *s_pJsonFile;
+#endif // _DEBUG
+};
+
+struct EventPipeProviderConfiguration
+{
+
+private:
+
+    LPCWSTR m_pProviderName;
+    UINT64 m_keywords;
+    unsigned int m_loggingLevel;
+
+public:
+
+    LPCWSTR GetProviderName() const
+    {
+        LIMITED_METHOD_CONTRACT;
+        return m_pProviderName;
+    }
+
+    UINT64 GetKeywords() const
+    {
+        LIMITED_METHOD_CONTRACT;
+        return m_keywords;
+    }
+
+    unsigned int GetLevel() const
+    {
+        LIMITED_METHOD_CONTRACT;
+        return m_loggingLevel;
+    }
 };
 
 class EventPipeInternal
@@ -180,6 +239,14 @@ class EventPipeInternal
 
 public:
 
+    static void QCALLTYPE Enable(
+        __in_z LPCWSTR outputFile,
+        unsigned int circularBufferSizeInMB,
+        EventPipeProviderConfiguration *pProviders,
+        int numProviders);
+
+    static void QCALLTYPE Disable();
+
     static INT_PTR QCALLTYPE CreateProvider(
         GUID providerID,
         EventPipeCallback pCallbackFunc);
diff --git a/src/vm/eventpipebuffer.cpp b/src/vm/eventpipebuffer.cpp
new file mode 100644 (file)
index 0000000..e0e9610
--- /dev/null
@@ -0,0 +1,275 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+#include "common.h"
+#include "eventpipeeventinstance.h"
+#include "eventpipebuffer.h"
+
+#ifdef FEATURE_PERFTRACING
+
+EventPipeBuffer::EventPipeBuffer(unsigned int bufferSize)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    m_pBuffer = new BYTE[bufferSize];
+    memset(m_pBuffer, 0, bufferSize);
+    m_pCurrent = m_pBuffer;
+    m_pLimit = m_pBuffer + bufferSize;
+
+    m_mostRecentTimeStamp.QuadPart = 0;
+    m_pLastPoppedEvent = NULL;
+    m_pPrevBuffer = NULL;
+    m_pNextBuffer = NULL;
+}
+
+EventPipeBuffer::~EventPipeBuffer()
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    if(m_pBuffer != NULL)
+    {
+        delete[] m_pBuffer;
+    }
+}
+
+bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *pData, unsigned int dataLength, StackContents *pStack)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_TRIGGERS;
+        MODE_ANY;
+        PRECONDITION(pThread != NULL);
+    }
+    CONTRACTL_END;
+
+    // Calculate the size of the event.
+    unsigned int eventSize = sizeof(EventPipeEventInstance) + dataLength;
+
+    // Make sure we have enough space to write the event.
+    if(m_pCurrent + eventSize >= m_pLimit)
+    {
+        return false;
+    }
+
+    // Calculate the location of the data payload.
+    BYTE *pDataDest = m_pCurrent + sizeof(EventPipeEventInstance);
+
+    bool success = true;
+    EX_TRY
+    {
+        // Placement-new the EventPipeEventInstance.
+        EventPipeEventInstance *pInstance = new (m_pCurrent) EventPipeEventInstance(
+            event,
+            pThread->GetOSThreadId(),
+            pDataDest,
+            dataLength);
+
+        // Copy the stack if a separate stack trace was provided.
+        if(pStack != NULL)
+        {
+            StackContents *pInstanceStack = pInstance->GetStack();
+            pStack->CopyTo(pInstanceStack);
+        }
+
+        // Write the event payload data to the buffer.
+        if(dataLength > 0)
+        {
+            memcpy(pDataDest, pData, dataLength);
+        }
+
+        // Save the most recent event timestamp.
+        m_mostRecentTimeStamp = pInstance->GetTimeStamp();
+
+    }
+    EX_CATCH
+    {
+        // If a failure occurs, bail out and don't advance the pointer.
+        success = false;
+    }
+    EX_END_CATCH(SwallowAllExceptions);
+
+    if(success)
+    {
+        // Advance the current pointer past the event.
+        m_pCurrent += eventSize;
+    }
+
+    return success;
+}
+
+LARGE_INTEGER EventPipeBuffer::GetMostRecentTimeStamp() const
+{
+    LIMITED_METHOD_CONTRACT;
+
+    return m_mostRecentTimeStamp;
+}
+
+void EventPipeBuffer::Clear()
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_TRIGGERS;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    memset(m_pBuffer, 0, (size_t)(m_pLimit - m_pBuffer));
+    m_pCurrent = m_pBuffer;
+    m_mostRecentTimeStamp.QuadPart = 0;
+    m_pLastPoppedEvent = NULL;
+}
+
+EventPipeEventInstance* EventPipeBuffer::GetNext(EventPipeEventInstance *pEvent, LARGE_INTEGER beforeTimeStamp)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_TRIGGERS;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    EventPipeEventInstance *pNextInstance = NULL;
+    // If input is NULL, return the first event if there is one.
+    if(pEvent == NULL)
+    {
+        // If this buffer contains an event, select it.
+        if(m_pCurrent > m_pBuffer)
+        {
+            pNextInstance = (EventPipeEventInstance*)m_pBuffer;
+        }
+    }
+    else
+    {
+        // Confirm that pEvent is within the used range of the buffer.
+        if(((BYTE*)pEvent < m_pBuffer) || ((BYTE*)pEvent >= m_pCurrent))
+        {
+            _ASSERT(!"Input pointer is out of range.");
+            return NULL;
+        }
+
+        // We have a pointer within the bounds of the buffer.
+        // Find the next event by skipping the current event with it's data payload immediately after the instance.
+        pNextInstance = (EventPipeEventInstance *)(pEvent->GetData() + pEvent->GetLength());
+
+        // Check to see if we've reached the end of the written portion of the buffer.
+        if((BYTE*)pNextInstance >= m_pCurrent)
+        {
+            return NULL;
+        }
+    }
+
+    // Ensure that the timestamp is valid.  The buffer is zero'd before use, so a zero timestamp is invalid.
+    LARGE_INTEGER nextTimeStamp = pNextInstance->GetTimeStamp();
+    if(nextTimeStamp.QuadPart == 0)
+    {
+        return NULL;
+    }
+
+    // Ensure that the timestamp is earlier than the beforeTimeStamp.
+    if(nextTimeStamp.QuadPart >= beforeTimeStamp.QuadPart)
+    {
+        return NULL;
+    }
+
+    return pNextInstance;
+}
+
+EventPipeEventInstance* EventPipeBuffer::PeekNext(LARGE_INTEGER beforeTimeStamp)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    // Get the next event using the last popped event as a marker.
+    return GetNext(m_pLastPoppedEvent, beforeTimeStamp);
+}
+
+EventPipeEventInstance* EventPipeBuffer::PopNext(LARGE_INTEGER beforeTimeStamp)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    // Get the next event using the last popped event as a marker.
+    EventPipeEventInstance *pNext = PeekNext(beforeTimeStamp);
+    if(pNext != NULL)
+    {
+        m_pLastPoppedEvent = pNext;
+    }
+
+    return pNext;
+}
+
+#ifdef _DEBUG
+bool EventPipeBuffer::EnsureConsistency()
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    // Check to see if the buffer is empty.
+    if(m_pBuffer == m_pCurrent)
+    {
+        // Make sure that the buffer size is greater than zero.
+        _ASSERTE(m_pBuffer != m_pLimit);
+    }
+
+    // Validate the contents of the filled portion of the buffer.
+    BYTE *ptr = m_pBuffer;
+    while(ptr < m_pCurrent)
+    {
+        // Validate the event.
+        EventPipeEventInstance *pInstance = (EventPipeEventInstance*)ptr;
+        _ASSERTE(pInstance->EnsureConsistency());
+
+        // Validate that payload and length match.
+        _ASSERTE((pInstance->GetData() != NULL && pInstance->GetLength() > 0) || (pInstance->GetData() != NULL && pInstance->GetLength() == 0));
+
+        // Skip the event.
+        ptr += sizeof(*pInstance) + pInstance->GetLength();
+    }
+
+    // When we're done walking the filled portion of the buffer,
+    // ptr should be the same as m_pCurrent.
+    _ASSERTE(ptr == m_pCurrent);
+
+    // Walk the rest of the buffer, making sure it is properly zeroed.
+    while(ptr < m_pLimit)
+    {
+        _ASSERTE(*ptr++ == 0);
+    }
+
+    return true;
+}
+#endif // _DEBUG
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipebuffer.h b/src/vm/eventpipebuffer.h
new file mode 100644 (file)
index 0000000..97b858d
--- /dev/null
@@ -0,0 +1,109 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EVENTPIPE_BUFFER_H__
+#define __EVENTPIPE_BUFFER_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "eventpipeevent.h"
+#include "eventpipeeventinstance.h"
+
+class EventPipeBuffer
+{
+
+    friend class EventPipeBufferList;
+    friend class EventPipeBufferManager;
+
+private:
+
+    // A pointer to the actual buffer.
+    BYTE *m_pBuffer;
+
+    // The current write pointer.
+    BYTE *m_pCurrent;
+
+    // The max write pointer (end of the buffer).
+    BYTE *m_pLimit;
+
+    // The timestamp of the most recent event in the buffer.
+    LARGE_INTEGER m_mostRecentTimeStamp;
+
+    // Used by PopNext as input to GetNext.
+    // If NULL, no events have been popped.
+    // The event will still remain in the buffer after it is popped, but PopNext will not return it again.
+    EventPipeEventInstance *m_pLastPoppedEvent;
+
+    // Each buffer will become part of a per-thread linked list of buffers.
+    // The linked list is invasive, thus we declare the pointers here.
+    EventPipeBuffer *m_pPrevBuffer;
+    EventPipeBuffer *m_pNextBuffer;
+
+    unsigned int GetSize() const
+    {
+        LIMITED_METHOD_CONTRACT;
+        return (unsigned int)(m_pLimit - m_pBuffer);
+    }
+
+    EventPipeBuffer* GetPrevious() const
+    {
+        LIMITED_METHOD_CONTRACT;
+        return m_pPrevBuffer;
+    }
+
+    EventPipeBuffer* GetNext() const
+    {
+        LIMITED_METHOD_CONTRACT;
+        return m_pNextBuffer;
+    }
+
+    void SetPrevious(EventPipeBuffer *pBuffer)
+    {
+        LIMITED_METHOD_CONTRACT;
+        m_pPrevBuffer = pBuffer;
+    }
+
+    void SetNext(EventPipeBuffer *pBuffer)
+    {
+        LIMITED_METHOD_CONTRACT;
+        m_pNextBuffer = pBuffer;
+    }
+
+public:
+
+    EventPipeBuffer(unsigned int bufferSize);
+    ~EventPipeBuffer();
+
+    // Write an event to the buffer.
+    // An optional stack trace can be provided for sample profiler events.
+    // Otherwise, if a stack trace is needed, one will be automatically collected.
+    // Returns:
+    //  - true: The write succeeded.
+    //  - false: The write failed.  In this case, the buffer should be considered full.
+    bool WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *pData, unsigned int dataLength, StackContents *pStack = NULL);
+
+    // Get the timestamp of the most recent event in the buffer.
+    LARGE_INTEGER GetMostRecentTimeStamp() const;
+
+    // Clear the buffer.
+    void Clear();
+
+    // Get the next event from the buffer as long as it is before the specified timestamp.
+    // Input of NULL gets the first event.
+    EventPipeEventInstance* GetNext(EventPipeEventInstance *pEvent, LARGE_INTEGER beforeTimeStamp);
+
+    // Get the next event from the buffer, but don't mark it read.
+    EventPipeEventInstance* PeekNext(LARGE_INTEGER beforeTimeStamp);
+
+    // Get the next event from the buffer and mark it as read.
+    EventPipeEventInstance* PopNext(LARGE_INTEGER beforeTimeStamp);
+
+#ifdef _DEBUG
+    bool EnsureConsistency();
+#endif // _DEBUG
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_BUFFER_H__
diff --git a/src/vm/eventpipebuffermanager.cpp b/src/vm/eventpipebuffermanager.cpp
new file mode 100644 (file)
index 0000000..4e6b6b9
--- /dev/null
@@ -0,0 +1,798 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include "eventpipeconfiguration.h"
+#include "eventpipebuffer.h"
+#include "eventpipebuffermanager.h"
+
+#ifdef FEATURE_PERFTRACING
+
+EventPipeBufferManager::EventPipeBufferManager()
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    m_pPerThreadBufferList = new SList<SListElem<EventPipeBufferList*>>();
+    m_sizeOfAllBuffers = 0;
+    m_lock.Init(LOCK_TYPE_DEFAULT);
+
+#ifdef _DEBUG
+    m_numBuffersAllocated = 0;
+    m_numBuffersStolen = 0;
+    m_numBuffersLeaked = 0;
+    m_numEventsStored = 0;
+    m_numEventsWritten = 0;
+#endif // _DEBUG
+}
+
+EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(Thread *pThread, unsigned int requestSize)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_NOTRIGGER;
+        MODE_ANY;
+        PRECONDITION(pThread != NULL);
+        PRECONDITION(requestSize > 0);
+    }
+    CONTRACTL_END;
+
+    // Allocating a buffer requires us to take the lock.
+    SpinLockHolder _slh(&m_lock);
+
+    // Determine if the requesting thread has at least one buffer.
+    // If not, we guarantee that each thread gets at least one (to prevent thrashing when the circular buffer size is too small).
+    bool allocateNewBuffer = false;
+    EventPipeBufferList *pThreadBufferList = pThread->GetEventPipeBufferList();
+    if(pThreadBufferList == NULL)
+    {
+        pThreadBufferList = new EventPipeBufferList(this);
+        m_pPerThreadBufferList->InsertTail(new SListElem<EventPipeBufferList*>(pThreadBufferList));
+        pThread->SetEventPipeBufferList(pThreadBufferList);
+        allocateNewBuffer = true;
+    }
+
+    // Determine if policy allows us to allocate another buffer, or if we need to steal one
+    // from another thread.
+    if(!allocateNewBuffer)
+    {
+        EventPipeConfiguration *pConfig = EventPipe::GetConfiguration();
+        if(pConfig == NULL)
+        {
+            return NULL;
+        }
+
+        size_t circularBufferSizeInBytes = pConfig->GetCircularBufferSize();
+        if(m_sizeOfAllBuffers < circularBufferSizeInBytes)
+        {
+            // We don't worry about the fact that a new buffer could put us over the circular buffer size.
+            // This is OK, and we won't do it again if we actually go over.
+            allocateNewBuffer = true;
+        }
+    }
+
+    EventPipeBuffer *pNewBuffer = NULL;
+    if(!allocateNewBuffer)
+    {
+        // We can't allocate a new buffer.
+        // Find the oldest buffer, zero it, and re-purpose it for this thread.
+
+        // Find the thread that contains the oldest stealable buffer, and get its list of buffers.
+        EventPipeBufferList *pListToStealFrom = FindThreadToStealFrom();
+        if(pListToStealFrom != NULL)
+        {
+            // Assert that the buffer we're stealing is not the only buffer in the list.
+            // This invariant is enforced by FindThreadToStealFrom.
+            _ASSERTE((pListToStealFrom->GetHead() != NULL) && (pListToStealFrom->GetHead()->GetNext() != NULL));
+
+            // Remove the oldest buffer from the list.
+            pNewBuffer = pListToStealFrom->GetAndRemoveHead();
+
+            // De-allocate the buffer.  We do this because buffers are variable sized
+            // based on how much volume is coming from the thread.
+            DeAllocateBuffer(pNewBuffer);
+            pNewBuffer = NULL;
+
+            // Set that we want to allocate a new buffer.
+            allocateNewBuffer = true;
+
+#ifdef _DEBUG
+            m_numBuffersStolen++;
+#endif // _DEBUG
+
+        }
+        else
+        {
+            // This only happens when # of threads == # of buffers.
+            // We'll allocate one more buffer, and then this won't happen again.
+            allocateNewBuffer = true;
+        }
+    }
+
+    if(allocateNewBuffer)
+    {
+        // Pick a buffer size by multiplying the base buffer size by the number of buffers already allocated for this thread.
+        unsigned int sizeMultiplier = pThreadBufferList->GetCount() + 1;
+
+        // Pick the base buffer size based.  Debug builds have a smaller size to stress the allocate/steal path more.
+        unsigned int baseBufferSize =
+#ifdef _DEBUG
+            5 * 1024; // 5K
+#else
+            100 * 1024; // 100K
+#endif
+        unsigned int bufferSize = baseBufferSize * sizeMultiplier;
+
+        // Make sure that buffer size >= request size so that the buffer size does not
+        // determine the max event size.
+        if(bufferSize < requestSize)
+        {
+            bufferSize = requestSize;
+        }
+
+        pNewBuffer = new EventPipeBuffer(bufferSize);
+        m_sizeOfAllBuffers += bufferSize;
+#ifdef _DEBUG
+        m_numBuffersAllocated++;
+#endif // _DEBUG
+    }
+
+    // Set the buffer on the thread.
+    if(pNewBuffer != NULL)
+    {
+        pThreadBufferList->InsertTail(pNewBuffer);
+        return pNewBuffer;
+    }
+
+    return NULL;
+}
+
+EventPipeBufferList* EventPipeBufferManager::FindThreadToStealFrom()
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_NOTRIGGER;
+        MODE_ANY;
+        PRECONDITION(m_lock.OwnedByCurrentThread());
+    }
+    CONTRACTL_END;
+
+    // Find the thread buffer list containing the buffer whose most recent event is the oldest as long as the buffer is not
+    // the current buffer for the thread (e.g. it's next pointer is non-NULL).
+    // This means that the thread must also have multiple buffers, so that we don't steal its only buffer.
+    EventPipeBufferList *pOldestContainingList = NULL;
+
+    SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead();
+    while(pElem != NULL)
+    {
+        EventPipeBufferList *pCandidate = pElem->GetValue();
+
+        // The current candidate has more than one buffer (otherwise it is disqualified).
+        if(pCandidate->GetHead()->GetNext() != NULL)
+        {
+            // If we haven't seen any candidates, this one automatically becomes the oldest candidate.
+            if(pOldestContainingList == NULL)
+            {
+                pOldestContainingList = pCandidate;
+            }
+            // Otherwise, to replace the existing candidate, this candidate must have an older timestamp in its oldest buffer.
+            else if((pOldestContainingList->GetHead()->GetMostRecentTimeStamp().QuadPart) > 
+                      (pCandidate->GetHead()->GetMostRecentTimeStamp().QuadPart))
+            {
+                pOldestContainingList = pCandidate;
+            }
+        }
+
+        pElem = m_pPerThreadBufferList->GetNext(pElem);
+    }
+
+    return pOldestContainingList;
+}
+
+void EventPipeBufferManager::DeAllocateBuffer(EventPipeBuffer *pBuffer)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    if(pBuffer != NULL)
+    {
+        m_sizeOfAllBuffers -= pBuffer->GetSize();
+        delete(pBuffer);
+#ifdef _DEBUG
+        m_numBuffersAllocated--;
+#endif // _DEBUG
+    }
+}
+
+bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *pData, unsigned int length, Thread *pEventThread, StackContents *pStack)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+        // The input thread must match the current thread because no lock is taken on the buffer.
+        PRECONDITION(pThread == GetThread());
+    }
+    CONTRACTL_END;
+
+    _ASSERTE(pThread == GetThread());
+
+    // Check to see an event thread was specified.  If not, then use the current thread.
+    if(pEventThread == NULL)
+    {
+        pEventThread = pThread;
+    }
+
+    // Before we pick a buffer, make sure the event is enabled.
+    if(!event.IsEnabled())
+    {
+        return false;
+    }
+
+    // The event is still enabled.  Mark that the thread is now writing an event.
+    pThread->SetEventWriteInProgress(true);
+
+    // Check one more time to make sure that the event is still enabled.
+    // We do this because we might be trying to disable tracing and free buffers, so we
+    // must make sure that the event is enabled after we mark that we're writing to avoid
+    // races with the destructing thread.
+    if(!event.IsEnabled())
+    {
+        return false;
+    }
+
+    // See if the thread already has a buffer to try.
+    bool allocNewBuffer = false;
+    EventPipeBuffer *pBuffer = NULL;
+    EventPipeBufferList *pThreadBufferList = pThread->GetEventPipeBufferList();
+    if(pThreadBufferList == NULL)
+    {
+        allocNewBuffer = true;
+    }
+    else
+    {
+        // The thread already has a buffer list.  Select the newest buffer and attempt to write into it.
+        pBuffer = pThreadBufferList->GetTail();
+        if(pBuffer == NULL)
+        {
+            // This should never happen.  If the buffer list exists, it must contain at least one entry.
+            _ASSERT(!"Thread buffer list with zero entries encountered.");
+            return false;
+        }
+        else
+        {
+            // Attempt to write the event to the buffer.  If this fails, we should allocate a new buffer.
+            allocNewBuffer = !pBuffer->WriteEvent(pEventThread, event, pData, length, pStack);
+        }
+    }
+
+    // Check to see if we need to allocate a new buffer, and if so, do it here.
+    if(allocNewBuffer)
+    {
+        GCX_PREEMP();
+
+        unsigned int requestSize = sizeof(EventPipeEventInstance) + length;
+        pBuffer = AllocateBufferForThread(pThread, requestSize);
+    }
+
+    // Try to write the event after we allocated (or stole) a buffer.
+    // This is the first time if the thread had no buffers before the call to this function.
+    // This is the second time if this thread did have one or more buffers, but they were full.
+    if(allocNewBuffer && pBuffer != NULL)
+    {
+        allocNewBuffer = !pBuffer->WriteEvent(pEventThread, event, pData, length, pStack);
+    }
+
+    // Mark that the thread is no longer writing an event.
+     pThread->SetEventWriteInProgress(false);
+
+#ifdef _DEBUG
+    if(!allocNewBuffer)
+    {
+        InterlockedIncrement(&m_numEventsStored);
+    }
+#endif // _DEBUG
+    return !allocNewBuffer;
+}
+
+void EventPipeBufferManager::WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_INTEGER stopTimeStamp)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+        PRECONDITION(pFile != NULL);
+    }
+    CONTRACTL_END;
+
+    // TODO: Better version of merge sort.
+    // 1. Iterate through all of the threads, adding each buffer to a temporary list.
+    // 2. While iterating, get the lowest most recent timestamp.  This is the timestamp that we want to process up to.
+    // 3. Process up to the lowest most recent timestamp for the set of buffers.
+    // 4. When we get NULLs from each of the buffers on PopNext(), we're done.
+    // 5. While iterating if PopNext() == NULL && Empty() == NULL, remove the buffer from the list.  It's empty.
+    // 6. While iterating, grab the next lowest most recent timestamp.
+    // 7. Walk through the list again and look for any buffers that have a lower most recent timestamp than the next most recent timestamp.
+    // 8. If we find one, add it to the list and select its most recent timestamp as the lowest.
+    // 9. Process again (go to 3).
+    // 10. Continue until there are no more buffers to process.
+
+    // Take the lock before walking the buffer list.
+    SpinLockHolder _slh(&m_lock);
+
+    // Naively walk the circular buffer, writing the event stream in timestamp order.
+    while(true)
+    {
+        EventPipeEventInstance *pOldestInstance = NULL;
+        EventPipeBuffer *pOldestContainingBuffer = NULL;
+        EventPipeBufferList *pOldestContainingList = NULL;
+        SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead();
+        while(pElem != NULL)
+        {
+            EventPipeBufferList *pBufferList = pElem->GetValue();
+
+            // Peek the next event out of the list.
+            EventPipeBuffer *pContainingBuffer = NULL;
+            EventPipeEventInstance *pNext = pBufferList->PeekNextEvent(stopTimeStamp, &pContainingBuffer);
+            if(pNext != NULL)
+            {
+                // If it's the oldest event we've seen, then save it.
+                if((pOldestInstance == NULL) ||
+                   (pOldestInstance->GetTimeStamp().QuadPart > pNext->GetTimeStamp().QuadPart)) 
+                {
+                    pOldestInstance = pNext;
+                    pOldestContainingBuffer = pContainingBuffer;
+                    pOldestContainingList = pBufferList;
+                }
+            }
+
+            pElem = m_pPerThreadBufferList->GetNext(pElem);
+        }
+
+        if(pOldestInstance == NULL)
+        {
+            // We're done.  There are no more events.
+            break;
+        }
+
+        // Write the oldest event.
+        pFile->WriteEvent(*pOldestInstance);
+#ifdef _DEBUG
+        m_numEventsWritten++;
+#endif // _DEBUG
+
+        // Pop the event from the buffer.
+        pOldestContainingList->PopNextEvent(stopTimeStamp);
+    }
+}
+
+void EventPipeBufferManager::DeAllocateBuffers()
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    _ASSERTE(EnsureConsistency());
+
+    // Take the thread store lock because we're going to iterate through the thread list.
+    {
+        ThreadStoreLockHolder tsl;
+
+        // Take the buffer manager manipulation lock.
+        SpinLockHolder _slh(&m_lock);
+
+        Thread *pThread = NULL;
+        while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
+        {
+            // Get the thread's buffer list.
+            EventPipeBufferList *pBufferList = pThread->GetEventPipeBufferList();
+            if(pBufferList != NULL)
+            {
+                // Attempt to free the buffer list.
+                // If the thread is using its buffer list skip it.
+                // This means we will leak a single buffer, but if tracing is re-enabled, that buffer can be used again.
+                if(!pThread->GetEventWriteInProgress())
+                {
+                    EventPipeBuffer *pBuffer = pBufferList->GetAndRemoveHead();
+                    while(pBuffer != NULL)
+                    {
+                        DeAllocateBuffer(pBuffer);
+                        pBuffer = pBufferList->GetAndRemoveHead();
+                    }
+
+                    // Remove the list entry from the per thread buffer list.
+                    SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead();
+                    while(pElem != NULL)
+                    {
+                        EventPipeBufferList* pEntry = pElem->GetValue();
+                        if(pEntry == pBufferList)
+                        {
+                            pElem = m_pPerThreadBufferList->FindAndRemove(pElem);
+
+                            // In DEBUG, make sure that the element was found and removed.
+                            _ASSERTE(pElem != NULL);
+                        }
+                        pElem = m_pPerThreadBufferList->GetNext(pElem);
+                    }
+
+                    // Remove the list reference from the thread.
+                    pThread->SetEventPipeBufferList(NULL);
+
+                    // Now that all of the list elements have been freed, free the list itself.
+                    delete(pBufferList);
+                    pBufferList = NULL;
+                }
+#ifdef _DEBUG
+                else
+                {
+                    // We can't deallocate the buffers.
+                    m_numBuffersLeaked += pBufferList->GetCount();
+                }
+#endif // _DEBUG            
+            }
+        }
+    }
+
+    // Now that we've walked through all of the threads, let's see if there are any other buffers
+    // that belonged to threads that died during tracing.  We can free these now.
+
+    // Take the buffer manager manipulation lock
+    SpinLockHolder _slh(&m_lock);
+
+    SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead();
+    while(pElem != NULL)
+    {
+        // Get the list and determine if we can free it.
+        EventPipeBufferList *pBufferList = pElem->GetValue();
+        if(!pBufferList->OwnedByThread())
+        {
+            // Iterate over all nodes in the list and de-allocate them.
+            EventPipeBuffer *pBuffer = pBufferList->GetAndRemoveHead();
+            while(pBuffer != NULL)
+            {
+                DeAllocateBuffer(pBuffer);
+                pBuffer = pBufferList->GetAndRemoveHead();
+            }
+
+            // Remove the buffer list from the per-thread buffer list.
+            pElem = m_pPerThreadBufferList->FindAndRemove(pElem);
+            _ASSERTE(pElem != NULL);
+
+            // Now that all of the list elements have been freed, free the list itself.
+            delete(pBufferList);
+            pBufferList = NULL;
+        }
+
+        pElem = m_pPerThreadBufferList->GetNext(pElem);
+    } 
+}
+
+#ifdef _DEBUG
+bool EventPipeBufferManager::EnsureConsistency()
+{
+    LIMITED_METHOD_CONTRACT;
+
+    SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead();
+    while(pElem != NULL)
+    {
+        EventPipeBufferList *pBufferList = pElem->GetValue();
+
+        _ASSERTE(pBufferList->EnsureConsistency());
+
+        pElem = m_pPerThreadBufferList->GetNext(pElem);
+    }
+
+    return true;
+}
+#endif // _DEBUG
+
+EventPipeBufferList::EventPipeBufferList(EventPipeBufferManager *pManager)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    m_pManager = pManager;
+    m_pHeadBuffer = NULL;
+    m_pTailBuffer = NULL;
+    m_bufferCount = 0;
+    m_pReadBuffer = NULL;
+    m_ownedByThread = true;
+
+#ifdef _DEBUG
+    m_pCreatingThread = GetThread();
+#endif // _DEBUG
+}
+
+EventPipeBuffer* EventPipeBufferList::GetHead()
+{
+    LIMITED_METHOD_CONTRACT;
+
+    return m_pHeadBuffer;
+}
+
+EventPipeBuffer* EventPipeBufferList::GetTail()
+{
+    LIMITED_METHOD_CONTRACT;
+
+    return m_pTailBuffer;
+}
+
+void EventPipeBufferList::InsertTail(EventPipeBuffer *pBuffer)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+        PRECONDITION(pBuffer != NULL);
+    }
+    CONTRACTL_END;
+
+    _ASSERTE(EnsureConsistency());
+
+    // Ensure that the input buffer didn't come from another list that was improperly cleaned up.
+    _ASSERTE((pBuffer->GetNext() == NULL) && (pBuffer->GetPrevious() == NULL));
+
+    // First node in the list.
+    if(m_pTailBuffer == NULL)
+    {
+        m_pHeadBuffer = m_pTailBuffer = pBuffer;
+    }
+    else
+    {
+        // Set links between the old and new tail nodes.
+        m_pTailBuffer->SetNext(pBuffer);
+        pBuffer->SetPrevious(m_pTailBuffer);
+
+        // Set the new tail node.
+        m_pTailBuffer = pBuffer;
+    }
+
+    m_bufferCount++;
+
+    _ASSERTE(EnsureConsistency());
+}
+
+EventPipeBuffer* EventPipeBufferList::GetAndRemoveHead()
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    _ASSERTE(EnsureConsistency());
+
+    EventPipeBuffer *pRetBuffer = NULL;
+    if(m_pHeadBuffer != NULL)
+    {
+        // Save the head node.
+        pRetBuffer = m_pHeadBuffer;
+
+        // Set the new head node.
+        m_pHeadBuffer = m_pHeadBuffer->GetNext();
+
+        // Update the head node's previous pointer.
+        if(m_pHeadBuffer != NULL)
+        {
+            m_pHeadBuffer->SetPrevious(NULL);
+        }
+        else
+        {
+            // We just removed the last buffer from the list.
+            // Make sure both head and tail pointers are NULL.
+            m_pTailBuffer = NULL;
+        }
+
+        // Clear the next pointer of the old head node.
+        pRetBuffer->SetNext(NULL);
+
+        // Ensure that the old head node has no dangling references.
+        _ASSERTE((pRetBuffer->GetNext() == NULL) && (pRetBuffer->GetPrevious() == NULL));
+
+        // Decrement the count of buffers in the list.
+        m_bufferCount--;
+    }
+
+    _ASSERTE(EnsureConsistency());
+
+    return pRetBuffer;
+}
+
+unsigned int EventPipeBufferList::GetCount() const
+{
+    LIMITED_METHOD_CONTRACT;
+
+    return m_bufferCount;
+}
+
+EventPipeEventInstance* EventPipeBufferList::PeekNextEvent(LARGE_INTEGER beforeTimeStamp, EventPipeBuffer **pContainingBuffer)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    // Get the current read buffer.
+    // If it's not set, start with the head buffer.
+    if(m_pReadBuffer == NULL)
+    {
+        m_pReadBuffer = m_pHeadBuffer;
+    }
+
+    // If the read buffer is still NULL, then this list contains no buffers.
+    if(m_pReadBuffer == NULL)
+    {
+        return NULL;
+    }
+
+    // Get the next event in the buffer.
+    EventPipeEventInstance *pNext = m_pReadBuffer->PeekNext(beforeTimeStamp);
+
+    // If the next event is NULL, then go to the next buffer.
+    if(pNext == NULL)
+    {
+        m_pReadBuffer = m_pReadBuffer->GetNext();
+        if(m_pReadBuffer != NULL)
+        {
+            pNext = m_pReadBuffer->PeekNext(beforeTimeStamp);
+        }
+    }
+
+    // Set the containing buffer.
+    if(pNext != NULL && pContainingBuffer != NULL)
+    {
+        *pContainingBuffer = m_pReadBuffer;
+    }
+
+    // Make sure pContainingBuffer is properly set.
+    _ASSERTE((pNext == NULL) || (pNext != NULL && pContainingBuffer == NULL) || (pNext != NULL && *pContainingBuffer == m_pReadBuffer));
+    return pNext;
+}
+
+EventPipeEventInstance* EventPipeBufferList::PopNextEvent(LARGE_INTEGER beforeTimeStamp)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    // Get the next event.
+    EventPipeBuffer *pContainingBuffer = NULL;
+    EventPipeEventInstance *pNext = PeekNextEvent(beforeTimeStamp, &pContainingBuffer);
+
+    // If the event is non-NULL, pop it.
+    if(pNext != NULL && pContainingBuffer != NULL)
+    {
+        pContainingBuffer->PopNext(beforeTimeStamp);
+
+        // If the buffer is not the last buffer in the list and it has been drained, de-allocate it.
+        if((pContainingBuffer->GetNext() != NULL) && (pContainingBuffer->PeekNext(beforeTimeStamp) == NULL))
+        {
+            // This buffer must be the head node of the list.
+            _ASSERTE(pContainingBuffer->GetPrevious() == NULL);
+            EventPipeBuffer *pRemoved = GetAndRemoveHead();
+            _ASSERTE(pRemoved == pContainingBuffer);
+
+            // De-allocate the buffer.
+            m_pManager->DeAllocateBuffer(pRemoved);
+
+            // Reset the read buffer so that it becomes the head node on next peek or pop operation.
+            m_pReadBuffer = NULL;
+        }
+    }
+
+    return pNext;
+}
+
+bool EventPipeBufferList::OwnedByThread()
+{
+    LIMITED_METHOD_CONTRACT;
+    return m_ownedByThread;
+}
+
+void EventPipeBufferList::SetOwnedByThread(bool value)
+{
+    LIMITED_METHOD_CONTRACT;
+    m_ownedByThread = value;
+}
+
+#ifdef _DEBUG
+Thread* EventPipeBufferList::GetThread()
+{
+    LIMITED_METHOD_CONTRACT;
+
+    return m_pCreatingThread;
+}
+
+bool EventPipeBufferList::EnsureConsistency()
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    // Either the head and tail nodes are both NULL or both are non-NULL.
+    _ASSERTE((m_pHeadBuffer == NULL && m_pTailBuffer == NULL) || (m_pHeadBuffer != NULL && m_pTailBuffer != NULL));
+
+    // If the list is NULL, check the count and return.
+    if(m_pHeadBuffer == NULL)
+    {
+        _ASSERTE(m_bufferCount == 0);
+        return true;
+    }
+
+    // If the list is non-NULL, walk the list forward until we get to the end.
+    unsigned int nodeCount = (m_pHeadBuffer != NULL) ? 1 : 0;
+    EventPipeBuffer *pIter = m_pHeadBuffer;
+    while(pIter->GetNext() != NULL)
+    {
+        pIter = pIter->GetNext();
+        nodeCount++;
+
+        // Check for consistency of the buffer itself.
+        _ASSERTE(pIter->EnsureConsistency());
+
+        // Check for cycles.
+        _ASSERTE(nodeCount <= m_bufferCount);
+    }
+
+    // When we're done with the walk, pIter must point to the tail node.
+    _ASSERTE(pIter == m_pTailBuffer);
+
+    // Node count must equal the buffer count.
+    _ASSERTE(nodeCount == m_bufferCount);
+
+    // Now, walk the list in reverse.
+    pIter = m_pTailBuffer;
+    nodeCount = (m_pTailBuffer != NULL) ? 1 : 0;
+    while(pIter->GetPrevious() != NULL)
+    {
+        pIter = pIter->GetPrevious();
+        nodeCount++;
+
+        // Check for cycles.
+        _ASSERTE(nodeCount <= m_bufferCount);
+    }
+
+    // When we're done with the reverse walk, pIter must point to the head node.
+    _ASSERTE(pIter == m_pHeadBuffer);
+
+    // Node count must equal the buffer count.
+    _ASSERTE(nodeCount == m_bufferCount);
+
+    // We're done.
+    return true;
+}
+#endif // _DEBUG
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipebuffermanager.h b/src/vm/eventpipebuffermanager.h
new file mode 100644 (file)
index 0000000..74783d2
--- /dev/null
@@ -0,0 +1,161 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EVENTPIPE_BUFFERMANAGER_H__
+#define __EVENTPIPE_BUFFERMANAGER_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "eventpipefile.h"
+#include "eventpipebuffer.h"
+#include "spinlock.h"
+
+class EventPipeBufferList;
+
+class EventPipeBufferManager
+{
+
+    // Declare friends.
+    friend class EventPipeBufferList;
+
+private:
+
+    // A list of linked-lists of buffer objects.
+    // Each entry in this list represents a set of buffers owned by a single thread.
+    // The actual Thread object has a pointer to the object contained in this list.  This ensures that
+    // each thread can access its own list, while at the same time, ensuring that when
+    // a thread is destroyed, we keep the buffers around without having to perform any
+    // migration or book-keeping.
+    SList<SListElem<EventPipeBufferList*>> *m_pPerThreadBufferList;
+
+    // The total allocation size of buffers under management.
+    size_t m_sizeOfAllBuffers;
+
+    // Lock to protect access to the per-thread buffer list and total allocation size.
+    SpinLock m_lock;
+
+#ifdef _DEBUG
+    // For debugging purposes.
+    unsigned int m_numBuffersAllocated;
+    unsigned int m_numBuffersStolen;
+    unsigned int m_numBuffersLeaked;
+    Volatile<LONG> m_numEventsStored;
+    LONG m_numEventsWritten;
+#endif // _DEBUG
+
+    // Allocate a new buffer for the specified thread.
+    // This function will store the buffer in the thread's buffer list for future use and also return it here.
+    // A NULL return value means that a buffer could not be allocated.
+    EventPipeBuffer* AllocateBufferForThread(Thread *pThread, unsigned int requestSize);
+
+    // Add a buffer to the thread buffer list.
+    void AddBufferToThreadBufferList(EventPipeBufferList *pThreadBuffers, EventPipeBuffer *pBuffer);
+
+    // Find the thread that owns the oldest buffer that is eligible to be stolen.
+    EventPipeBufferList* FindThreadToStealFrom();
+
+    // De-allocates the input buffer.
+    void DeAllocateBuffer(EventPipeBuffer *pBuffer);
+
+public:
+
+    EventPipeBufferManager();
+
+    // Write an event to the input thread's current event buffer.
+    // An optional eventThread can be provided for sample profiler events.
+    // This is because the thread that writes the events is not the same as the "event thread".
+    // An optional stack trace can be provided for sample profiler events.
+    // Otherwise, if a stack trace is needed, one will be automatically collected.
+    bool WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *pData, unsigned int length, Thread *pEventThread = NULL, StackContents *pStack = NULL);
+
+    // Write the contents of the managed buffers to the specified file.
+    // The stopTimeStamp is used to determine when tracing was stopped to ensure that we
+    // skip any events that might be partially written due to races when tracing is stopped.
+    void WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_INTEGER stopTimeStamp);
+
+    // Attempt to de-allocate resources as best we can.  It is possible for some buffers to leak because
+    // threads can be in the middle of a write operation and get blocked, and we may not get an opportunity
+    // to free their buffer for a very long time.
+    void DeAllocateBuffers();
+
+#ifdef _DEBUG
+    bool EnsureConsistency();
+#endif // _DEBUG
+};
+
+// Represents a list of buffers associated with a specific thread.
+class EventPipeBufferList
+{
+private:
+
+    // The buffer manager that owns this list.
+    EventPipeBufferManager *m_pManager;
+
+    // Buffers are stored in an intrusive linked-list from oldest to newest.
+    // Head is the oldest buffer.  Tail is the newest (and currently used) buffer.
+    EventPipeBuffer *m_pHeadBuffer;
+    EventPipeBuffer *m_pTailBuffer;
+
+    // The number of buffers in the list.
+    unsigned int m_bufferCount;
+
+    // The current read buffer (used when processing events on tracing stop).
+    EventPipeBuffer *m_pReadBuffer;
+
+    // True if this thread is owned by a thread.
+    // If it is false, then this buffer can be de-allocated after it is drained.
+    Volatile<bool> m_ownedByThread;
+
+#ifdef _DEBUG
+    // For diagnostics, keep the thread pointer.
+    Thread *m_pCreatingThread;
+#endif // _DEBUG
+
+public:
+
+    EventPipeBufferList(EventPipeBufferManager *pManager);
+
+    // Get the head node of the list.
+    EventPipeBuffer* GetHead();
+
+    // Get the tail node of the list.
+    EventPipeBuffer* GetTail();
+
+    // Insert a new buffer at the tail of the list.
+    void InsertTail(EventPipeBuffer *pBuffer);
+
+    // Remove the head node of the list.
+    EventPipeBuffer* GetAndRemoveHead();
+
+    // Get the count of buffers in the list.
+    unsigned int GetCount() const;
+
+    // Get the next event as long as it is before the specified timestamp.
+    EventPipeEventInstance* PeekNextEvent(LARGE_INTEGER beforeTimeStamp, EventPipeBuffer **pContainingBuffer);
+
+    // Get the next event as long as it is before the specified timestamp, and also mark it as read.
+    EventPipeEventInstance* PopNextEvent(LARGE_INTEGER beforeTimeStamp);
+
+    // True if a thread owns this list.
+    bool OwnedByThread();
+
+    // Set whether or not this list is owned by a thread.
+    // If it is not owned by a thread, then it can be de-allocated
+    // after the buffer is drained.
+    // The default value is true.
+    void SetOwnedByThread(bool value);
+
+#ifdef _DEBUG
+    // Get the thread associated with this list.
+    Thread* GetThread();
+
+    // Validate the consistency of the list.
+    // This function will assert if the list is in an inconsistent state.
+    bool EnsureConsistency();
+#endif // _DEBUG
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_BUFFERMANAGER_H__
index 0128685..73fa963 100644 (file)
@@ -18,6 +18,9 @@ EventPipeConfiguration::EventPipeConfiguration()
 {
     STANDARD_VM_CONTRACT;
 
+    m_enabled = false;
+    m_circularBufferSizeInBytes = 1024 * 1024 * 1000; // Default to 1000MB.
+    m_pEnabledProviderList = NULL;
     m_pProviderList = new SList<SListElem<EventPipeProvider*>>();
 }
 
@@ -31,6 +34,12 @@ EventPipeConfiguration::~EventPipeConfiguration()
     }
     CONTRACTL_END;
 
+    if(m_pEnabledProviderList != NULL)
+    {
+        delete(m_pEnabledProviderList);
+        m_pEnabledProviderList = NULL;
+    }
+
     if(m_pProviderList != NULL)
     {
         delete(m_pProviderList);
@@ -56,7 +65,7 @@ void EventPipeConfiguration::Initialize()
         0,      /* keywords */
         0,      /* eventID */
         0,      /* eventVersion */
-        EventPipeEventLevel::Critical,
+        EventPipeEventLevel::LogAlways,
         false); /* needStack */
 }
 
@@ -83,9 +92,18 @@ bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider)
     // The provider has not been registered, so register it.
     m_pProviderList->InsertTail(new SListElem<EventPipeProvider*>(&provider));
 
-    // TODO: Set the provider configuration and enable it if we know
-    // anything about the provider before it is registered.
-    provider.SetConfiguration(true /* providerEnabled */, 0xFFFFFFFFFFFFFFFF /* keywords */, EventPipeEventLevel::Verbose /* level */);
+    // Set the provider configuration and enable it if we know anything about the provider before it is registered.
+    if(m_pEnabledProviderList != NULL)
+    {
+        EventPipeEnabledProvider *pEnabledProvider = m_pEnabledProviderList->GetEnabledProvider(&provider);
+        if(pEnabledProvider != NULL)
+        {
+            provider.SetConfiguration(
+                true /* providerEnabled */,
+                pEnabledProvider->GetKeywords(),
+                pEnabledProvider->GetLevel());
+        }
+    }
 
     return true;
 }
@@ -170,7 +188,27 @@ EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const GUID &provide
     return NULL;
 }
 
-void EventPipeConfiguration::Enable()
+size_t EventPipeConfiguration::GetCircularBufferSize() const
+{
+    LIMITED_METHOD_CONTRACT;
+
+    return m_circularBufferSizeInBytes;
+}
+
+void EventPipeConfiguration::SetCircularBufferSize(size_t circularBufferSize)
+{
+    LIMITED_METHOD_CONTRACT;
+    
+    if(!m_enabled)
+    {
+        m_circularBufferSizeInBytes = circularBufferSize;
+    }
+}
+
+void EventPipeConfiguration::Enable(
+    uint circularBufferSizeInMB,
+    EventPipeProviderConfiguration *pProviders,
+    int numProviders)
 {
     CONTRACTL
     {
@@ -182,12 +220,24 @@ void EventPipeConfiguration::Enable()
     }
     CONTRACTL_END;
 
+    m_circularBufferSizeInBytes = circularBufferSizeInMB * 1024 * 1024;
+    m_pEnabledProviderList = new EventPipeEnabledProviderList(pProviders, static_cast<unsigned int>(numProviders));
+    m_enabled = true;
+
     SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
     while(pElem != NULL)
     {
-        // TODO: Only enable the providers that have been explicitly enabled with specified keywords/level.
         EventPipeProvider *pProvider = pElem->GetValue();
-        pProvider->SetConfiguration(true /* providerEnabled */, 0xFFFFFFFFFFFFFFFF /* keywords */, EventPipeEventLevel::Verbose /* level */);
+
+        // Enable the provider if it has been configured.
+        EventPipeEnabledProvider *pEnabledProvider = m_pEnabledProviderList->GetEnabledProvider(pProvider);
+        if(pEnabledProvider != NULL)
+        {
+            pProvider->SetConfiguration(
+                true /* providerEnabled */,
+                pEnabledProvider->GetKeywords(),
+                pEnabledProvider->GetLevel());
+        }
 
         pElem = m_pProviderList->GetNext(pElem);
     }
@@ -214,9 +264,24 @@ void EventPipeConfiguration::Disable()
 
         pElem = m_pProviderList->GetNext(pElem);
     }
+
+    m_enabled = false;
+
+    // Free the enabled providers list.
+    if(m_pEnabledProviderList != NULL)
+    {
+        delete(m_pEnabledProviderList);
+        m_pEnabledProviderList = NULL;
+    }
+}
+
+bool EventPipeConfiguration::Enabled() const
+{
+    LIMITED_METHOD_CONTRACT;
+    return m_enabled;
 }
 
-EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEvent &sourceEvent, BYTE *pPayloadData, unsigned int payloadLength)
+EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, BYTE *pPayloadData, unsigned int payloadLength)
 {
     CONTRACTL
     {
@@ -233,6 +298,7 @@ EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPip
     // - Optional event description payload.
 
     // Calculate the size of the event.
+    EventPipeEvent &sourceEvent = *sourceInstance.GetEvent();
     const GUID &providerID = sourceEvent.GetProvider()->GetProviderID();
     unsigned int eventID = sourceEvent.GetEventID();
     unsigned int eventVersion = sourceEvent.GetEventVersion();
@@ -266,7 +332,192 @@ EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPip
         pInstancePayload,
         instancePayloadSize);
 
+    // Set the timestamp to match the source event, because the metadata event
+    // will be emitted right before the source event.
+    pInstance->SetTimeStamp(sourceInstance.GetTimeStamp());
+
     return pInstance;
 }
 
+EventPipeEnabledProviderList::EventPipeEnabledProviderList(
+    EventPipeProviderConfiguration *pConfigs,
+    unsigned int numConfigs)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    // Test COMPLUS variable to enable tracing at start-up.
+    // If tracing is enabled at start-up create the catch-all provider and always return it.
+    if((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_PerformanceTracing) & 1) == 1)
+    {
+        m_pCatchAllProvider = new EventPipeEnabledProvider();
+        m_pCatchAllProvider->Set(NULL, 0xFFFFFFFF, EventPipeEventLevel::Verbose);
+        m_pProviders = NULL;
+        m_numProviders = 0;
+        return;
+    }
+
+    m_pCatchAllProvider = NULL;
+    m_numProviders = numConfigs;
+    if(m_numProviders == 0)
+    {
+        return;
+    }
+
+    m_pProviders = new EventPipeEnabledProvider[m_numProviders];
+    for(int i=0; i<m_numProviders; i++)
+    {
+        m_pProviders[i].Set(
+            pConfigs[i].GetProviderName(),
+            pConfigs[i].GetKeywords(),
+            (EventPipeEventLevel)pConfigs[i].GetLevel());
+    }
+}
+
+EventPipeEnabledProviderList::~EventPipeEnabledProviderList()
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    if(m_pProviders != NULL)
+    {
+        delete[] m_pProviders;
+        m_pProviders = NULL;
+    }
+    if(m_pCatchAllProvider != NULL)
+    {
+        delete(m_pCatchAllProvider);
+        m_pCatchAllProvider = NULL;
+    }
+}
+
+EventPipeEnabledProvider* EventPipeEnabledProviderList::GetEnabledProvider(
+    EventPipeProvider *pProvider)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    // If tracing was enabled on start-up, all events should be on (this is a diagnostic config).
+    if(m_pCatchAllProvider != NULL)
+    {
+        return m_pCatchAllProvider;
+    }
+
+    if(m_pProviders == NULL)
+    {
+        return NULL;
+    }
+
+    // TEMPORARY: Convert the provider GUID to a string.
+    const unsigned int guidSize = 39;
+    WCHAR wszProviderID[guidSize];
+    if(!StringFromGUID2(pProvider->GetProviderID(), wszProviderID, guidSize))
+    {
+        wszProviderID[0] = '\0';
+    }
+
+    // Strip off the {}.
+    SString providerNameStr(&wszProviderID[1], guidSize-3);
+    LPCWSTR providerName = providerNameStr.GetUnicode();
+
+    EventPipeEnabledProvider *pEnabledProvider = NULL;
+    for(int i=0; i<m_numProviders; i++)
+    {
+        EventPipeEnabledProvider *pCandidate = &m_pProviders[i];
+        if(pCandidate != NULL)
+        {
+            if(wcscmp(providerName, pCandidate->GetProviderName()) == 0)
+            {
+                pEnabledProvider = pCandidate;
+                break;
+            }
+        }
+    }
+
+    return pEnabledProvider;
+}
+
+EventPipeEnabledProvider::EventPipeEnabledProvider()
+{
+    LIMITED_METHOD_CONTRACT;
+    m_pProviderName = NULL;
+    m_keywords = 0;
+}
+
+EventPipeEnabledProvider::~EventPipeEnabledProvider()
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    if(m_pProviderName != NULL)
+    {
+        delete[] m_pProviderName;
+        m_pProviderName = NULL;
+    }
+}
+
+void EventPipeEnabledProvider::Set(LPCWSTR providerName, UINT64 keywords, EventPipeEventLevel loggingLevel)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    if(m_pProviderName != NULL)
+    {
+        delete(m_pProviderName);
+        m_pProviderName = NULL;
+    }
+
+    if(providerName != NULL)
+    {
+        unsigned int bufSize = wcslen(providerName) + 1;
+        m_pProviderName = new WCHAR[bufSize];
+        wcscpy_s(m_pProviderName, bufSize, providerName);
+    }
+    m_keywords = keywords;
+    m_loggingLevel = loggingLevel;
+}
+
+LPCWSTR EventPipeEnabledProvider::GetProviderName() const
+{
+    LIMITED_METHOD_CONTRACT;
+    return m_pProviderName;
+}
+
+UINT64 EventPipeEnabledProvider::GetKeywords() const
+{
+    LIMITED_METHOD_CONTRACT;
+    return m_keywords;
+}
+
+EventPipeEventLevel EventPipeEnabledProvider::GetLevel() const
+{
+    LIMITED_METHOD_CONTRACT;
+    return m_loggingLevel;
+}
+
 #endif // FEATURE_PERFTRACING
index a237775..baa9b3b 100644 (file)
@@ -8,9 +8,22 @@
 
 #include "slist.h"
 
+class EventPipeEnabledProvider;
+class EventPipeEnabledProviderList;
 class EventPipeEvent;
 class EventPipeEventInstance;
 class EventPipeProvider;
+struct EventPipeProviderConfiguration;
+
+enum class EventPipeEventLevel
+{
+    LogAlways,
+    Critical,
+    Error,
+    Warning,
+    Informational,
+    Verbose
+};
 
 class EventPipeConfiguration
 {
@@ -31,20 +44,42 @@ public:
     // Get the provider with the specified provider ID if it exists.
     EventPipeProvider* GetProvider(const GUID &providerID);
 
+    // Get the configured size of the circular buffer.
+    size_t GetCircularBufferSize() const;
+
+    // Set the configured size of the circular buffer.
+    void SetCircularBufferSize(size_t circularBufferSize);
+
     // Enable the event pipe.
-    void Enable();
+    void Enable(
+        uint circularBufferSizeInMB,
+        EventPipeProviderConfiguration *pProviders,
+        int numProviders);
 
     // Disable the event pipe.
     void Disable();
 
+    // Get the status of the event pipe.
+    bool Enabled() const;
+
     // Get the event used to write metadata to the event stream.
-    EventPipeEventInstance* BuildEventMetadataEvent(EventPipeEvent &sourceEvent, BYTE *pPayloadData = NULL, unsigned int payloadLength = 0);
+    EventPipeEventInstance* BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, BYTE *pPayloadData = NULL, unsigned int payloadLength = 0);
 
 private:
 
     // Get the provider without taking the lock.
     EventPipeProvider* GetProviderNoLock(const GUID &providerID);
 
+    // Determines whether or not the event pipe is enabled.
+    Volatile<bool> m_enabled;
+
+    // The configured size of the circular buffer.
+    size_t m_circularBufferSizeInBytes;
+
+    // EventPipeConfiguration only supports a single session.
+    // This is the set of configurations for each enabled provider.
+    EventPipeEnabledProviderList *m_pEnabledProviderList;
+
     // The list of event pipe providers.
     SList<SListElem<EventPipeProvider*>> *m_pProviderList;
 
@@ -59,6 +94,59 @@ private:
     static const GUID s_configurationProviderID;
 };
 
+class EventPipeEnabledProviderList
+{
+
+private:
+
+    // The number of providers in the list.
+    unsigned int m_numProviders;
+
+    // The list of providers.
+    EventPipeEnabledProvider *m_pProviders;
+
+    // A catch-all provider used when tracing is enabled at start-up
+    // under (COMPlus_PerformanceTracing & 1) == 1.
+    EventPipeEnabledProvider *m_pCatchAllProvider;
+
+public:
+
+    // Create a new list based on the input.
+    EventPipeEnabledProviderList(EventPipeProviderConfiguration *pConfigs, unsigned int numConfigs);
+    ~EventPipeEnabledProviderList();
+
+    // Get the enabled provider for the specified provider.
+    // Return NULL if one doesn't exist.
+    EventPipeEnabledProvider* GetEnabledProvider(EventPipeProvider *pProvider);
+};
+
+class EventPipeEnabledProvider
+{
+private:
+
+    // The provider name.
+    WCHAR *m_pProviderName;
+
+    // The enabled keywords.
+    UINT64 m_keywords;
+
+    // The loging level.
+    EventPipeEventLevel m_loggingLevel;
+
+public:
+
+    EventPipeEnabledProvider();
+    ~EventPipeEnabledProvider();
+
+    void Set(LPCWSTR providerName, UINT64 keywords, EventPipeEventLevel loggingLevel);
+
+    LPCWSTR GetProviderName() const;
+
+    UINT64 GetKeywords() const;
+
+    EventPipeEventLevel GetLevel() const;
+};
+
 #endif // FEATURE_PERFTRACING
 
 #endif // __EVENTPIPE_CONFIGURATION_H__
index 9e42615..3076617 100644 (file)
@@ -35,7 +35,7 @@ private:
     bool m_needStack;
 
     // True if the event is current enabled.
-    bool m_enabled;
+    Volatile<bool> m_enabled;
 
     // Refreshes the runtime state for this event.
     // Called by EventPipeProvider when the provider configuration changes.
index 2bf500b..7877b79 100644 (file)
@@ -24,6 +24,10 @@ EventPipeEventInstance::EventPipeEventInstance(
     }
     CONTRACTL_END;
 
+#ifdef _DEBUG
+    m_debugEventStart = 0xDEADBEEF;
+    m_debugEventEnd = 0xCAFEBABE;
+#endif // _DEBUG
     m_pEvent = &event;
     m_threadID = threadID;
     m_pData = pData;
@@ -34,6 +38,10 @@ EventPipeEventInstance::EventPipeEventInstance(
     {
         EventPipe::WalkManagedStackForCurrentThread(m_stackContents);
     }
+
+#ifdef _DEBUG
+    EnsureConsistency();
+#endif // _DEBUG
 }
 
 StackContents* EventPipeEventInstance::GetStack()
@@ -50,6 +58,13 @@ EventPipeEvent* EventPipeEventInstance::GetEvent() const
     return m_pEvent;
 }
 
+LARGE_INTEGER EventPipeEventInstance::GetTimeStamp() const
+{
+    LIMITED_METHOD_CONTRACT;
+
+    return m_timeStamp;
+}
+
 BYTE* EventPipeEventInstance::GetData() const
 {
     LIMITED_METHOD_CONTRACT;
@@ -113,6 +128,7 @@ void EventPipeEventInstance::FastSerialize(FastSerializer *pSerializer, StreamLa
     }
 }
 
+#ifdef _DEBUG
 void EventPipeEventInstance::SerializeToJsonFile(EventPipeJsonFile *pFile)
 {
     CONTRACTL
@@ -147,6 +163,35 @@ void EventPipeEventInstance::SerializeToJsonFile(EventPipeJsonFile *pFile)
     }
     EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
 }
+#endif
+
+void EventPipeEventInstance::SetTimeStamp(LARGE_INTEGER timeStamp)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    m_timeStamp = timeStamp;
+}
+
+#ifdef _DEBUG
+bool EventPipeEventInstance::EnsureConsistency()
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    // Validate event start.
+    _ASSERTE(m_debugEventStart == 0xDEADBEEF);
+
+    // Validate event end.
+    _ASSERTE(m_debugEventEnd == 0xCAFEBABE);
+
+    return true;
+}
+#endif // _DEBUG
 
 SampleProfilerEventInstance::SampleProfilerEventInstance(Thread *pThread)
     :EventPipeEventInstance(*SampleProfiler::s_pThreadTimeEvent, pThread->GetOSThreadId(), NULL, 0)
index 84ad566..7e0ea47 100644 (file)
@@ -14,6 +14,8 @@
 
 class EventPipeEventInstance
 {
+    // Declare friends.
+    friend EventPipeConfiguration;
 
 public:
 
@@ -25,6 +27,9 @@ public:
     // Get the stack contents object to either read or write to it.
     StackContents* GetStack();
 
+    // Get the timestamp.
+    LARGE_INTEGER GetTimeStamp() const;
+
     // Get a pointer to the data payload.
     BYTE* GetData() const;
 
@@ -34,11 +39,19 @@ public:
     // Serialize this object using FastSerialization.
     void FastSerialize(FastSerializer *pSerializer, StreamLabel metadataLabel);
 
+#ifdef _DEBUG
     // Serialize this event to the JSON file.
     void SerializeToJsonFile(EventPipeJsonFile *pFile);
 
+    bool EnsureConsistency();
+#endif // _DEBUG
+
 protected:
 
+#ifdef _DEBUG
+    unsigned int m_debugEventStart;
+#endif // _DEBUG
+
     EventPipeEvent *m_pEvent;
     DWORD m_threadID;
     LARGE_INTEGER m_timeStamp;
@@ -46,6 +59,17 @@ protected:
     BYTE *m_pData;
     unsigned int m_dataLength;
     StackContents m_stackContents;
+
+#ifdef _DEBUG
+    unsigned int m_debugEventEnd;
+#endif // _DEBUG
+
+private:
+
+    // This is used for metadata events by EventPipeConfiguration because
+    // the metadata event is created after the first instance of the event
+    // but must be inserted into the file before the first instance of the event.
+    void SetTimeStamp(LARGE_INTEGER timeStamp);
 };
 
 // A specific type of event instance for use by the SampleProfiler.
index 895f732..f574814 100644 (file)
@@ -3,12 +3,19 @@
 // See the LICENSE file in the project root for more information.
 
 #include "common.h"
+#include "eventpipebuffer.h"
 #include "eventpipeconfiguration.h"
 #include "eventpipefile.h"
 
 #ifdef FEATURE_PERFTRACING
 
-EventPipeFile::EventPipeFile(SString &outputFilePath)
+EventPipeFile::EventPipeFile(
+    SString &outputFilePath
+#ifdef _DEBUG
+    ,
+    bool lockOnWrite
+#endif // _DEBUG
+)
 {
     CONTRACTL
     {
@@ -22,6 +29,10 @@ EventPipeFile::EventPipeFile(SString &outputFilePath)
     m_serializationLock.Init(LOCK_TYPE_DEFAULT);
     m_pMetadataLabels = new MapSHashWithRemove<EventPipeEvent*, StreamLabel>();
 
+#ifdef _DEBUG
+    m_lockOnWrite = lockOnWrite;
+#endif // _DEBUG
+
     // File start time information.
     GetSystemTime(&m_fileOpenSystemTime);
     QueryPerformanceCounter(&m_fileOpenTimeStamp);
@@ -78,22 +89,29 @@ void EventPipeFile::WriteEvent(EventPipeEventInstance &instance)
     }
     CONTRACTL_END;
 
-    // Take the serialization lock.
-    SpinLockHolder _slh(&m_serializationLock);
+#ifdef _DEBUG
+    if(m_lockOnWrite)
+    {
+        // Take the serialization lock.
+        // This is used for synchronous file writes.
+        // The circular buffer path only writes from one thread.
+        SpinLockHolder _slh(&m_serializationLock);
+    }
+#endif // _DEBUG
 
     // Check to see if we've seen this event type before.
     // If not, then write the event metadata to the event stream first.
     StreamLabel metadataLabel = GetMetadataLabel(*instance.GetEvent());
     if(metadataLabel == 0)
     {
-        EventPipeEventInstance* pMetadataInstance = EventPipe::GetConfiguration()->BuildEventMetadataEvent(*instance.GetEvent());
+        EventPipeEventInstance* pMetadataInstance = EventPipe::GetConfiguration()->BuildEventMetadataEvent(instance);
 
         metadataLabel = m_pSerializer->GetStreamLabel();
         pMetadataInstance->FastSerialize(m_pSerializer, (StreamLabel)0); // 0 breaks recursion and represents the metadata event.
 
         SaveMetadataLabel(*instance.GetEvent(), metadataLabel);
 
-        delete (pMetadataInstance->GetData());
+        delete[] (pMetadataInstance->GetData());
         delete (pMetadataInstance);
     }
 
index 1fbb4c0..2f68535 100644 (file)
 class EventPipeFile : public FastSerializableObject
 {
     public:
-        EventPipeFile(SString &outputFilePath);
+
+        EventPipeFile(SString &outputFilePath
+#ifdef _DEBUG
+            ,
+            bool lockOnWrite = false
+#endif // _DEBUG
+        );
         ~EventPipeFile();
 
         // Write an event to the file.
@@ -68,6 +74,10 @@ class EventPipeFile : public FastSerializableObject
 
         // Hashtable of metadata labels.
         MapSHashWithRemove<EventPipeEvent*, StreamLabel> *m_pMetadataLabels;
+
+#ifdef _DEBUG
+        bool m_lockOnWrite;
+#endif // _DEBUG
 };
 
 #endif // FEATURE_PERFTRACING
index ea2dd29..f769590 100644 (file)
@@ -5,6 +5,7 @@
 #include "common.h"
 #include "eventpipejsonfile.h"
 
+#ifdef _DEBUG
 #ifdef FEATURE_PERFTRACING
 
 EventPipeJsonFile::EventPipeJsonFile(SString &outFilePath)
@@ -140,4 +141,5 @@ void EventPipeJsonFile::FormatCallStack(StackContents &stackContents, SString &r
     }
 }
 
+#endif // _DEBUG
 #endif // FEATURE_PERFTRACING
index 2b836d2..7686db7 100644 (file)
@@ -6,6 +6,7 @@
 #ifndef __EVENTPIPE_JSONFILE_H__
 #define __EVENTPIPE_JSONFILE_H__
 
+#ifdef _DEBUG
 #ifdef FEATURE_PERFTRACING
 
 #include "common.h"
@@ -44,5 +45,6 @@ class EventPipeJsonFile
 };
 
 #endif // FEATURE_PERFTRACING
+#endif // _DEBUG
 
 #endif // __EVENTPIPE_JSONFILE_H__
index 362c37a..be87fb4 100644 (file)
@@ -27,11 +27,11 @@ EventPipeProvider::EventPipeProvider(const GUID &providerID, EventPipeCallback p
     m_pEventList = new SList<SListElem<EventPipeEvent*>>();
     m_pCallbackFunction = pCallbackFunction;
     m_pCallbackData = pCallbackData;
+    m_pConfig = EventPipe::GetConfiguration();
+    _ASSERTE(m_pConfig != NULL);
 
     // Register the provider.
-    EventPipeConfiguration* pConfig = EventPipe::GetConfiguration();
-    _ASSERTE(pConfig != NULL);
-    pConfig->RegisterProvider(*this);
+    m_pConfig->RegisterProvider(*this);
 }
 
 EventPipeProvider::~EventPipeProvider()
@@ -46,6 +46,9 @@ EventPipeProvider::~EventPipeProvider()
 
     // Unregister the provider.
     // This call is re-entrant.
+    // NOTE: We don't use the cached event pipe configuration pointer
+    // in case this runs during shutdown and the configuration has already
+    // been freed.
     EventPipeConfiguration* pConfig = EventPipe::GetConfiguration();
     _ASSERTE(pConfig != NULL);
     pConfig->UnregisterProvider(*this);
@@ -81,7 +84,7 @@ bool EventPipeProvider::Enabled() const
 {
     LIMITED_METHOD_CONTRACT;
 
-    return m_enabled;
+    return (m_pConfig->Enabled() && m_enabled);
 }
 
 bool EventPipeProvider::EventEnabled(INT64 keywords) const
@@ -163,6 +166,7 @@ void EventPipeProvider::AddEvent(EventPipeEvent &event)
     CrstHolder _crst(EventPipe::GetLock());
 
     m_pEventList->InsertTail(new SListElem<EventPipeEvent*>(&event));
+    event.RefreshState();
 }
 
 void EventPipeProvider::InvokeCallback()
index d1ce158..7aaa8e4 100644 (file)
@@ -7,6 +7,7 @@
 
 #ifdef FEATURE_PERFTRACING
 
+#include "eventpipeconfiguration.h"
 #include "slist.h"
 
 class EventPipeEvent;
@@ -21,16 +22,6 @@ typedef void (*EventPipeCallback)(
     void *FilterData,
     void *CallbackContext);
 
-enum class EventPipeEventLevel
-{
-    LogAlways,
-    Critical,
-    Error,
-    Warning,
-    Informational,
-    Verbose
-};
-
 class EventPipeProvider
 {
     // Declare friends.
@@ -59,6 +50,9 @@ private:
     // The optional provider callback data pointer.
     void *m_pCallbackData;
 
+    // The configuration object.
+    EventPipeConfiguration *m_pConfig;
+
 public:
 
     EventPipeProvider(const GUID &providerID, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL);
index 6a6a23a..7c6429a 100644 (file)
@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 #include "common.h"
+#include "eventpipebuffermanager.h"
 #include "eventpipeeventinstance.h"
 #include "sampleprofiler.h"
 #include "hosting.h"
@@ -136,7 +137,7 @@ DWORD WINAPI SampleProfiler::ThreadProc(void *args)
         }
     }
 
-    // Destroy the sampling thread when done running.
+    // Destroy the sampling thread when it is done running.
     DestroyThread(s_pSamplingThread);
     s_pSamplingThread = NULL;
 
@@ -158,19 +159,18 @@ void SampleProfiler::WalkManagedThreads()
     }
     CONTRACTL_END;
 
-    Thread *pThread = NULL;
+    Thread *pTargetThread = NULL;
 
     // Iterate over all managed threads.
     // Assumes that the ThreadStoreLock is held because we've suspended all threads.
-    while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
+    while ((pTargetThread = ThreadStore::GetThreadList(pTargetThread)) != NULL)
     {
-        SampleProfilerEventInstance instance(pThread);
-        StackContents &stackContents = *(instance.GetStack());
+        StackContents stackContents;
 
         // Walk the stack and write it out as an event.
-        if(EventPipe::WalkManagedStackForThread(pThread, stackContents) && !stackContents.IsEmpty())
+        if(EventPipe::WalkManagedStackForThread(pTargetThread, stackContents) && !stackContents.IsEmpty())
         {
-            EventPipe::WriteSampleProfileEvent(instance);
+            EventPipe::WriteSampleProfileEvent(s_pSamplingThread, pTargetThread, stackContents);
         }
     }
 }
index 5ad388d..19deb08 100644 (file)
@@ -14,6 +14,7 @@ class SampleProfiler
 {
 
     // Declare friends.
+    friend class EventPipe;
     friend class SampleProfilerEventInstance;
 
     public:
index c36232e..df8916c 100644 (file)
 #include "olecontexthelpers.h"
 #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
 
+#ifdef FEATURE_PERFTRACING
+#include "eventpipebuffermanager.h"
+#endif // FEATURE_PERFTRACING
+
 
 
 SPTR_IMPL(ThreadStore, ThreadStore, s_pThreadStore);
@@ -988,6 +992,16 @@ void DestroyThread(Thread *th)
         th->SetThreadState(Thread::TS_ReportDead);
         th->OnThreadTerminate(FALSE);
     }
+
+#ifdef FEATURE_PERFTRACING
+    // Before the thread dies, mark its buffers as no longer owned
+    // so that they can be cleaned up after the thread dies.
+    EventPipeBufferList *pBufferList = th->GetEventPipeBufferList();
+    if(pBufferList != NULL)
+    {
+        pBufferList->SetOwnedByThread(false);
+    }
+#endif // FEATURE_PERFTRACING
 }
 
 //-------------------------------------------------------------------------
@@ -1084,6 +1098,16 @@ HRESULT Thread::DetachThread(BOOL fDLLThreadDetach)
     m_pClrDebugState = NULL;
 #endif //ENABLE_CONTRACTS_DATA
 
+#ifdef FEATURE_PERFTRACING
+    // Before the thread dies, mark its buffers as no longer owned
+    // so that they can be cleaned up after the thread dies.
+    EventPipeBufferList *pBufferList = m_pEventPipeBufferList.Load();
+    if(pBufferList != NULL)
+    {
+        pBufferList->SetOwnedByThread(false);
+    }
+#endif // FEATURE_PERFTRACING
+
     FastInterlockOr((ULONG*)&m_State, (int) (Thread::TS_Detached | Thread::TS_ReportDead));
     // Do not touch Thread object any more.  It may be destroyed.
 
@@ -2008,6 +2032,11 @@ Thread::Thread()
 #endif
 
     m_pAllLoggedTypes = NULL;
+
+#ifdef FEATURE_PERFTRACING
+    m_pEventPipeBufferList = NULL;
+    m_eventWriteInProgress = false;
+#endif // FEATURE_PERFTRACING
     m_HijackReturnKind = RT_Illegal;
 }
 
index 34fca24..fbff1b9 100644 (file)
@@ -185,6 +185,10 @@ typedef DPTR(PTR_ThreadLocalBlock) PTR_PTR_ThreadLocalBlock;
 #include "interoputil.h"
 #include "eventtrace.h"
 
+#ifdef FEATURE_PERFTRACING
+class EventPipeBufferList;
+#endif // FEATURE_PERFTRACING
+
 #ifdef CROSSGEN_COMPILE
 
 #include "asmconstants.h"
@@ -5334,6 +5338,40 @@ public:
         m_pAllLoggedTypes = pAllLoggedTypes;
     }
 
+#ifdef FEATURE_PERFTRACING
+private:
+    // The object that contains the list write buffers used by this thread.
+    Volatile<EventPipeBufferList*> m_pEventPipeBufferList;
+
+    // Whether or not the thread is currently writing an event.
+    Volatile<bool> m_eventWriteInProgress;
+
+public:
+    EventPipeBufferList* GetEventPipeBufferList()
+    {
+        LIMITED_METHOD_CONTRACT;
+        return m_pEventPipeBufferList;
+    }
+
+    void SetEventPipeBufferList(EventPipeBufferList *pList)
+    {
+        LIMITED_METHOD_CONTRACT;
+        m_pEventPipeBufferList = pList;
+    }
+
+    bool GetEventWriteInProgress() const
+    {
+        LIMITED_METHOD_CONTRACT;
+        return m_eventWriteInProgress;
+    }
+
+    void SetEventWriteInProgress(bool value)
+    {
+        LIMITED_METHOD_CONTRACT;
+        m_eventWriteInProgress = value;
+    }
+#endif // FEATURE_PERFTRACING
+
 #ifdef FEATURE_HIJACK
 private: