Skip to content

Commit d05d76e

Browse files
barosiaknoahfalk
andauthored
Add support for DOTNET prefix on ICorProfiler env vars (#121646)
Add DOTNET_* variations to existing CORECLR_* profiler environment variables. Fix #117902 --------- Co-authored-by: Noah Falk <noahfalk@users.noreply.github.com>
1 parent 4fed3f3 commit d05d76e

File tree

11 files changed

+88
-63
lines changed

11 files changed

+88
-63
lines changed

docs/design/coreclr/profiling/Profiler Loading.md

Lines changed: 0 additions & 23 deletions
This file was deleted.

docs/design/coreclr/profiling/davbr-blog-archive/Debugging - Activation.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
*This blog post originally appeared on David Broman's blog on 12/11/2007*
22

3+
***[Update 11/14/25]: The archived content below refers to an old naming convention of the environmental variables. `CORECLR_` prefix has been changed to `DOTNET_`. Both are supported for now, but the support for `CORECLR_` prefix may be removed in the future.
4+
Please see [Profiler Loading](../Profiler%20Loading.md) for details on the change.***
5+
36

47
This is the first of some tips to help you debug your profiler. Note that these tips assume you're using CLR 2.x (see [this entry](https://learn.microsoft.com/archive/blogs/davbr/versions-of-microsoft-net-framework-clr-and-your-profiler) for info on how CLR version numbers map to .NET Framework version numbers). In today's post, I address a frequent question from profiler developers and users: "Why didn't my profiler load?".
58

src/coreclr/inc/clrconfig.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ class CLRConfig
3636
// The configuration should be parsed using a 10 radix as opposed to the
3737
// default of 16.
3838
ParseIntegerAsBase10 = 0x4,
39+
40+
// If set, prepend DOTNET_ or CORECLR_ prefix when doing environment variable lookup.
41+
CoreclrFallbackPrefix = 0x8,
3942
};
4043

4144
// Struct used to store information about where/how to find a Config DWORD.

src/coreclr/inc/clrconfignocache.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
#define COMPLUS_PREFIX W("COMPlus_")
1515
#define LEN_OF_COMPLUS_PREFIX STRING_LENGTH(COMPLUS_PREFIX_A)
1616

17+
#define CORECLR_PREFIX_A "CORECLR_"
18+
#define CORECLR_PREFIX W("CORECLR_")
19+
#define LEN_OF_CORECLR_PREFIX STRING_LENGTH(CORECLR_PREFIX_A)
20+
1721
#define DOTNET_PREFIX_A "DOTNET_"
1822
#define DOTNET_PREFIX W("DOTNET_")
1923
#define LEN_OF_DOTNET_PREFIX STRING_LENGTH(DOTNET_PREFIX_A)
@@ -52,7 +56,11 @@ class CLRConfigNoCache
5256
return fSuccess;
5357
}
5458

