Skip to content
Merged
23 changes: 0 additions & 23 deletions docs/design/coreclr/profiling/Profiler Loading.md

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
*This blog post originally appeared on David Broman's blog on 12/11/2007*

***[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.
Please see [Profiler Loading](../Profiler%20Loading.md) for details on the change.***


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?".

Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/inc/clrconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class CLRConfig
// The configuration should be parsed using a 10 radix as opposed to the
// default of 16.
ParseIntegerAsBase10 = 0x4,

// If set, prepend DOTNET_ or CORECLR_ prefix when doing environment variable lookup.
CoreclrFallbackPrefix = 0x8,
};

// Struct used to store information about where/how to find a Config DWORD.
Expand Down
17 changes: 13 additions & 4 deletions src/coreclr/inc/clrconfignocache.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
#define COMPLUS_PREFIX W("COMPlus_")
#define LEN_OF_COMPLUS_PREFIX STRING_LENGTH(COMPLUS_PREFIX_A)

#define CORECLR_PREFIX_A "CORECLR_"
#define CORECLR_PREFIX W("CORECLR_")
#define LEN_OF_CORECLR_PREFIX STRING_LENGTH(CORECLR_PREFIX_A)

#define DOTNET_PREFIX_A "DOTNET_"
#define DOTNET_PREFIX W("DOTNET_")
#define LEN_OF_DOTNET_PREFIX STRING_LENGTH(DOTNET_PREFIX_A)
Expand Down Expand Up @@ -52,7 +56,11 @@ class CLRConfigNoCache
return fSuccess;
}

