From e9e9f1054d2a262128208f275913e88b2b0df8c7 Mon Sep 17 00:00:00 2001 From: David Mason Date: Tue, 17 Aug 2021 11:32:20 -0700 Subject: [PATCH] Add tests for notification profilers (#57429) 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 --- src/coreclr/dlls/mscorrc/mscorrc.rc | 46 +++++------ src/coreclr/inc/profilepriv.h | 2 +- src/coreclr/inc/profilepriv.inl | 2 +- src/coreclr/vm/eetoprofinterfaceimpl.cpp | 11 --- src/coreclr/vm/profdetach.cpp | 8 +- src/coreclr/vm/proftoeeinterfaceimpl.cpp | 6 +- src/tests/profiler/common/ProfilerTestRunner.cs | 33 +++++++- src/tests/profiler/multiple/multiple.cs | 60 ++++++++++++++ src/tests/profiler/multiple/multiple.csproj | 15 ++++ src/tests/profiler/native/CMakeLists.txt | 1 + src/tests/profiler/native/classfactory.cpp | 81 ++++++++++++------ .../native/eltprofiler/slowpatheltprofiler.h | 2 +- .../eventpipeprofiler/eventpipereadingprofiler.h | 2 +- .../eventpipeprofiler/eventpipewritingprofiler.h | 2 +- .../native/gcallocateprofiler/gcallocateprofiler.h | 2 +- .../native/gcbasicprofiler/gcbasicprofiler.h | 2 +- src/tests/profiler/native/gcprofiler/gcprofiler.h | 2 +- .../getappdomainstaticaddress.h | 2 +- src/tests/profiler/native/guids.cpp | 1 + .../metadatagetdispenser/metadatagetdispenser.h | 2 +- src/tests/profiler/native/multiple/multiple.cpp | 95 ++++++++++++++++++++++ src/tests/profiler/native/multiple/multiple.h | 29 +++++++ .../profiler/native/nullprofiler/nullprofiler.h | 2 +- src/tests/profiler/native/profiler.cpp | 24 ++++-- src/tests/profiler/native/profiler.h | 14 ++-- .../profiler/native/rejitprofiler/rejitprofiler.h | 2 +- .../native/releaseondetach/releaseondetach.cpp | 2 +- .../native/releaseondetach/releaseondetach.h | 2 +- .../profiler/native/transitions/transitions.h | 2 +- src/tests/profiler/unittest/releaseondetach.cs | 6 +- 30 files changed, 355 insertions(+), 105 deletions(-) create mode 100644 src/tests/profiler/multiple/multiple.cs create mode 100644 src/tests/profiler/multiple/multiple.csproj create mode 100644 src/tests/profiler/native/multiple/multiple.cpp create mode 100644 src/tests/profiler/native/multiple/multiple.h diff --git a/src/coreclr/dlls/mscorrc/mscorrc.rc b/src/coreclr/dlls/mscorrc/mscorrc.rc index e8955bf..161f6e1 100644 --- a/src/coreclr/dlls/mscorrc/mscorrc.rc +++ b/src/coreclr/dlls/mscorrc/mscorrc.rc @@ -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 diff --git a/src/coreclr/inc/profilepriv.h b/src/coreclr/inc/profilepriv.h index 75d1c3d..4a77b65 100644 --- a/src/coreclr/inc/profilepriv.h +++ b/src/coreclr/inc/profilepriv.h @@ -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? diff --git a/src/coreclr/inc/profilepriv.inl b/src/coreclr/inc/profilepriv.inl index e99591c..cee2dfb 100644 --- a/src/coreclr/inc/profilepriv.inl +++ b/src/coreclr/inc/profilepriv.inl @@ -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) diff --git a/src/coreclr/vm/eetoprofinterfaceimpl.cpp b/src/coreclr/vm/eetoprofinterfaceimpl.cpp index 745f2b6..78a85b9 100644 --- a/src/coreclr/vm/eetoprofinterfaceimpl.cpp +++ b/src/coreclr/vm/eetoprofinterfaceimpl.cpp @@ -637,9 +637,6 @@ HRESULT EEToProfInterfaceImpl::CreateProfiler( } CONTRACTL_END; - // Always called before Thread created. - _ASSERTE(GetThreadNULLOk() == NULL); - // Try and CoCreate the registered profiler ReleaseHolder 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); diff --git a/src/coreclr/vm/profdetach.cpp b/src/coreclr/vm/profdetach.cpp index 0161bef..ffbe0be 100644 --- a/src/coreclr/vm/profdetach.cpp +++ b/src/coreclr/vm/profdetach.cpp @@ -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; diff --git a/src/coreclr/vm/proftoeeinterfaceimpl.cpp b/src/coreclr/vm/proftoeeinterfaceimpl.cpp index c1e9833..40b3ce5 100644 --- a/src/coreclr/vm/proftoeeinterfaceimpl.cpp +++ b/src/coreclr/vm/proftoeeinterfaceimpl.cpp @@ -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 diff --git a/src/tests/profiler/common/ProfilerTestRunner.cs b/src/tests/profiler/common/ProfilerTestRunner.cs index 38c8546..1c4520d 100644 --- a/src/tests/profiler/common/ProfilerTestRunner.cs +++ b/src/tests/profiler/common/ProfilerTestRunner.cs @@ -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 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 index 0000000..9de72d9 --- /dev/null +++ b/src/tests/profiler/multiple/multiple.cs @@ -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 index 0000000..c7b6a7b --- /dev/null +++ b/src/tests/profiler/multiple/multiple.csproj @@ -0,0 +1,15 @@ + + + .NETCoreApp + exe + BuildAndRun + true + 0 + + + + + + + + diff --git a/src/tests/profiler/native/CMakeLists.txt b/src/tests/profiler/native/CMakeLists.txt index e068fdc..b7357a6 100644 --- a/src/tests/profiler/native/CMakeLists.txt +++ b/src/tests/profiler/native/CMakeLists.txt @@ -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 diff --git a/src/tests/profiler/native/classfactory.cpp b/src/tests/profiler/native/classfactory.cpp index fe99992..d09cb4c 100644 --- a/src/tests/profiler/native/classfactory.cpp +++ b/src/tests/profiler/native/classfactory.cpp @@ -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; diff --git a/src/tests/profiler/native/eltprofiler/slowpatheltprofiler.h b/src/tests/profiler/native/eltprofiler/slowpatheltprofiler.h index 254ba12..571e677 100644 --- a/src/tests/profiler/native/eltprofiler/slowpatheltprofiler.h +++ b/src/tests/profiler/native/eltprofiler/slowpatheltprofiler.h @@ -63,7 +63,7 @@ public: _testType(TestType::Unknown) {} - virtual GUID GetClsid(); + static GUID GetClsid(); virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk); virtual HRESULT STDMETHODCALLTYPE Shutdown(); diff --git a/src/tests/profiler/native/eventpipeprofiler/eventpipereadingprofiler.h b/src/tests/profiler/native/eventpipeprofiler/eventpipereadingprofiler.h index 1d7a417..2b5bc20 100644 --- a/src/tests/profiler/native/eventpipeprofiler/eventpipereadingprofiler.h +++ b/src/tests/profiler/native/eventpipeprofiler/eventpipereadingprofiler.h @@ -22,7 +22,7 @@ public: _metadataCache() {} - virtual GUID GetClsid(); + static GUID GetClsid(); virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk); diff --git a/src/tests/profiler/native/eventpipeprofiler/eventpipewritingprofiler.h b/src/tests/profiler/native/eventpipeprofiler/eventpipewritingprofiler.h index fcb9217..9de3dab 100644 --- a/src/tests/profiler/native/eventpipeprofiler/eventpipewritingprofiler.h +++ b/src/tests/profiler/native/eventpipeprofiler/eventpipewritingprofiler.h @@ -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); diff --git a/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h b/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h index 65fb3b1..66095b8 100644 --- a/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h +++ b/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h @@ -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(); diff --git a/src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.h b/src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.h index 613f5f1..1e1c44c 100644 --- a/src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.h +++ b/src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.h @@ -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); diff --git a/src/tests/profiler/native/gcprofiler/gcprofiler.h b/src/tests/profiler/native/gcprofiler/gcprofiler.h index 8e0fbeb..ab519f6 100644 --- a/src/tests/profiler/native/gcprofiler/gcprofiler.h +++ b/src/tests/profiler/native/gcprofiler/gcprofiler.h @@ -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); diff --git a/src/tests/profiler/native/getappdomainstaticaddress/getappdomainstaticaddress.h b/src/tests/profiler/native/getappdomainstaticaddress/getappdomainstaticaddress.h index 1b902a7..740ebbd 100644 --- a/src/tests/profiler/native/getappdomainstaticaddress/getappdomainstaticaddress.h +++ b/src/tests/profiler/native/getappdomainstaticaddress/getappdomainstaticaddress.h @@ -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; diff --git a/src/tests/profiler/native/guids.cpp b/src/tests/profiler/native/guids.cpp index d8d5cf9..4928404 100644 --- a/src/tests/profiler/native/guids.cpp +++ b/src/tests/profiler/native/guids.cpp @@ -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); diff --git a/src/tests/profiler/native/metadatagetdispenser/metadatagetdispenser.h b/src/tests/profiler/native/metadatagetdispenser/metadatagetdispenser.h index 407fa73..28f08bb 100644 --- a/src/tests/profiler/native/metadatagetdispenser/metadatagetdispenser.h +++ b/src/tests/profiler/native/metadatagetdispenser/metadatagetdispenser.h @@ -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 index 0000000..420fe10 --- /dev/null +++ b/src/tests/profiler/native/multiple/multiple.cpp @@ -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 + +#define MAX_PROFILERS 3 + +using std::thread; + +std::atomic MultiplyLoaded::_exceptionThrownSeenCount(0); +std::atomic MultiplyLoaded::_detachCount(0); +std::atomic 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 index 0000000..56dc794 --- /dev/null +++ b/src/tests/profiler/native/multiple/multiple.h @@ -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 _exceptionThrownSeenCount; + static std::atomic _detachCount; + static std::atomic _failures; + + HRESULT InitializeCommon(IUnknown* pCorProfilerInfoUnk); +}; diff --git a/src/tests/profiler/native/nullprofiler/nullprofiler.h b/src/tests/profiler/native/nullprofiler/nullprofiler.h index 1d25829..93831ae 100644 --- a/src/tests/profiler/native/nullprofiler/nullprofiler.h +++ b/src/tests/profiler/native/nullprofiler/nullprofiler.h @@ -18,7 +18,7 @@ public: } - virtual GUID GetClsid(); + static GUID GetClsid(); virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk); virtual HRESULT STDMETHODCALLTYPE Shutdown(); }; diff --git a/src/tests/profiler/native/profiler.cpp b/src/tests/profiler/native/profiler.cpp index 8812cba..60eca18 100644 --- a/src/tests/profiler/native/profiler.cpp +++ b/src/tests/profiler/native/profiler.cpp @@ -12,6 +12,9 @@ Profiler *Profiler::Instance = nullptr; std::atomic ShutdownGuard::s_preventHooks(false); std::atomic 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 diff --git a/src/tests/profiler/native/profiler.h b/src/tests/profiler/native/profiler.h index 1086d9a..4bcfa1b 100644 --- a/src/tests/profiler/native/profiler.h +++ b/src/tests/profiler/native/profiler.h @@ -84,28 +84,29 @@ public: return; \ } -class Profiler : public ICorProfilerCallback10 +class Profiler : public ICorProfilerCallback11 { private: std::atomic 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); }; diff --git a/src/tests/profiler/native/rejitprofiler/rejitprofiler.h b/src/tests/profiler/native/rejitprofiler/rejitprofiler.h index f105945..c6d0751 100644 --- a/src/tests/profiler/native/rejitprofiler/rejitprofiler.h +++ b/src/tests/profiler/native/rejitprofiler/rejitprofiler.h @@ -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(); diff --git a/src/tests/profiler/native/releaseondetach/releaseondetach.cpp b/src/tests/profiler/native/releaseondetach/releaseondetach.cpp index 3af0fda..5efb373 100644 --- a/src/tests/profiler/native/releaseondetach/releaseondetach.cpp +++ b/src/tests/profiler/native/releaseondetach/releaseondetach.cpp @@ -31,7 +31,7 @@ ReleaseOnDetach::~ReleaseOnDetach() fflush(stdout); - NotifyManagedCodeViaCallback(); + NotifyManagedCodeViaCallback(pCorProfilerInfo); } GUID ReleaseOnDetach::GetClsid() diff --git a/src/tests/profiler/native/releaseondetach/releaseondetach.h b/src/tests/profiler/native/releaseondetach/releaseondetach.h index 0f75a99..458dede 100644 --- a/src/tests/profiler/native/releaseondetach/releaseondetach.h +++ b/src/tests/profiler/native/releaseondetach/releaseondetach.h @@ -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(); diff --git a/src/tests/profiler/native/transitions/transitions.h b/src/tests/profiler/native/transitions/transitions.h index 0ddf19a..6c9e9d5 100644 --- a/src/tests/profiler/native/transitions/transitions.h +++ b/src/tests/profiler/native/transitions/transitions.h @@ -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); diff --git a/src/tests/profiler/unittest/releaseondetach.cs b/src/tests/profiler/unittest/releaseondetach.cs index 8b01318..e50190b 100644 --- a/src/tests/profiler/unittest/releaseondetach.cs +++ b/src/tests/profiler/unittest/releaseondetach.cs @@ -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); -- 2.7.4