55-
static CLRConfigNoCache Get(LPCSTR cfg, bool noPrefix = false, char*(*getEnvFptr)(const char*) = nullptr)
59+
static CLRConfigNoCache Get(
60+
LPCSTR cfg,
61+
bool noPrefix = false,
62+
char*(*getEnvFptr)(const char*) = nullptr,
63+
bool coreclrFallbackPrefix = false)
5664
{
5765
char nameBuffer[64];
5866
const char* fallbackPrefix = NULL;
@@ -71,16 +79,17 @@ class CLRConfigNoCache
7179
else
7280
{
7381
bool dotnetValid = namelen < (size_t)(STRING_LENGTH(nameBuffer) - LEN_OF_DOTNET_PREFIX);
82+
bool coreclrValid = namelen < (size_t)(STRING_LENGTH(nameBuffer) - LEN_OF_CORECLR_PREFIX);
7483
bool complusValid = namelen < (size_t)(STRING_LENGTH(nameBuffer) - LEN_OF_COMPLUS_PREFIX);
75-
if (!dotnetValid || !complusValid)
84+
if (!dotnetValid || !coreclrValid || !complusValid)
7685
{
7786
_ASSERTE(!"Environment variable name too long.");
7887
return {};
7988
}
8089

81-
// Priority order is DOTNET_ and then COMPlus_.
90+
// Priority order is DOTNET_, then (CORECLR_ or COMPlus_).
8291
strcpy_s(nameBuffer, ARRAY_SIZE(nameBuffer), DOTNET_PREFIX_A);
83-
fallbackPrefix = COMPLUS_PREFIX_A;
92+
fallbackPrefix = coreclrFallbackPrefix ? CORECLR_PREFIX_A : COMPLUS_PREFIX_A;
8493
}
8594

8695
strcat_s(nameBuffer, ARRAY_SIZE(nameBuffer), cfg);

src/coreclr/inc/clrconfigvalues.h

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -402,19 +402,19 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinRetryCount, W("SpinRetryCount"), 0xA, "Hex
402402
///
403403
/// Profiling API / ETW
404404
///
405-
RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_CORECLR_ENABLE_PROFILING, W("CORECLR_ENABLE_PROFILING"), 0, "CoreCLR only: Flag to indicate whether profiling should be enabled for the currently running process.", CLRConfig::LookupOptions::DontPrependPrefix)
406-
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER, W("CORECLR_PROFILER"), "CoreCLR only: Specifies GUID of profiler to load into currently running process", CLRConfig::LookupOptions::DontPrependPrefix)
407-
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH, W("CORECLR_PROFILER_PATH"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running process", CLRConfig::LookupOptions::DontPrependPrefix)
408-
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_32, W("CORECLR_PROFILER_PATH_32"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running 32 process", CLRConfig::LookupOptions::DontPrependPrefix)
409-
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_64, W("CORECLR_PROFILER_PATH_64"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running 64 process", CLRConfig::LookupOptions::DontPrependPrefix)
410-
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_ARM32, W("CORECLR_PROFILER_PATH_ARM32"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running ARM32 process", CLRConfig::LookupOptions::DontPrependPrefix)
411-
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_ARM64, W("CORECLR_PROFILER_PATH_ARM64"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running ARM64 process", CLRConfig::LookupOptions::DontPrependPrefix)
412-
RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_CORECLR_ENABLE_NOTIFICATION_PROFILERS, W("CORECLR_ENABLE_NOTIFICATION_PROFILERS"), 0, "Set to 0 to disable loading notification profilers.", CLRConfig::LookupOptions::DontPrependPrefix)
413-
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_NOTIFICATION_PROFILERS, W("CORECLR_NOTIFICATION_PROFILERS"), "A semi-colon separated list of notification profilers to load into currently running process in the form \"path={guid}\"", CLRConfig::LookupOptions::DontPrependPrefix)
414-
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_NOTIFICATION_PROFILERS_32, W("CORECLR_NOTIFICATION_PROFILERS_32"), "A semi-colon separated list of notification profilers to load into currently running 32 process in the form \"path={guid}\"", CLRConfig::LookupOptions::DontPrependPrefix)
415-
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_NOTIFICATION_PROFILERS_64, W("CORECLR_NOTIFICATION_PROFILERS_64"), "A semi-colon separated list of notification profilers to load into currently running 64 process in the form \"path={guid}\"", CLRConfig::LookupOptions::DontPrependPrefix)
416-
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_NOTIFICATION_PROFILERS_ARM32, W("CORECLR_NOTIFICATION_PROFILERS_ARM32"), "A semi-colon separated list of notification profilers to load into currently running ARM32 process in the form \"path={guid}\"", CLRConfig::LookupOptions::DontPrependPrefix)
417-
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_NOTIFICATION_PROFILERS_ARM64, W("CORECLR_NOTIFICATION_PROFILERS_ARM64"), "A semi-colon separated list of notification profilers to load into currently running ARM64 process in the form \"path={guid}\"", CLRConfig::LookupOptions::DontPrependPrefix)
405+
RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_CORECLR_ENABLE_PROFILING, W("ENABLE_PROFILING"), 0, "CoreCLR only: Flag to indicate whether profiling should be enabled for the currently running process.", CLRConfig::LookupOptions::CoreclrFallbackPrefix)
406+
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER, W("PROFILER"), "CoreCLR only: Specifies GUID of profiler to load into currently running process", CLRConfig::LookupOptions::CoreclrFallbackPrefix)
407+
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH, W("PROFILER_PATH"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running process", CLRConfig::LookupOptions::CoreclrFallbackPrefix)
408+
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_32, W("PROFILER_PATH_32"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running 32 process", CLRConfig::LookupOptions::CoreclrFallbackPrefix)
409+
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_64, W("PROFILER_PATH_64"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running 64 process", CLRConfig::LookupOptions::CoreclrFallbackPrefix)
410+
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_ARM32, W("PROFILER_PATH_ARM32"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running ARM32 process", CLRConfig::LookupOptions::CoreclrFallbackPrefix)
411+
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_ARM64, W("PROFILER_PATH_ARM64"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running ARM64 process", CLRConfig::LookupOptions::CoreclrFallbackPrefix)
412+
RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_CORECLR_ENABLE_NOTIFICATION_PROFILERS, W("ENABLE_NOTIFICATION_PROFILERS"), 0, "Set to 0 to disable loading notification profilers.", CLRConfig::LookupOptions::CoreclrFallbackPrefix)
413+
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_NOTIFICATION_PROFILERS, W("NOTIFICATION_PROFILERS"), "A semi-colon separated list of notification profilers to load into currently running process in the form \"path={guid}\"", CLRConfig::LookupOptions::CoreclrFallbackPrefix)
414+
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_NOTIFICATION_PROFILERS_32, W("NOTIFICATION_PROFILERS_32"), "A semi-colon separated list of notification profilers to load into currently running 32 process in the form \"path={guid}\"", CLRConfig::LookupOptions::CoreclrFallbackPrefix)
415+
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_NOTIFICATION_PROFILERS_64, W("NOTIFICATION_PROFILERS_64"), "A semi-colon separated list of notification profilers to load into currently running 64 process in the form \"path={guid}\"", CLRConfig::LookupOptions::CoreclrFallbackPrefix)
416+
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_NOTIFICATION_PROFILERS_ARM32, W("NOTIFICATION_PROFILERS_ARM32"), "A semi-colon separated list of notification profilers to load into currently running ARM32 process in the form \"path={guid}\"", CLRConfig::LookupOptions::CoreclrFallbackPrefix)
417+
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_NOTIFICATION_PROFILERS_ARM64, W("NOTIFICATION_PROFILERS_ARM64"), "A semi-colon separated list of notification profilers to load into currently running ARM64 process in the form \"path={guid}\"", CLRConfig::LookupOptions::CoreclrFallbackPrefix)
418418
RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_ProfAPI_ProfilerCompatibilitySetting, W("ProfAPI_ProfilerCompatibilitySetting"), "Specifies the profiler loading policy (the default is not to load a V2 profiler in V4)", CLRConfig::LookupOptions::TrimWhiteSpaceFromStringValue)
419419
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ProfAPI_DetachMinSleepMs, W("ProfAPI_DetachMinSleepMs"), 0, "The minimum time, in milliseconds, the CLR will wait before checking whether a profiler that is in the process of detaching is ready to be unloaded.")
420420
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ProfAPI_DetachMaxSleepMs, W("ProfAPI_DetachMaxSleepMs"), 0, "The maximum time, in milliseconds, the CLR will wait before checking whether a profiler that is in the process of detaching is ready to be unloaded.")

