Skip to content

CORECLR_NOTIFICATION_PROFILERS silently ignores single-entry lists (.NET 9+ regression) #126197

@simonswine

Description

@simonswine

Description

In .NET 9+, CORECLR_NOTIFICATION_PROFILERS silently does nothing when the value contains a single entry with no trailing semicolon - the most common real-world usage. This is a regression from .NET 8.

I noticed this when adding integration tests for notification profiling for 9 and 10: grafana/pyroscope-dotnet#252

Root cause

AttemptLoadProfilerList was rewritten in .NET 9 from wcstok_s-based parsing to SString::Find-based iteration in #98008. The new loop uses Find(sectionEnd, W(';')) as its condition:

// .NET 9+ (broken) — profilinghelper.cpp line 797
for (SString::Iterator sectionStart = profilerList.Begin(), sectionEnd = profilerList.Begin();
    profilerList.Find(sectionEnd, W(';'));   // ← never true if no ';' present
    sectionStart = ++sectionEnd)
{
    // load profiler from [sectionStart, sectionEnd)
}

If the string contains no ;, Find returns false immediately and the loop body never
executes
. The last (or only) entry — which has no trailing semicolon — is silently dropped.

The .NET 8 implementation handled this correctly using wcstok_s, which returns all tokens
including the final one regardless of whether a trailing delimiter is present:

// .NET 8 (correct) — profilinghelper.cpp line 817
currentSection = wcstok_s(wszProfilerList, W(";"), &pOuter);
for (; currentSection != NULL; currentSection = wcstok_s(NULL, W(";"), &pOuter))
{
    // processes all tokens including the last one without trailing ';'
}

Steps to reproduce

Set the following environment variables and launch a .NET 9+ process with a profiler that supports
the notification profiler role (i.e. LoadAsNotificationOnly returns TRUE):

CORECLR_ENABLE_PROFILING=1
CORECLR_PROFILER={main-profiler-guid}
CORECLR_PROFILER_PATH=/path/to/main-profiler.so
CORECLR_ENABLE_NOTIFICATION_PROFILERS=1
CORECLR_NOTIFICATION_PROFILERS=/path/to/notification-profiler.so={notification-guid}

Expected: notification profiler loads and its Initialize is called.
Actual: notification profiler is never loaded; no error or warning is emitted.

Workaround

Append a trailing semicolon to the value:

CORECLR_NOTIFICATION_PROFILERS=/path/to/notification-profiler.so={notification-guid};

Impact

Any tool using CORECLR_NOTIFICATION_PROFILERS with a single entry silently stops working when upgrading from .NET 8 to .NET 9+. The failure is invisible: no error is logged, the app starts normally, and profiling data is simply absent.

Confirmed affected: .NET 9.0.14, .NET 10.0.105 (SDK 10.0.201), main as of 2026-03-27.

Suggested fix

Either handle the trailing entry after the loop, or normalize input before parsing:

// Option A: process the final entry after the loop
if (sectionStart != profilerList.End())
{
    // process [sectionStart, End())
}

// Option B: normalize input by appending ';' before parsing
profilerList.Append(W(';'));

Source references

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions