diff --git a/src/TraceEvent/TraceEvent.Tests/Regression/ProviderNameToGuidTests.cs b/src/TraceEvent/TraceEvent.Tests/Regression/ProviderNameToGuidTests.cs new file mode 100644 index 000000000..e0ce4ea82 --- /dev/null +++ b/src/TraceEvent/TraceEvent.Tests/Regression/ProviderNameToGuidTests.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.Diagnostics.Tracing.Parsers; +using Microsoft.Diagnostics.Tracing.Session; +using Xunit; + +namespace TraceEventTests +{ + public class ProviderNameToGuidTests + { + /// + /// Test that GetProviderGuidByName (which uses ProviderNameToGuid internally) doesn't throw + /// when called and correctly returns the GUID for Microsoft-Windows-DotNETRuntime. + /// This test validates the fix for the race condition that could cause + /// ERROR_INSUFFICIENT_BUFFER (HR 122) crashes. + /// + [WindowsFact] + public void ProviderNameToGuid_DoesNotThrow_AndReturnsCorrectGuid() + { + // Act - Call ProviderNameToGuid and ensure it doesn't throw + Guid actualGuid = TraceEventProviders.GetProviderGuidByName(ClrTraceEventParser.ProviderName); + + // Assert - Verify it matches the expected GUID + Assert.Equal(ClrTraceEventParser.ProviderGuid, actualGuid); + } + } +} diff --git a/src/TraceEvent/TraceEventSession.cs b/src/TraceEvent/TraceEventSession.cs index 9df626f6f..3e1cb624b 100644 --- a/src/TraceEvent/TraceEventSession.cs +++ b/src/TraceEvent/TraceEventSession.cs @@ -1760,27 +1760,47 @@ internal static SortedDictionary ProviderNameToGuid { SortedDictionary providersByName = new SortedDictionary(StringComparer.OrdinalIgnoreCase); int buffSize = 0; - var hr = TraceEventNativeMethods.TdhEnumerateProviders(null, ref buffSize); - Debug.Assert(hr == 122); // ERROR_INSUFFICIENT_BUFFER - var buffer = stackalloc byte[buffSize]; - var providersDesc = (TraceEventNativeMethods.PROVIDER_ENUMERATION_INFO*)buffer; + byte[] buffer = null; + int hr; - hr = TraceEventNativeMethods.TdhEnumerateProviders(providersDesc, ref buffSize); - if ((hr == 0) && (providersDesc != null)) + // Retry loop to handle the case where the buffer size changes between calls + // This can happen if providers are registered/unregistered between the two calls + for (; ; ) { - var providers = (TraceEventNativeMethods.TRACE_PROVIDER_INFO*)&providersDesc[1]; - for (int i = 0; i < providersDesc->NumberOfProviders; i++) + if (buffSize > 0) { - var name = new string((char*)&buffer[providers[i].ProviderNameOffset]); - providersByName[name] = providers[i].ProviderGuid; + buffer = new byte[buffSize]; } - s_providersByName = providersByName; - } - else - { - throw new Exception("TdhEnumerateProviders failed HR = " + hr); + fixed (byte* bufferPtr = buffer) + { + var providersDesc = (TraceEventNativeMethods.PROVIDER_ENUMERATION_INFO*)bufferPtr; + + hr = TraceEventNativeMethods.TdhEnumerateProviders(providersDesc, ref buffSize); + if (hr == 0) + { + if (providersDesc != null) + { + var providers = (TraceEventNativeMethods.TRACE_PROVIDER_INFO*)&providersDesc[1]; + for (int i = 0; i < providersDesc->NumberOfProviders; i++) + { + var name = new string((char*)&bufferPtr[providers[i].ProviderNameOffset]); + providersByName[name] = providers[i].ProviderGuid; + } + } + break; + } + } + + // Error 122 means buffer not big enough. For that error we retry, everything else simply fail. + if (hr != 122) + { + throw new Exception("Failed to enumerate trace providers. TdhEnumerateProviders failed HR = " + hr); + } } + + // Always assign providersByName to avoid NullReferenceException on subsequent lookups + s_providersByName = providersByName; } } }