src/coreclr/utilcode/clrconfig.cpp

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ namespace
146146
const size_t namelen = u16_strlen(name);
147147

148148
bool noPrefix = CheckLookupOption(options, LookupOptions::DontPrependPrefix);
149+
bool coreclrFallbackPrefix = CheckLookupOption(options, LookupOptions::CoreclrFallbackPrefix);
150+
149151
if (noPrefix)
150152
{
151153
if (namelen >= ARRAY_SIZE(buff))
@@ -159,8 +161,9 @@ namespace
159161
else
160162
{
161163
bool dotnetValid = namelen < (size_t)(STRING_LENGTH(buff) - LEN_OF_DOTNET_PREFIX);
164+
bool coreclrValid = namelen < (size_t)(STRING_LENGTH(buff) - LEN_OF_CORECLR_PREFIX);
162165
bool complusValid = namelen < (size_t)(STRING_LENGTH(buff) - LEN_OF_COMPLUS_PREFIX);
163-
if(!dotnetValid || !complusValid)
166+
if(!dotnetValid || !coreclrValid || !complusValid)
164167
{
165168
_ASSERTE(!"Environment variable name too long.");
166169
return NULL;
@@ -170,9 +173,9 @@ namespace
170173
if (!EnvCacheValueNameSeenPerhaps(name))
171174
return NULL;
172175

173-
// Priority order is DOTNET_ and then COMPlus_.
176+
// Priority order is DOTNET_, then (CORECLR_ or COMPlus_).
174177
wcscpy_s(buff, ARRAY_SIZE(buff), DOTNET_PREFIX);
175-
fallbackPrefix = COMPLUS_PREFIX;
178+
fallbackPrefix = coreclrFallbackPrefix ? CORECLR_PREFIX : COMPLUS_PREFIX;
176179
}
177180

178181
wcscat_s(buff, ARRAY_SIZE(buff), name);
@@ -202,9 +205,9 @@ namespace
202205
SString nameToConvert(name);
203206

204207
#ifdef HOST_WINDOWS
205-
CLRConfigNoCache nonCache = CLRConfigNoCache::Get(nameToConvert.GetUTF8(), noPrefix);
208+
CLRConfigNoCache nonCache = CLRConfigNoCache::Get(nameToConvert.GetUTF8(), noPrefix, nullptr, coreclrFallbackPrefix);
206209
#else
207-
CLRConfigNoCache nonCache = CLRConfigNoCache::Get(nameToConvert.GetUTF8(), noPrefix, &PAL_getenv);
210+
CLRConfigNoCache nonCache = CLRConfigNoCache::Get(nameToConvert.GetUTF8(), noPrefix, &PAL_getenv, coreclrFallbackPrefix);
208211
#endif
209212
LPCSTR valueNoCache = nonCache.AsString();
210213

@@ -661,11 +664,18 @@ void CLRConfig::Initialize()
661664
if (*wszCurr == W('='))
662665
{
663666
// Check the prefix
664-
if(matchC
665-
&& SString::_wcsnicmp(wszName, COMPLUS_PREFIX, LEN_OF_COMPLUS_PREFIX) == 0)
667+
if(matchC)
666668
{
667-
wszName += LEN_OF_COMPLUS_PREFIX;
668-
s_EnvNames.Add(wszName, (DWORD) (wszCurr - wszName));
669+
if(SString::_wcsnicmp(wszName, COMPLUS_PREFIX, LEN_OF_COMPLUS_PREFIX) == 0)
670+
{
671+
wszName += LEN_OF_COMPLUS_PREFIX;
672+
s_EnvNames.Add(wszName, (DWORD) (wszCurr - wszName));
673+
}
674+
else if(SString::_wcsnicmp(wszName, CORECLR_PREFIX, LEN_OF_CORECLR_PREFIX) == 0)
675+
{
676+
wszName += LEN_OF_CORECLR_PREFIX;
677+
s_EnvNames.Add(wszName, (DWORD) (wszCurr - wszName));
678+
}
669679
}
670680
else if (matchD
671681
&& SString::_wcsnicmp(wszName, DOTNET_PREFIX, LEN_OF_DOTNET_PREFIX) == 0)

src/tests/profiler/README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ Because of this two layer architecture just running the managed test executable
99
```
1010
#~/bin/bash
1111
12-
export CORECLR_ENABLE_PROFILING=1
13-
export CORECLR_PROFILER={2726B5B4-3F88-462D-AEC0-4EFDC8D7B921}
14-
export CORECLR_PROFILER_PATH=<Path to test binaries>/profiler/eventpipe/eventpipe/libProfiler.so
12+
export DOTNET_ENABLE_PROFILING=1
13+
export DOTNET_PROFILER={2726B5B4-3F88-462D-AEC0-4EFDC8D7B921}
14+
export DOTNET_PROFILER_PATH=<Path to test binaries>/profiler/eventpipe/eventpipe/libProfiler.so
1515
1616
<Path to test binaries>/Core_Root/corerun <Path to test binaries>/profiler/eventpipe/eventpipe/eventpipe.dll RunTest
1717
```
18+
19+
**Note:** The `CORECLR_` prefix is still supported for backwards compatibility but may be removed in the future. Use the `DOTNET_` prefix for new projects.

src/tests/profiler/assembly/ALCTest.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,17 @@ public static int Main(string[] args)
2727
return RunTest(args);
2828
}
2929

30+
Console.WriteLine($"Running the test using environment variables with CORECLR prefix.");
31+
ProfilerTestRunner.Run(profileePath: System.Reflection.Assembly.GetExecutingAssembly().Location,
32+
testName: "ALCTest",
33+
profilerClsid: AssemblyProfilerGuid,
34+
envVarProfilerPrefix: "CORECLR");
35+
36+
Console.WriteLine($"Running the test using environment variables with DOTNET prefix.");
3037
return ProfilerTestRunner.Run(profileePath: System.Reflection.Assembly.GetExecutingAssembly().Location,
3138
testName: "ALCTest",
32-
profilerClsid: AssemblyProfilerGuid);
39+
profilerClsid: AssemblyProfilerGuid,
40+
envVarProfilerPrefix: "DOTNET");
3341
}
3442
}
3543
}

