Skip to content
43 changes: 31 additions & 12 deletions src/TraceEvent/TraceEventSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1760,27 +1760,46 @@ 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;
TraceEventNativeMethods.PROVIDER_ENUMERATION_INFO* providersDesc = 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 (; ; )
{
if (buffSize > 0)
{
var space = stackalloc byte[buffSize];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should not need another local variable. This should just be directly assigned to buffer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept the intermediate space variable because C# requires it for assigning stackalloc to a pointer (direct assignment causes error CS8346). This matches the pattern in GetEnabledProvidersForSession (lines 2571-2572). Is there a way to avoid the intermediate variable that I'm missing?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be using stackalloc at all inside of a loop. How big can buffSize get? Using stackalloc at all is likely over-optimization here. We only ever run this code once (and ensure that with a lock) surely just allocating on the heap shouldn't cause too much trouble here, and will ensure easier to follow code, just use Array.Resize or similar.

buffer = space;
providersDesc = (TraceEventNativeMethods.PROVIDER_ENUMERATION_INFO*)buffer;
}

hr = TraceEventNativeMethods.TdhEnumerateProviders(providersDesc, ref buffSize);
if (hr == 0)
{
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);
}
}

if (providersDesc != null)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If after all this, hr == 0 but providersDesc is null, then we should just assign providersByName (now an empty dictionary) to s_providersByName so that any subsequent lookups fail, but they don't throw a NullReferenceException.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Now always assigns providersByName to s_providersByName (even if empty when providersDesc is null) to avoid NullReferenceException on subsequent lookups. (commit 226f120)

{
var providers = (TraceEventNativeMethods.TRACE_PROVIDER_INFO*)&providersDesc[1];
for (int i = 0; i < providersDesc->NumberOfProviders; i++)
{
var name = new string((char*)&buffer[providers[i].ProviderNameOffset]);
providersByName[name] = providers[i].ProviderGuid;
}

s_providersByName = providersByName;
}
else
{
throw new Exception("TdhEnumerateProviders failed HR = " + hr);
}

// Always assign providersByName to avoid NullReferenceException on subsequent lookups
s_providersByName = providersByName;
}
}
}
Expand Down