static CLRConfigNoCache Get(LPCSTR cfg, bool noPrefix = false, char*(*getEnvFptr)(const char*) = nullptr)
static CLRConfigNoCache Get(
LPCSTR cfg,
bool noPrefix = false,
char*(*getEnvFptr)(const char*) = nullptr,
bool coreclrFallbackPrefix = false)
{
char nameBuffer[64];
const char* fallbackPrefix = NULL;
Expand All @@ -71,16 +79,17 @@ class CLRConfigNoCache
else
{
bool dotnetValid = namelen < (size_t)(STRING_LENGTH(nameBuffer) - LEN_OF_DOTNET_PREFIX);
bool coreclrValid = namelen < (size_t)(STRING_LENGTH(nameBuffer) - LEN_OF_CORECLR_PREFIX);
bool complusValid = namelen < (size_t)(STRING_LENGTH(nameBuffer) - LEN_OF_COMPLUS_PREFIX);
if (!dotnetValid || !complusValid)
if (!dotnetValid || !coreclrValid || !complusValid)
{
_ASSERTE(!"Environment variable name too long.");
return {};
}

// Priority order is DOTNET_ and then COMPlus_.
// Priority order is DOTNET_, then (CORECLR_ or COMPlus_).
strcpy_s(nameBuffer, ARRAY_SIZE(nameBuffer), DOTNET_PREFIX_A);
fallbackPrefix = COMPLUS_PREFIX_A;
fallbackPrefix = coreclrFallbackPrefix ? CORECLR_PREFIX_A : COMPLUS_PREFIX_A;
}

strcat_s(nameBuffer, ARRAY_SIZE(nameBuffer), cfg);
Expand Down
26 changes: 13 additions & 13 deletions src/coreclr/inc/clrconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,19 +402,19 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinRetryCount, W("SpinRetryCount"), 0xA, "Hex
///
/// Profiling API / ETW
///
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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.")
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.")
Expand Down
28 changes: 19 additions & 9 deletions src/coreclr/utilcode/clrconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ namespace
const size_t namelen = u16_strlen(name);

bool noPrefix = CheckLookupOption(options, LookupOptions::DontPrependPrefix);
bool coreclrFallbackPrefix = CheckLookupOption(options, LookupOptions::CoreclrFallbackPrefix);

if (noPrefix)
{
if (namelen >= ARRAY_SIZE(buff))
Expand All @@ -159,8 +161,9 @@ namespace
else
{
bool dotnetValid = namelen < (size_t)(STRING_LENGTH(buff) - LEN_OF_DOTNET_PREFIX);
bool coreclrValid = namelen < (size_t)(STRING_LENGTH(buff) - LEN_OF_CORECLR_PREFIX);
bool complusValid = namelen < (size_t)(STRING_LENGTH(buff) - LEN_OF_COMPLUS_PREFIX);
if(!dotnetValid || !complusValid)
if(!dotnetValid || !coreclrValid || !complusValid)
{
_ASSERTE(!"Environment variable name too long.");
return NULL;
Expand All @@ -170,9 +173,9 @@ namespace
if (!EnvCacheValueNameSeenPerhaps(name))
return NULL;

// Priority order is DOTNET_ and then COMPlus_.
// Priority order is DOTNET_, then (CORECLR_ or COMPlus_).
wcscpy_s(buff, ARRAY_SIZE(buff), DOTNET_PREFIX);
fallbackPrefix = COMPLUS_PREFIX;
fallbackPrefix = coreclrFallbackPrefix ? CORECLR_PREFIX : COMPLUS_PREFIX;
}

wcscat_s(buff, ARRAY_SIZE(buff), name);
Expand Down Expand Up @@ -202,9 +205,9 @@ namespace
SString nameToConvert(name);

#ifdef HOST_WINDOWS
CLRConfigNoCache nonCache = CLRConfigNoCache::Get(nameToConvert.GetUTF8(), noPrefix);
CLRConfigNoCache nonCache = CLRConfigNoCache::Get(nameToConvert.GetUTF8(), noPrefix, nullptr, coreclrFallbackPrefix);
#else
CLRConfigNoCache nonCache = CLRConfigNoCache::Get(nameToConvert.GetUTF8(), noPrefix, &PAL_getenv);
CLRConfigNoCache nonCache = CLRConfigNoCache::Get(nameToConvert.GetUTF8(), noPrefix, &PAL_getenv, coreclrFallbackPrefix);
#endif
LPCSTR valueNoCache = nonCache.AsString();

Expand Down Expand Up @@ -661,11 +664,18 @@ void CLRConfig::Initialize()
if (*wszCurr == W('='))
{
// Check the prefix
if(matchC
&& SString::_wcsnicmp(wszName, COMPLUS_PREFIX, LEN_OF_COMPLUS_PREFIX) == 0)
if(matchC)
{
wszName += LEN_OF_COMPLUS_PREFIX;
s_EnvNames.Add(wszName, (DWORD) (wszCurr - wszName));
if(SString::_wcsnicmp(wszName, COMPLUS_PREFIX, LEN_OF_COMPLUS_PREFIX) == 0)
{
wszName += LEN_OF_COMPLUS_PREFIX;
s_EnvNames.Add(wszName, (DWORD) (wszCurr - wszName));
}
else if(SString::_wcsnicmp(wszName, CORECLR_PREFIX, LEN_OF_CORECLR_PREFIX) == 0)
{
wszName += LEN_OF_CORECLR_PREFIX;
s_EnvNames.Add(wszName, (DWORD) (wszCurr - wszName));
}
}
else if (matchD
&& SString::_wcsnicmp(wszName, DOTNET_PREFIX, LEN_OF_DOTNET_PREFIX) == 0)
Expand Down
8 changes: 5 additions & 3 deletions src/tests/profiler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ Because of this two layer architecture just running the managed test executable
```
#~/bin/bash

export CORECLR_ENABLE_PROFILING=1
export CORECLR_PROFILER={2726B5B4-3F88-462D-AEC0-4EFDC8D7B921}
export CORECLR_PROFILER_PATH=<Path to test binaries>/profiler/eventpipe/eventpipe/libProfiler.so
export DOTNET_ENABLE_PROFILING=1
export DOTNET_PROFILER={2726B5B4-3F88-462D-AEC0-4EFDC8D7B921}
export DOTNET_PROFILER_PATH=<Path to test binaries>/profiler/eventpipe/eventpipe/libProfiler.so

<Path to test binaries>/Core_Root/corerun <Path to test binaries>/profiler/eventpipe/eventpipe/eventpipe.dll RunTest
```

**Note:** The `CORECLR_` prefix is still supported for backwards compatibility but may be removed in the future. Use the `DOTNET_` prefix for new projects.
10 changes: 9 additions & 1 deletion src/tests/profiler/assembly/ALCTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,17 @@ public static int Main(string[] args)
return RunTest(args);
}

Console.WriteLine($"Running the test using environment variables with CORECLR prefix.");
ProfilerTestRunner.Run(profileePath: System.Reflection.Assembly.GetExecutingAssembly().Location,
testName: "ALCTest",
profilerClsid: AssemblyProfilerGuid,
envVarProfilerPrefix: "CORECLR");

Console.WriteLine($"Running the test using environment variables with DOTNET prefix.");
return ProfilerTestRunner.Run(profileePath: System.Reflection.Assembly.GetExecutingAssembly().Location,
testName: "ALCTest",
profilerClsid: AssemblyProfilerGuid);
profilerClsid: AssemblyProfilerGuid,
envVarProfilerPrefix: "DOTNET");
}
}
}
13 changes: 7 additions & 6 deletions src/tests/profiler/common/ProfilerTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public static int Run(string profileePath,
Dictionary<string, string> envVars = null,
string reverseServerName = null,
bool loadAsNotification = false,
int notificationCopies = 1)
int notificationCopies = 1,
string envVarProfilerPrefix = "DOTNET")
{
string arguments;
string program;
Expand All @@ -47,7 +48,7 @@ public static int Run(string profileePath,
string profilerPath = GetProfilerPath();
if (!profileeOptions.HasFlag(ProfileeOptions.NoStartupAttach))
{
envVars.Add("CORECLR_ENABLE_PROFILING", "1");
envVars.Add(envVarProfilerPrefix + "_ENABLE_PROFILING", "1");

if (loadAsNotification)
{
Expand All @@ -62,14 +63,14 @@ public static int Run(string profileePath,
builder.Append(";");
}

envVars.Add("CORECLR_ENABLE_NOTIFICATION_PROFILERS", "1");
envVars.Add("CORECLR_NOTIFICATION_PROFILERS", builder.ToString());
envVars.Add(envVarProfilerPrefix + "_ENABLE_NOTIFICATION_PROFILERS", "1");
envVars.Add(envVarProfilerPrefix + "_NOTIFICATION_PROFILERS", builder.ToString());

}
else
{
envVars.Add("CORECLR_PROFILER", "{" + profilerClsid + "}");
envVars.Add("CORECLR_PROFILER_PATH", profilerPath);
envVars.Add(envVarProfilerPrefix + "_PROFILER", "{" + profilerClsid + "}");
envVars.Add(envVarProfilerPrefix + "_PROFILER_PATH", profilerPath);
}
}

Expand Down
Loading
Loading