src/tests/profiler/common/ProfilerTestRunner.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ public static int Run(string profileePath,
3131
Dictionary<string, string> envVars = null,
3232
string reverseServerName = null,
3333
bool loadAsNotification = false,
34-
int notificationCopies = 1)
34+
int notificationCopies = 1,
35+
string envVarProfilerPrefix = "DOTNET")
3536
{
3637
string arguments;
3738
string program;
@@ -47,7 +48,7 @@ public static int Run(string profileePath,
4748
string profilerPath = GetProfilerPath();
4849
if (!profileeOptions.HasFlag(ProfileeOptions.NoStartupAttach))
4950
{
50-
envVars.Add("CORECLR_ENABLE_PROFILING", "1");
51+
envVars.Add(envVarProfilerPrefix + "_ENABLE_PROFILING", "1");
5152

5253
if (loadAsNotification)
5354
{
@@ -62,14 +63,14 @@ public static int Run(string profileePath,
6263
builder.Append(";");
6364
}
6465

65-
envVars.Add("CORECLR_ENABLE_NOTIFICATION_PROFILERS", "1");
66-
envVars.Add("CORECLR_NOTIFICATION_PROFILERS", builder.ToString());
66+
envVars.Add(envVarProfilerPrefix + "_ENABLE_NOTIFICATION_PROFILERS", "1");
67+
envVars.Add(envVarProfilerPrefix + "_NOTIFICATION_PROFILERS", builder.ToString());
6768

6869
}
6970
else
7071
{
71-
envVars.Add("CORECLR_PROFILER", "{" + profilerClsid + "}");
72-
envVars.Add("CORECLR_PROFILER_PATH", profilerPath);
72+
envVars.Add(envVarProfilerPrefix + "_PROFILER", "{" + profilerClsid + "}");
73+
envVars.Add(envVarProfilerPrefix + "_PROFILER_PATH", profilerPath);
7374
}
7475
}
7576

0 commit comments

Comments
 (0)