Add tests for notification profilers (#57429)
authorDavid Mason <davmason@microsoft.com>
Tue, 17 Aug 2021 18:32:20 +0000 (11:32 -0700)
committerGitHub <noreply@github.com>
Tue, 17 Aug 2021 18:32:20 +0000 (11:32 -0700)
Add CI tests for notification profilers, and fix a couple things to make life with multiple profilers easier
  * Reduced default timeout for profiler detach - we used to try at 300ms, 600ms, 5s, 10s, and then 10 minutes. The check is just a read of an int, so there's no reason to wait 10 minutes
  * Got rid of a couple asserts that were wrong since switching from the dedicated profiler attach thread to the diagnostics server implementation
  * ProfControlBlock::GetProfilerInfo would only work for active profilers, not attaching profilers, which could cause subtle bugs

30 files changed:
src/coreclr/dlls/mscorrc/mscorrc.rc
src/coreclr/inc/profilepriv.h
src/coreclr/inc/profilepriv.inl
src/coreclr/vm/eetoprofinterfaceimpl.cpp
src/coreclr/vm/profdetach.cpp
src/coreclr/vm/proftoeeinterfaceimpl.cpp
src/tests/profiler/common/ProfilerTestRunner.cs
src/tests/profiler/multiple/multiple.cs [new file with mode: 0644]
src/tests/profiler/multiple/multiple.csproj [new file with mode: 0644]
src/tests/profiler/native/CMakeLists.txt
src/tests/profiler/native/classfactory.cpp
src/tests/profiler/native/eltprofiler/slowpatheltprofiler.h
src/tests/profiler/native/eventpipeprofiler/eventpipereadingprofiler.h
src/tests/profiler/native/eventpipeprofiler/eventpipewritingprofiler.h
src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h
src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.h
src/tests/profiler/native/gcprofiler/gcprofiler.h
src/tests/profiler/native/getappdomainstaticaddress/getappdomainstaticaddress.h
src/tests/profiler/native/guids.cpp
src/tests/profiler/native/metadatagetdispenser/metadatagetdispenser.h
src/tests/profiler/native/multiple/multiple.cpp [new file with mode: 0644]
src/tests/profiler/native/multiple/multiple.h [new file with mode: 0644]
src/tests/profiler/native/nullprofiler/nullprofiler.h
src/tests/profiler/native/profiler.cpp
src/tests/profiler/native/profiler.h
src/tests/profiler/native/rejitprofiler/rejitprofiler.h
src/tests/profiler/native/releaseondetach/releaseondetach.cpp
src/tests/profiler/native/releaseondetach/releaseondetach.h
src/tests/profiler/native/transitions/transitions.h
src/tests/profiler/unittest/releaseondetach.cs

index e8955bf..161f6e1 100644 (file)
@@ -726,29 +726,29 @@ END
 // Profiler messages for event log
 STRINGTABLE DISCARDABLE
 BEGIN
-        IDS_E_PROF_BAD_PATH                     "Loading profiler failed.  COR_ENABLE_PROFILING and COR_PROFILER were set properly, but COR_PROFILER_PATH was not.  COR_PROFILER_PATH must be set to the full path of the profiler DLL to load with no more than 260 charaters including the null terminator."
-        IDS_E_PROF_NO_CLSID                     "Loading profiler failed.  COR_ENABLE_PROFILING was set properly, but COR_PROFILER was not.  COR_PROFILER must be set to the CLSID of the profiler to load."
-        IDS_E_PROF_INTERNAL_INIT                "Loading profiler failed due to an internal profiling services initialization failure.  Profiler CLSID: '%s'.  HRESULT: 0x%x."
-        IDS_E_PROF_BAD_CLSID                    "Loading profiler failed.  COR_PROFILER is set to an invalid CLSID: '%s'.  HRESULT: 0x%x."
-        IDS_E_PROF_NO_CALLBACK_IFACE            "Loading profiler failed.  COR_PROFILER is set to a CLSID of a COM object that does not implement the interface GUID (IID) requested by the CLR.  This often indicates that the profiler does not support this version of the CLR.  Profiler CLSID: '%s'.  Requested IID: '%s'."
-        IDS_E_PROF_CCI_FAILED                   "Loading profiler failed during CoCreateInstance.  Profiler CLSID: '%s'.  HRESULT: 0x%x."
-        IDS_E_PROF_INIT_CALLBACK_FAILED         "Loading profiler failed.  The profiler COM object was instantiated, but the profiler failed during its initialization callback.  Profiler CLSID: '%s'.  HRESULT: 0x%x."
-        IDS_PROF_SUPPLEMENTARY_INFO             "Process ID (decimal): %d.  Message ID: [0x%x]."
-        IDS_PROF_LOAD_COMPLETE                  "The profiler was loaded successfully.  Profiler CLSID: '%s'."
-        IDS_E_PROF_NOT_ATTACHABLE               "Loading profiler failed.  The profiler COM object was instantiated, but the profiler does not support attaching to a live process.  The profiler must be loaded at application startup by using a launcher program included with the profiler (if any) or by setting the COR_ENABLE_PROFILING and COR_PROFILER environment variables before launching the application to be profiled.  Profiler CLSID: '%s'"
-        IDS_E_PROF_UNHANDLED_EXCEPTION_ON_LOAD  "Loading profiler failed.  There was an unhandled exception while trying to instantiate the profiler COM object.  Please ensure the CLSID is associated with a valid profiler designed to work with this version of the runtime.  Profiler CLSID: '%s'."
-        IDS_PROF_ATTACH_REQUEST_RECEIVED        "The CLR received a request to attach a profiler. Profiler CLSID: '%s'."
-        IDS_PROF_DETACH_INITIATED               "The profiler currently in use has requested to be detached from the process.  The CLR has disabled communication with the profiler and will unload the profiler when it is safe to do so."
-        IDS_PROF_DETACH_COMPLETE                "The CLR has fully detached and unloaded the profiler."
-        IDS_PROF_DETACH_THREAD_ERROR            "There was an internal failure in the profiling API detach infrastructure.  The profiler will not be able to be detached.  Error code: %d."
-        IDS_PROF_CANCEL_ACTIVATION              "The profiler has requested that the CLR instance not load the profiler into this process.  Profiler CLSID: '%s'."
-        IDS_PROF_V2PROFILER_DISABLED            "Loading profiler failed.   The profiler that was configured to load was designed for an older version of the CLR.  You can use the COMPlus_ProfAPI_ProfilerCompatibilitySetting environment variable to allow older profilers to be loaded by the current version of the CLR.  Please consult the documentation for information on how to use this environment variable, and the risks associated with it.  Profiler CLSID: '%s'."
-        IDS_PROF_V2PROFILER_ENABLED             "A profiler designed for an older version of the CLR was loaded because of the environment variable setting below.  Older profilers will continue to work in many cases, but if you encounter problems, please consider upgrading the profiler or changing the setting of the environment variable.  Please consult the documentation for information on how to use this environment variable, and the risks associated with it.  Environment variable setting: %s=%s.  Profiler CLSID: '%s'."
-        IDS_PROF_PROFILER_DISABLED              "Profilers will not be loaded by the current version of the CLR because of the environment variable setting below.  Please consult the documentation for information on how to use this environment variable, and the risks associated with it.  Environment variable setting: %s=%s. Profiler CLSID: '%s'."
-        IDS_E_PROF_NOTIFICATION_DISABLED        "Profiler was prevented from loading notification profiler due to app settings."
-        IDS_E_PROF_NOTIFICATION_LIMIT_EXCEEDED  "Notification profiler was prevented from loading because the limit of notification profilers was reached."
-        IDS_E_PROF_TIMEOUT_WAITING_FOR_CONCURRENT_GC    "Profiler timed out on waiting for concurrent GC to finish after '%d' milliseconds. Please configure your profiler to increase its attaching time out value or consult the documentation for the COMPlus_ProfAPI_AttachProfilerMinTimeoutInMs environment variable and try again. Profiler CLSID: '%s'."
-        IDS_PROF_ALREADY_LOADED                 "A request was made to load a profiler when a profiler was already loaded."
+        IDS_E_PROF_BAD_PATH                     "Loading profiler failed.  COR_ENABLE_PROFILING and COR_PROFILER were set properly, but COR_PROFILER_PATH was not.  COR_PROFILER_PATH must be set to the full path of the profiler DLL to load with no more than 260 charaters including the null terminator.\n"
+        IDS_E_PROF_NO_CLSID                     "Loading profiler failed.  COR_ENABLE_PROFILING was set properly, but COR_PROFILER was not.  COR_PROFILER must be set to the CLSID of the profiler to load.\n"
+        IDS_E_PROF_INTERNAL_INIT                "Loading profiler failed due to an internal profiling services initialization failure.  Profiler CLSID: '%s'.  HRESULT: 0x%x.\n"
+        IDS_E_PROF_BAD_CLSID                    "Loading profiler failed.  COR_PROFILER is set to an invalid CLSID: '%s'.  HRESULT: 0x%x.\n"
+        IDS_E_PROF_NO_CALLBACK_IFACE            "Loading profiler failed.  COR_PROFILER is set to a CLSID of a COM object that does not implement the interface GUID (IID) requested by the CLR.  This often indicates that the profiler does not support this version of the CLR.  Profiler CLSID: '%s'.  Requested IID: '%s'.\n"
+        IDS_E_PROF_CCI_FAILED                   "Loading profiler failed during CoCreateInstance.  Profiler CLSID: '%s'.  HRESULT: 0x%x.\n"
+        IDS_E_PROF_INIT_CALLBACK_FAILED         "Loading profiler failed.  The profiler COM object was instantiated, but the profiler failed during its initialization callback.  Profiler CLSID: '%s'.  HRESULT: 0x%x.\n"
+        IDS_PROF_SUPPLEMENTARY_INFO             "Process ID (decimal): %d.  Message ID: [0x%x].\n"
+        IDS_PROF_LOAD_COMPLETE                  "The profiler was loaded successfully.  Profiler CLSID: '%s'.\n"
+        IDS_E_PROF_NOT_ATTACHABLE               "Loading profiler failed.  The profiler COM object was instantiated, but the profiler does not support attaching to a live process.  The profiler must be loaded at application startup by using a launcher program included with the profiler (if any) or by setting the COR_ENABLE_PROFILING and COR_PROFILER environment variables before launching the application to be profiled.  Profiler CLSID: '%s'\n"
+        IDS_E_PROF_UNHANDLED_EXCEPTION_ON_LOAD  "Loading profiler failed.  There was an unhandled exception while trying to instantiate the profiler COM object.  Please ensure the CLSID is associated with a valid profiler designed to work with this version of the runtime.  Profiler CLSID: '%s'.\n"
+        IDS_PROF_ATTACH_REQUEST_RECEIVED        "The CLR received a request to attach a profiler. Profiler CLSID: '%s'.\n"
+        IDS_PROF_DETACH_INITIATED               "The profiler currently in use has requested to be detached from the process.  The CLR has disabled communication with the profiler and will unload the profiler when it is safe to do so.\n"
+        IDS_PROF_DETACH_COMPLETE                "The CLR has fully detached and unloaded the profiler.\n"
+        IDS_PROF_DETACH_THREAD_ERROR            "There was an internal failure in the profiling API detach infrastructure.  The profiler will not be able to be detached.  Error code: %d.\n"
+        IDS_PROF_CANCEL_ACTIVATION              "The profiler has requested that the CLR instance not load the profiler into this process.  Profiler CLSID: '%s'.\n"
+        IDS_PROF_V2PROFILER_DISABLED            "Loading profiler failed.   The profiler that was configured to load was designed for an older version of the CLR.  You can use the COMPlus_ProfAPI_ProfilerCompatibilitySetting environment variable to allow older profilers to be loaded by the current version of the CLR.  Please consult the documentation for information on how to use this environment variable, and the risks associated with it.  Profiler CLSID: '%s'.\n"
+        IDS_PROF_V2PROFILER_ENABLED             "A profiler designed for an older version of the CLR was loaded because of the environment variable setting below.  Older profilers will continue to work in many cases, but if you encounter problems, please consider upgrading the profiler or changing the setting of the environment variable.  Please consult the documentation for information on how to use this environment variable, and the risks associated with it.  Environment variable setting: %s=%s.  Profiler CLSID: '%s'.\n"
+        IDS_PROF_PROFILER_DISABLED              "Profilers will not be loaded by the current version of the CLR because of the environment variable setting below.  Please consult the documentation for information on how to use this environment variable, and the risks associated with it.  Environment variable setting: %s=%s. Profiler CLSID: '%s'.\n"
+        IDS_E_PROF_NOTIFICATION_DISABLED        "Profiler was prevented from loading notification profiler due to app settings.\n"
+        IDS_E_PROF_NOTIFICATION_LIMIT_EXCEEDED  "Notification profiler was prevented from loading because the limit of notification profilers was reached.\n"
+        IDS_E_PROF_TIMEOUT_WAITING_FOR_CONCURRENT_GC    "Profiler timed out on waiting for concurrent GC to finish after '%d' milliseconds. Please configure your profiler to increase its attaching time out value or consult the documentation for the COMPlus_ProfAPI_AttachProfilerMinTimeoutInMs environment variable and try again. Profiler CLSID: '%s'.\n"
+        IDS_PROF_ALREADY_LOADED                 "A request was made to load a profiler when a profiler was already loaded.\n"
   END
 
 
index 75d1c3d..4a77b65 100644 (file)
@@ -110,7 +110,7 @@ public:
     EventMask eventMask;
 
     //---------------------------------------------------------------
-    // m_dwProfilerEvacuationCounter keeps track of how many profiler
+    // dwProfilerEvacuationCounter keeps track of how many profiler
     // callback calls remain on the stack
     //---------------------------------------------------------------
     // Why volatile?
index e99591c..cee2dfb 100644 (file)
@@ -168,7 +168,7 @@ inline BOOL ProfControlBlock::IsMainProfiler(ProfToEEInterfaceImpl *pProfToEE)
 inline ProfilerInfo *ProfControlBlock::GetProfilerInfo(ProfToEEInterfaceImpl *pProfToEE)
 {
     ProfilerInfo *pProfilerInfo = NULL;
-    IterateProfilers(ProfilerCallbackType::Active,
+    IterateProfilers(ProfilerCallbackType::ActiveOrInitializing,
                     [](ProfilerInfo *pProfilerInfo, ProfToEEInterfaceImpl *pProfToEE, ProfilerInfo **ppFoundProfilerInfo)
                       {
                           if (pProfilerInfo->pProfInterface->m_pProfToEE == pProfToEE)
index 745f2b6..78a85b9 100644 (file)
@@ -637,9 +637,6 @@ HRESULT EEToProfInterfaceImpl::CreateProfiler(
     }
     CONTRACTL_END;
 
-    // Always called before Thread created.
-    _ASSERTE(GetThreadNULLOk() == NULL);
-
     // Try and CoCreate the registered profiler
     ReleaseHolder<ICorProfilerCallback2> pCallback2;
     HModuleHolder hmodProfilerDLL;
@@ -2771,10 +2768,6 @@ HRESULT EEToProfInterfaceImpl::InitializeForAttach(void * pvClientData, UINT cbC
 
     _ASSERTE(m_pProfToEE != NULL);
 
-    // Attach initialization occurs on the AttachThread, which does not have an EEThread
-    // object
-    _ASSERTE(GetThreadNULLOk() == NULL);
-
     // Should only be called on profilers that support ICorProfilerCallback3
     _ASSERTE(m_pCallback3 != NULL);
 
@@ -2826,10 +2819,6 @@ HRESULT EEToProfInterfaceImpl::ProfilerAttachComplete()
                                 LL_INFO10,
                                 "**PROF: Calling profiler's ProfilerAttachComplete() method.\n"));
 
-    // Attach initialization occurs on the AttachThread, which does not have an EEThread
-    // object
-    _ASSERTE(GetThreadNULLOk() == NULL);
-
     // Should only be called on profilers that support ICorProfilerCallback3
     _ASSERTE(m_pCallback3 != NULL);
 
index 0161bef..ffbe0be 100644 (file)
@@ -170,9 +170,9 @@ HRESULT ProfilingAPIDetach::RequestProfilerDetach(ProfilerInfo *pProfilerInfo, D
 
     if (dwExpectedCompletionMilliseconds == 0)
     {
-        // Pick suitable default if the profiler just leaves this at 0.  5 seconds is
+        // Pick suitable default if the profiler just leaves this at 0. 2.5 seconds is
         // reasonable.
-        dwExpectedCompletionMilliseconds = 5000;
+        dwExpectedCompletionMilliseconds = 2500;
     }
 
     {
@@ -365,8 +365,8 @@ void ProfilingAPIDetach::SleepWhileProfilerEvacuates(ProfilerDetachInfo *pDetach
     const DWORD kdwDefaultMinSleepMs = 300;
 
     // The default "steady state" max sleep is how long we'll wait if, after a couple
-    // tries the profiler still hasn't evacuated. Default to every 10 minutes
-    const DWORD kdwDefaultMaxSleepMs = 600000;
+    // tries the profiler still hasn't evacuated. Default to every 5 seconds
+    const DWORD kdwDefaultMaxSleepMs = 5000;
 
     static DWORD s_dwMinSleepMs = 0;
     static DWORD s_dwMaxSleepMs = 0;
index c1e9833..40b3ce5 100644 (file)
@@ -9545,14 +9545,16 @@ HRESULT ProfToEEInterfaceImpl::RequestProfilerDetach(DWORD dwExpectedCompletionM
     }
     CONTRACTL_END;
 
-    PROFILER_TO_CLR_ENTRYPOINT_SYNC_EX(
+    PROFILER_TO_CLR_ENTRYPOINT_ASYNC_EX(
         kP2EEAllowableAfterAttach | kP2EETriggers,
         (LF_CORPROF,
         LL_INFO1000,
         "**PROF: RequestProfilerDetach.\n"));
 
 #ifdef FEATURE_PROFAPI_ATTACH_DETACH
-    return ProfilingAPIDetach::RequestProfilerDetach(g_profControlBlock.GetProfilerInfo(this), dwExpectedCompletionMilliseconds);
+    ProfilerInfo *pProfilerInfo = g_profControlBlock.GetProfilerInfo(this);
+    _ASSERTE(pProfilerInfo != NULL);
+    return ProfilingAPIDetach::RequestProfilerDetach(pProfilerInfo, dwExpectedCompletionMilliseconds);
 #else // FEATURE_PROFAPI_ATTACH_DETACH
     return E_NOTIMPL;
 #endif // FEATURE_PROFAPI_ATTACH_DETACH
index 38c8546..1c4520d 100644 (file)
@@ -10,6 +10,8 @@ using System.Threading.Tasks;
 
 namespace Profiler.Tests
 {
+    public delegate void ProfilerCallback();
+
     [Flags]
     public enum ProfileeOptions
     {
@@ -27,7 +29,9 @@ namespace Profiler.Tests
                               string profileeArguments = "",
                               ProfileeOptions profileeOptions = ProfileeOptions.None,
                               Dictionary<string, string> envVars = null,
-                              string reverseServerName = null)
+                              string reverseServerName = null,
+                              bool loadAsNotification = false,
+                              int notificationCopies = 1)
         {
             string arguments;
             string program;
@@ -44,8 +48,29 @@ namespace Profiler.Tests
             if (!profileeOptions.HasFlag(ProfileeOptions.NoStartupAttach))
             {
                 envVars.Add("CORECLR_ENABLE_PROFILING", "1");
-                envVars.Add("CORECLR_PROFILER_PATH", profilerPath);
-                envVars.Add("CORECLR_PROFILER", "{" + profilerClsid + "}");
+
+                if (loadAsNotification)
+                {
+                    StringBuilder builder = new StringBuilder();
+                    for(int i = 0; i < notificationCopies; ++i)
+                    {
+                        builder.Append(profilerPath);
+                        builder.Append("=");
+                        builder.Append("{");
+                        builder.Append(profilerClsid.ToString());
+                        builder.Append("}");
+                        builder.Append(";");
+                    }
+
+                    envVars.Add("CORECLR_ENABLE_NOTIFICATION_PROFILERS", "1");
+                    envVars.Add("CORECLR_NOTIFICATION_PROFILERS", builder.ToString());
+
+                }
+                else
+                {
+                    envVars.Add("CORECLR_PROFILER", "{" + profilerClsid + "}");
+                    envVars.Add("CORECLR_PROFILER_PATH", profilerPath);
+                }
             }
 
             if (profileeOptions.HasFlag(ProfileeOptions.OptimizationSensitive))
@@ -123,7 +148,7 @@ namespace Profiler.Tests
             return 100;
         }
 
-        private static string GetProfilerPath()
+        public static string GetProfilerPath()
         {
             string profilerName;
             if (TestLibrary.Utilities.IsWindows)
diff --git a/src/tests/profiler/multiple/multiple.cs b/src/tests/profiler/multiple/multiple.cs
new file mode 100644 (file)
index 0000000..9de72d9
--- /dev/null
@@ -0,0 +1,60 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace Profiler.Tests
+{
+    class MultiplyLoaded
+    {
+        static readonly Guid MultipleProfilerGuid = new Guid("BFA8EF13-E144-49B9-B95C-FC1C150C7651");
+        static readonly string ProfilerPath = ProfilerTestRunner.GetProfilerPath();
+
+        [DllImport("Profiler")]
+        private static extern void PassCallbackToProfiler(ProfilerCallback callback);
+
+        public static int RunTest(String[] args) 
+        {
+            ManualResetEvent _profilerDone = new ManualResetEvent(false);
+            PassCallbackToProfiler(() => _profilerDone.Set());
+
+            ProfilerControlHelpers.AttachProfilerToSelf(MultipleProfilerGuid, ProfilerPath);
+
+            try
+            {
+                Console.WriteLine("Throwing exception");
+                throw new Exception("Test exception!");
+            }
+            catch
+            {
+                // intentionally swallow the exception
+                Console.WriteLine("Exception caught");
+            }
+
+            Console.WriteLine("Waiting for profilers to all detach");
+            if (!_profilerDone.WaitOne(TimeSpan.FromMinutes(5)))
+            {
+                Console.WriteLine("Profiler did not set the callback, test will fail.");
+            }
+
+            return 100;
+        }
+
+        public static int Main(string[] args)
+        {
+            if (args.Length > 0 && args[0].Equals("RunTest", StringComparison.OrdinalIgnoreCase))
+            {
+                return RunTest(args);
+            }
+
+            return ProfilerTestRunner.Run(profileePath: System.Reflection.Assembly.GetExecutingAssembly().Location,
+                                          testName: "MultiplyLoaded",
+                                          profilerClsid: MultipleProfilerGuid,
+                                          loadAsNotification: true,
+                                          notificationCopies: 2);
+        }
+    }
+}
diff --git a/src/tests/profiler/multiple/multiple.csproj b/src/tests/profiler/multiple/multiple.csproj
new file mode 100644 (file)
index 0000000..c7b6a7b
--- /dev/null
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFrameworkIdentifier>.NETCoreApp</TargetFrameworkIdentifier>
+    <OutputType>exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <CLRTestPriority>0</CLRTestPriority>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+    <ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
+    <ProjectReference Include="../common/profiler_common.csproj" />
+    <ProjectReference Include="$(MSBuildThisFileDirectory)/../native/CMakeLists.txt" />
+  </ItemGroup>
+</Project>
index e068fdc..b7357a6 100644 (file)
@@ -12,6 +12,7 @@ set(SOURCES
     gcprofiler/gcprofiler.cpp
     getappdomainstaticaddress/getappdomainstaticaddress.cpp
     metadatagetdispenser/metadatagetdispenser.cpp
+    multiple/multiple.cpp
     nullprofiler/nullprofiler.cpp
     rejitprofiler/rejitprofiler.cpp
     rejitprofiler/ilrewriter.cpp
index fe99992..d09cb4c 100644 (file)
@@ -14,6 +14,7 @@
 #include "rejitprofiler/rejitprofiler.h"
 #include "releaseondetach/releaseondetach.h"
 #include "transitions/transitions.h"
+#include "multiple/multiple.h"
 
 ClassFactory::ClassFactory(REFCLSID clsid) : refCount(0), clsid(clsid)
 {
@@ -60,34 +61,60 @@ HRESULT STDMETHODCALLTYPE ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFI
         return CLASS_E_NOAGGREGATION;
     }
 
-       //A little simplistic, we create an instance of every profiler, then return the one whose CLSID matches
-       Profiler* profilers[] = {
-        new GCAllocateProfiler(),
-               new GCBasicProfiler(),
-        new ReJITProfiler(),
-        new EventPipeReadingProfiler(),
-        new EventPipeWritingProfiler(),
-        new MetaDataGetDispenser(),
-        new GetAppDomainStaticAddress(),
-        new SlowPathELTProfiler(),
-        new GCProfiler(),
-        new ReleaseOnDetach(),
-        new Transitions(),
-        new NullProfiler()
-               // add new profilers here
-       };
-
        Profiler* profiler = nullptr;
-       for (unsigned int i = 0; i < sizeof(profilers)/sizeof(Profiler*); i++)
-       {
-               if (clsid == profilers[i]->GetClsid())
-               {
-                       profiler = profilers[i];
-                       break;
-               }
-       }
-
-       if (profiler == nullptr)
+    if (clsid == GCAllocateProfiler::GetClsid())
+    {
+        profiler = new GCAllocateProfiler();
+    }
+    else if (clsid == GCBasicProfiler::GetClsid())
+    {
+        profiler = new GCBasicProfiler();
+    }
+    else if (clsid == ReJITProfiler::GetClsid())
+    {
+        profiler = new ReJITProfiler();
+    }
+    else if (clsid == EventPipeReadingProfiler::GetClsid())
+    {
+        profiler = new EventPipeReadingProfiler();
+    }
+    else if (clsid == EventPipeWritingProfiler::GetClsid())
+    {
+        profiler = new EventPipeWritingProfiler();
+    }
+    else if (clsid == MetaDataGetDispenser::GetClsid())
+    {
+        profiler = new MetaDataGetDispenser();
+    }
+    else if (clsid == GetAppDomainStaticAddress::GetClsid())
+    {
+        profiler = new GetAppDomainStaticAddress();
+    }
+    else if (clsid == SlowPathELTProfiler::GetClsid())
+    {
+        profiler = new SlowPathELTProfiler();
+    }
+    else if (clsid == GCProfiler::GetClsid())
+    {
+        profiler = new GCProfiler();
+    }
+    else if (clsid == ReleaseOnDetach::GetClsid())
+    {
+        profiler = new ReleaseOnDetach();
+    }
+    else if (clsid == Transitions::GetClsid())
+    {
+        profiler = new Transitions();
+    }
+    else if (clsid == NullProfiler::GetClsid())
+    {
+        profiler = new NullProfiler();
+    }
+    else if (clsid == MultiplyLoaded::GetClsid())
+    {
+        profiler = new MultiplyLoaded();
+    }
+    else
     {
         printf("No profiler found in ClassFactory::CreateInstance. Did you add your profiler to the list?\n");
         return E_FAIL;
index 254ba12..571e677 100644 (file)
@@ -63,7 +63,7 @@ public:
         _testType(TestType::Unknown)
     {}
 
-    virtual GUID GetClsid();
+    static GUID GetClsid();
     virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
     virtual HRESULT STDMETHODCALLTYPE Shutdown();
 
index 1d7a417..2b5bc20 100644 (file)
@@ -22,7 +22,7 @@ public:
         _metadataCache()
     {}
 
-    virtual GUID GetClsid();
+    static GUID GetClsid();
 
     virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
 
index fcb9217..9de3dab 100644 (file)
@@ -17,7 +17,7 @@ public:
         _simpleEvent(0)
     {}
 
-    virtual GUID GetClsid();
+    static GUID GetClsid();
     virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
     virtual HRESULT STDMETHODCALLTYPE Shutdown();
     virtual HRESULT STDMETHODCALLTYPE JITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock);
index 65fb3b1..66095b8 100644 (file)
@@ -14,7 +14,7 @@ public:
         _failures(0)
     {}
 
-       virtual GUID GetClsid();
+       static GUID GetClsid();
     virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
     virtual HRESULT STDMETHODCALLTYPE ObjectAllocated(ObjectID objectId, ClassID classId);
     virtual HRESULT STDMETHODCALLTYPE Shutdown();
index 613f5f1..1e1c44c 100644 (file)
@@ -14,7 +14,7 @@ public:
         _failures(0)
     {}
 
-       virtual GUID GetClsid();
+       static GUID GetClsid();
     virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
     virtual HRESULT STDMETHODCALLTYPE Shutdown();
     virtual HRESULT STDMETHODCALLTYPE GarbageCollectionStarted(int cGenerations, BOOL generationCollected[], COR_PRF_GC_REASON reason);
index 8e0fbeb..ab519f6 100644 (file)
@@ -23,7 +23,7 @@ public:
         _objectReferencesSeen()
     {}
 
-    virtual GUID GetClsid();
+    static GUID GetClsid();
     virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
     virtual HRESULT STDMETHODCALLTYPE Shutdown();
     virtual HRESULT STDMETHODCALLTYPE GarbageCollectionStarted(int cGenerations, BOOL generationCollected[], COR_PRF_GC_REASON reason);
index 1b902a7..740ebbd 100644 (file)
@@ -26,7 +26,7 @@ public:
     GetAppDomainStaticAddress();
     virtual ~GetAppDomainStaticAddress();
 
-    virtual GUID GetClsid() override;
+    static GUID GetClsid();
     virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk) override;
     virtual HRESULT STDMETHODCALLTYPE Shutdown() override;
 
index d8d5cf9..4928404 100644 (file)
@@ -30,6 +30,7 @@ DEFINE_GUID(IID_ICorProfilerCallback7,                 0xF76A2DBA,0x1D52,0x4539,
 DEFINE_GUID(IID_ICorProfilerCallback8,                 0x5BED9B15,0xC079,0x4D47,0xBF,0xE2,0x21,0x5A,0x14,0x0C,0x07,0xE0);
 DEFINE_GUID(IID_ICorProfilerCallback9,                 0x27583EC3,0xC8F5,0x482F,0x80,0x52,0x19,0x4B,0x8C,0xE4,0x70,0x5A);
 DEFINE_GUID(IID_ICorProfilerCallback10,                0xCEC5B60E,0xC69C,0x495F,0x87,0xF6,0x84,0xD2,0x8E,0xE1,0x6F,0xFB);
+DEFINE_GUID(IID_ICorProfilerCallback11,                0x42350846,0xAAED,0x47F7,0xB1,0x28,0xFD,0x0C,0x98,0x88,0x1C,0xDE);
 DEFINE_GUID(IID_ICorProfilerInfo,                      0x28B5557D,0x3F3F,0x48B4,0x90,0xB2,0x5F,0x9E,0xEA,0x2F,0x6C,0x48);
 DEFINE_GUID(IID_ICorProfilerInfo2,                     0xCC0935CD,0xA518,0x487D,0xB0,0xBB,0xA9,0x32,0x14,0xE6,0x54,0x78);
 DEFINE_GUID(IID_ICorProfilerInfo3,                     0xB555ED4F,0x452A,0x4E54,0x8B,0x39,0xB5,0x36,0x0B,0xAD,0x32,0xA0);
index 407fa73..28f08bb 100644 (file)
@@ -18,7 +18,7 @@ public:
     MetaDataGetDispenser();
     virtual ~MetaDataGetDispenser();
 
-    virtual GUID GetClsid();
+    static GUID GetClsid();
     virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
     virtual HRESULT STDMETHODCALLTYPE Shutdown();
 
diff --git a/src/tests/profiler/native/multiple/multiple.cpp b/src/tests/profiler/native/multiple/multiple.cpp
new file mode 100644 (file)
index 0000000..420fe10
--- /dev/null
@@ -0,0 +1,95 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "multiple.h"
+#include <thread>
+
+#define MAX_PROFILERS 3
+
+using std::thread;
+
+std::atomic<int> MultiplyLoaded::_exceptionThrownSeenCount(0);
+std::atomic<int> MultiplyLoaded::_detachCount(0);
+std::atomic<int> MultiplyLoaded::_failures(0);
+
+GUID MultiplyLoaded::GetClsid()
+{
+    // {BFA8EF13-E144-49B9-B95C-FC1C150C7651}
+    GUID clsid = { 0xBFA8EF13, 0xE144, 0x49B9, { 0xB9, 0x5C, 0xFC, 0x1C, 0x15, 0x0C, 0x76, 0x51 } };
+    return clsid;
+}
+
+HRESULT MultiplyLoaded::InitializeCommon(IUnknown* pICorProfilerInfoUnk)
+{
+    Profiler::Initialize(pICorProfilerInfoUnk);
+
+    HRESULT hr = S_OK;
+    printf("Setting exception mask\n");
+    if (FAILED(hr = pCorProfilerInfo->SetEventMask2(COR_PRF_MONITOR_EXCEPTIONS, 0)))
+    {
+        _failures++;
+        printf("FAIL: ICorProfilerInfo::SetEventMask2() failed hr=0x%x", hr);
+        return hr;
+    }
+
+    return S_OK;
+}
+
+HRESULT MultiplyLoaded::Initialize(IUnknown* pICorProfilerInfoUnk)
+{
+    return InitializeCommon(pICorProfilerInfoUnk);
+}
+
+HRESULT MultiplyLoaded::InitializeForAttach(IUnknown* pICorProfilerInfoUnk, void* pvClientData, UINT cbClientData)
+{
+    return InitializeCommon(pICorProfilerInfoUnk);
+}
+
+HRESULT MultiplyLoaded::LoadAsNotficationOnly(BOOL *pbNotificationOnly)
+{
+    *pbNotificationOnly = TRUE;
+    return S_OK;
+}
+
+HRESULT MultiplyLoaded::ProfilerDetachSucceeded()
+{
+    ++_detachCount;
+
+    printf("ProfilerDetachSucceeded _detachCount=%d\n", _detachCount.load());
+    if (_detachCount == (MAX_PROFILERS - 1)
+        &&  _exceptionThrownSeenCount >= (MAX_PROFILERS - 1)
+        &&  _failures == 0)
+    {
+        printf("PROFILER TEST PASSES\n");
+        NotifyManagedCodeViaCallback(pCorProfilerInfo);
+    }
+
+    return S_OK;
+}
+
+HRESULT MultiplyLoaded::ExceptionThrown(ObjectID thrownObjectId)
+{
+    int seen = _exceptionThrownSeenCount++;
+
+    printf("MultiplyLoaded::ExceptionThrown, number seen = %d\n", seen);
+
+    thread detachThread([&]()
+        {
+            printf("Requesting detach!!\n");
+            HRESULT hr = pCorProfilerInfo->RequestProfilerDetach(0);
+            printf("RequestProfilerDetach hr=0x%x\n", hr);
+        });
+
+    detachThread.detach();
+
+    return S_OK;
+}
+
+HRESULT MultiplyLoaded::Shutdown()
+{
+    Profiler::Shutdown();
+
+    fflush(stdout);
+
+    return S_OK;
+}
diff --git a/src/tests/profiler/native/multiple/multiple.h b/src/tests/profiler/native/multiple/multiple.h
new file mode 100644 (file)
index 0000000..56dc794
--- /dev/null
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma once
+
+#include "../profiler.h"
+
+class MultiplyLoaded : public Profiler
+{
+public:
+    MultiplyLoaded() : Profiler()
+    {}
+
+    static GUID GetClsid();
+    virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
+    virtual HRESULT STDMETHODCALLTYPE InitializeForAttach(IUnknown* pICorProfilerInfoUnk, void* pvClientData, UINT cbClientData);
+    virtual HRESULT STDMETHODCALLTYPE Shutdown();
+    virtual HRESULT STDMETHODCALLTYPE LoadAsNotficationOnly(BOOL *pbNotificationOnly);
+
+    virtual HRESULT STDMETHODCALLTYPE ProfilerDetachSucceeded();
+    virtual HRESULT STDMETHODCALLTYPE ExceptionThrown(ObjectID thrownObjectId);
+
+private:
+    static std::atomic<int> _exceptionThrownSeenCount;
+    static std::atomic<int> _detachCount;
+    static std::atomic<int> _failures;
+
+    HRESULT InitializeCommon(IUnknown* pCorProfilerInfoUnk);
+};
index 1d25829..93831ae 100644 (file)
@@ -18,7 +18,7 @@ public:
         
     }
 
-    virtual GUID GetClsid();
+    static GUID GetClsid();
     virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
     virtual HRESULT STDMETHODCALLTYPE Shutdown();
 };
index 8812cba..60eca18 100644 (file)
@@ -12,6 +12,9 @@ Profiler *Profiler::Instance = nullptr;
 std::atomic<bool> ShutdownGuard::s_preventHooks(false);
 std::atomic<int> ShutdownGuard::s_hooksInProgress(0);
 
+ProfilerCallback Profiler::s_callback;
+ManualEvent Profiler::s_callbackSet;
+
 Profiler::Profiler() : refCount(0), pCorProfilerInfo(nullptr)
 {
     Profiler::Instance = this;
@@ -532,9 +535,16 @@ HRESULT STDMETHODCALLTYPE Profiler::EventPipeProviderCreated(EVENTPIPE_PROVIDER
     return S_OK;
 }
 
+HRESULT STDMETHODCALLTYPE Profiler::LoadAsNotficationOnly(BOOL *pbNotificationOnly)
+{
+    *pbNotificationOnly = FALSE;
+    return S_OK;
+}
+
 HRESULT STDMETHODCALLTYPE Profiler::QueryInterface(REFIID riid, void **ppvObject)
 {
-    if (riid == __uuidof(ICorProfilerCallback10) ||
+    if (riid == __uuidof(ICorProfilerCallback11) ||
+        riid == __uuidof(ICorProfilerCallback10) ||
         riid == __uuidof(ICorProfilerCallback9) ||
         riid == __uuidof(ICorProfilerCallback8) ||
         riid == __uuidof(ICorProfilerCallback7) ||
@@ -771,13 +781,13 @@ String Profiler::GetModuleIDName(ModuleID modId)
 void Profiler::SetCallback(ProfilerCallback cb)
 {
     assert(cb != NULL);
-    callback = cb;
-    callbackSet.Signal();
+    s_callback = cb;
+    s_callbackSet.Signal();
 }
 
-void Profiler::NotifyManagedCodeViaCallback()
+void Profiler::NotifyManagedCodeViaCallback(ICorProfilerInfo11 *pCorProfilerInfo)
 {
-    callbackSet.Wait();
+    s_callbackSet.Wait();
 
     thread callbackThread([&]()
     {
@@ -785,7 +795,7 @@ void Profiler::NotifyManagedCodeViaCallback()
         // some crst order asserts if we call back in to managed code. Spin up
         // a new thread to avoid that.
         pCorProfilerInfo->InitializeCurrentThread();
-        callback();
+        s_callback();
     });
 
     callbackThread.join();
@@ -793,7 +803,7 @@ void Profiler::NotifyManagedCodeViaCallback()
 
 extern "C" EXPORT void STDMETHODCALLTYPE PassCallbackToProfiler(ProfilerCallback callback)
 {
-    Profiler::Instance->SetCallback(callback);
+    Profiler::SetCallback(callback);
 }
 
 #ifndef WIN32
index 1086d9a..4bcfa1b 100644 (file)
@@ -84,28 +84,29 @@ public:
         return;                                 \
     }
 
-class Profiler : public ICorProfilerCallback10
+class Profiler : public ICorProfilerCallback11
 {
 private:
     std::atomic<int> refCount;
-    ProfilerCallback callback;
-    ManualEvent callbackSet;
+    static ProfilerCallback s_callback;
+    static ManualEvent s_callbackSet;
     
 
 protected:
+    static void NotifyManagedCodeViaCallback(ICorProfilerInfo11 *pCorProfilerInfo);
+
     String GetClassIDName(ClassID classId);
     String GetFunctionIDName(FunctionID funcId);
     String GetModuleIDName(ModuleID modId);
-    void NotifyManagedCodeViaCallback();
 
 public:
     static Profiler *Instance;
+    static void SetCallback(ProfilerCallback callback);
 
     ICorProfilerInfo11* pCorProfilerInfo;
 
     Profiler();
     virtual ~Profiler();
-       virtual GUID GetClsid() = 0;
     HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk) override;
     HRESULT STDMETHODCALLTYPE Shutdown() override;
     HRESULT STDMETHODCALLTYPE AppDomainCreationStarted(AppDomainID appDomainId) override;
@@ -212,10 +213,9 @@ public:
         ULONG numStackFrames,
         UINT_PTR stackFrames[]) override;
     HRESULT STDMETHODCALLTYPE EventPipeProviderCreated(EVENTPIPE_PROVIDER provider) override;
+    HRESULT STDMETHODCALLTYPE LoadAsNotficationOnly(BOOL *pbNotificationOnly) override;
 
     HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
     ULONG STDMETHODCALLTYPE AddRef(void) override;
     ULONG STDMETHODCALLTYPE Release(void) override;
-
-    void SetCallback(ProfilerCallback callback);
 };
index f105945..c6d0751 100644 (file)
@@ -29,7 +29,7 @@ public:
     ReJITProfiler();
     virtual ~ReJITProfiler() = default;
 
-       virtual GUID GetClsid();
+       static GUID GetClsid();
     virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
     virtual HRESULT STDMETHODCALLTYPE Shutdown();
 
index 3af0fda..5efb373 100644 (file)
@@ -31,7 +31,7 @@ ReleaseOnDetach::~ReleaseOnDetach()
 
     fflush(stdout);
 
-    NotifyManagedCodeViaCallback();
+    NotifyManagedCodeViaCallback(pCorProfilerInfo);
 }
 
 GUID ReleaseOnDetach::GetClsid()
index 0f75a99..458dede 100644 (file)
@@ -21,7 +21,7 @@ public:
     ReleaseOnDetach();
     virtual ~ReleaseOnDetach();
 
-    virtual GUID GetClsid();
+    static GUID GetClsid();
     virtual HRESULT STDMETHODCALLTYPE InitializeForAttach(IUnknown* pCorProfilerInfoUnk, void* pvClientData, UINT cbClientData);
     virtual HRESULT STDMETHODCALLTYPE Shutdown();
 
index 0ddf19a..6c9e9d5 100644 (file)
@@ -11,7 +11,7 @@ public:
     Transitions();
     virtual ~Transitions() = default;
 
-    virtual GUID GetClsid();
+    static GUID GetClsid();
     virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
     virtual HRESULT STDMETHODCALLTYPE Shutdown();
     virtual HRESULT STDMETHODCALLTYPE UnmanagedToManagedTransition(FunctionID functionID, COR_PRF_TRANSITION_REASON reason);
index 8b01318..e50190b 100644 (file)
@@ -9,14 +9,10 @@ using System.Threading;
 
 namespace Profiler.Tests
 {
-    public delegate void ProfilerCallback();
-
     class ReleaseOnShutdown
     {
         private static readonly Guid ReleaseOnShutdownGuid = new Guid("B8C47A29-9C1D-4EEA-ABA0-8E8B3E3B792E");
 
-        private static ManualResetEvent _profilerDone; 
-
         [DllImport("Profiler")]
         private static extern void PassCallbackToProfiler(ProfilerCallback callback);
         
@@ -39,7 +35,7 @@ namespace Profiler.Tests
             string rootPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
             string profilerPath = Path.Combine(rootPath, profilerName);
 
-            _profilerDone = new ManualResetEvent(false);
+            ManualResetEvent _profilerDone = new ManualResetEvent(false);
             Console.WriteLine($"Attaching profiler {profilerPath} to self.");
             ProfilerControlHelpers.AttachProfilerToSelf(ReleaseOnShutdownGuid, profilerPath);