Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Session;
using Xunit;

namespace TraceEventTests
{
public class ProviderNameToGuidTests
{
/// <summary>
/// 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.
/// </summary>
[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);
}
}
}
50 changes: 35 additions & 15 deletions src/TraceEvent/TraceEventSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1760,27 +1760,47 @@ internal static SortedDictionary<string, Guid> ProviderNameToGuid
{
SortedDictionary<string, Guid> providersByName = new SortedDictionary<string, Guid>(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;
}
}
}
Expand Down