Skip to content

Commit

Permalink
Add profiler startup command to the diagnostics command set (#52175)
Browse files Browse the repository at this point in the history
Add a diagnostics IPC command to load a startup profiler
  • Loading branch information
davmason authored May 7, 2021
1 parent 9c54ae4 commit f511a4a
Show file tree
Hide file tree
Showing 27 changed files with 744 additions and 159 deletions.
4 changes: 4 additions & 0 deletions src/coreclr/inc/profilepriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ struct ProfControlBlock
BOOL fGCInProgress;
BOOL fBaseSystemClassesLoaded;

BOOL fIsStoredProfilerRegistered;
CLSID clsStoredProfilerGuid;
SString sStoredProfilerPath;

#ifdef PROF_TEST_ONLY_FORCE_ELT_DATA
// #TestOnlyELT This implements a test-only (and debug-only) hook that allows a test
// profiler to ensure enter/leave/tailcall is enabled on startup even though no
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/vm/ceemain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,11 @@ void EEStartupHelper()
}
#endif

#ifdef PROFILING_SUPPORTED
// The diagnostics server might register a profiler for delayed startup, indicate here that
// it is not currently set. This will be set to TRUE if a profiler is registered.
g_profControlBlock.fIsStoredProfilerRegistered = FALSE;
#endif // PROFILING_SUPPORTED
#ifdef FEATURE_PERFTRACING
DiagnosticServerAdapter::Initialize();
DiagnosticServerAdapter::PauseForDiagnosticsMonitor();
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/vm/diagnosticserveradapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ class DiagnosticServerAdapter final
{
return ds_server_resume_runtime_startup();
}

static bool IsPausedInRuntimeStartup()
{
return ds_server_is_paused_in_startup();
}
};

#endif // FEATURE_PERFTRACING && !CROSSGEN_COMPILE
Expand Down
24 changes: 21 additions & 3 deletions src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,11 @@ DS_RT_DEFINE_ARRAY_REVERSE_ITERATOR (port_config_array, ds_rt_port_config_array_
/*
* DiagnosticsProfiler.
*/

#ifdef FEATURE_PROFAPI_ATTACH_DETACH
#ifdef PROFILING_SUPPORTED
#include "profilinghelper.h"
#include "profilinghelper.inl"

#ifdef FEATURE_PROFAPI_ATTACH_DETACH
static
uint32_t
ds_rt_profiler_attach (DiagnosticsAttachProfilerCommandPayload *payload)
Expand All @@ -285,7 +285,7 @@ ds_rt_profiler_attach (DiagnosticsAttachProfilerCommandPayload *payload)
// Certain actions are only allowable during attach, and this flag is how we track it.
ClrFlsSetThreadType (ThreadType_ProfAPI_Attach);

HRESULT hr;
HRESULT hr = S_OK;
EX_TRY {
hr = ProfilingAPIUtility::LoadProfilerForAttach (reinterpret_cast<const CLSID *>(ds_attach_profiler_command_payload_get_profiler_guid_cref (payload)),
reinterpret_cast<LPCWSTR>(ds_attach_profiler_command_payload_get_profiler_path (payload)),
Expand All @@ -302,6 +302,24 @@ ds_rt_profiler_attach (DiagnosticsAttachProfilerCommandPayload *payload)
}
#endif // FEATURE_PROFAPI_ATTACH_DETACH

static
uint32_t
ds_rt_profiler_startup (DiagnosticsStartupProfilerCommandPayload *payload)
{
STATIC_CONTRACT_NOTHROW;

HRESULT hr = S_OK;
EX_TRY {
memcpy(&(g_profControlBlock.clsStoredProfilerGuid), reinterpret_cast<const CLSID *>(ds_startup_profiler_command_payload_get_profiler_guid_cref (payload)), sizeof(CLSID));
g_profControlBlock.sStoredProfilerPath.Set(reinterpret_cast<LPCWSTR>(ds_startup_profiler_command_payload_get_profiler_path (payload)));
g_profControlBlock.fIsStoredProfilerRegistered = TRUE;
}
EX_CATCH_HRESULT (hr);

return hr;
}
#endif // PROFILING_SUPPORTED

/*
* DiagnosticServer.
*/
Expand Down
136 changes: 80 additions & 56 deletions src/coreclr/vm/profilinghelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -686,85 +686,109 @@ HRESULT ProfilingAPIUtility::AttemptLoadProfilerForStartup()

fProfEnabled = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_ENABLE_PROFILING);

// If profiling is not enabled, return.
if (fProfEnabled == 0)
{
LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling not enabled.\n"));
return S_FALSE;
}
NewArrayHolder<WCHAR> wszClsid(NULL);
NewArrayHolder<const WCHAR> cwszProfilerDLL(NULL);
CLSID *pClsid;
CLSID clsid;

LOG((LF_CORPROF, LL_INFO10, "**PROF: Initializing Profiling Services.\n"));
if (fProfEnabled != 0)
{
LOG((LF_CORPROF, LL_INFO10, "**PROF: Initializing Profiling Services.\n"));

// Get the CLSID of the profiler to CoCreate
NewArrayHolder<WCHAR> wszClsid(NULL);
NewArrayHolder<WCHAR> wszProfilerDLL(NULL);
NewArrayHolder<WCHAR> wszProfilerDLL(NULL);

IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER, &wszClsid));
IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER, &wszClsid));

#if defined(TARGET_ARM64)
IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH_ARM64, &wszProfilerDLL));
#elif defined(TARGET_ARM)
IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH_ARM32, &wszProfilerDLL));
#endif
if(wszProfilerDLL == NULL)
{
#ifdef TARGET_64BIT
IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH_64, &wszProfilerDLL));
#else
IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH_32, &wszProfilerDLL));
#endif
#if defined(TARGET_ARM64)
IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH_ARM64, &wszProfilerDLL));
#elif defined(TARGET_ARM)
IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH_ARM32, &wszProfilerDLL));
#endif
if(wszProfilerDLL == NULL)
{
IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH, &wszProfilerDLL));
#ifdef TARGET_64BIT
IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH_64, &wszProfilerDLL));
#else
IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH_32, &wszProfilerDLL));
#endif
if(wszProfilerDLL == NULL)
{
IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CORECLR_PROFILER_PATH, &wszProfilerDLL));
}
}
}

// If the environment variable doesn't exist, profiling is not enabled.
if (wszClsid == NULL)
{
LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling flag set, but required "
"environment variable does not exist.\n"));
// If the environment variable doesn't exist, profiling is not enabled.
if (wszClsid == NULL)
{
LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling flag set, but required "
"environment variable does not exist.\n"));

LogProfError(IDS_E_PROF_NO_CLSID);
LogProfError(IDS_E_PROF_NO_CLSID);

return S_FALSE;
}
return S_FALSE;
}

if ((wszProfilerDLL != NULL) && (wcslen(wszProfilerDLL) >= MAX_LONGPATH))
{
LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling flag set, but COR_PROFILER_PATH was not set properly.\n"));
if ((wszProfilerDLL != NULL) && (wcslen(wszProfilerDLL) >= MAX_LONGPATH))
{
LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling flag set, but COR_PROFILER_PATH was not set properly.\n"));

LogProfError(IDS_E_PROF_BAD_PATH);
LogProfError(IDS_E_PROF_BAD_PATH);

return S_FALSE;
}
return S_FALSE;
}

#ifdef TARGET_UNIX
// If the environment variable doesn't exist, profiling is not enabled.
if (wszProfilerDLL == NULL)
{
LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling flag set, but required "
"environment variable does not exist.\n"));
#ifdef TARGET_UNIX
// If the environment variable doesn't exist, profiling is not enabled.
if (wszProfilerDLL == NULL)
{
LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling flag set, but required "
"environment variable does not exist.\n"));

LogProfError(IDS_E_PROF_BAD_PATH);
LogProfError(IDS_E_PROF_BAD_PATH);

return S_FALSE;
}
#endif // TARGET_UNIX
return S_FALSE;
}
#endif // TARGET_UNIX

CLSID clsid;
hr = ProfilingAPIUtility::ProfilerCLSIDFromString(wszClsid, &clsid);
if (FAILED(hr))
hr = ProfilingAPIUtility::ProfilerCLSIDFromString(wszClsid, &clsid);
if (FAILED(hr))
{
// ProfilerCLSIDFromString already logged an event if there was a failure
return hr;
}

pClsid = &clsid;
cwszProfilerDLL.Assign(wszProfilerDLL.GetValue());
wszProfilerDLL.SuppressRelease();
}
else if (g_profControlBlock.fIsStoredProfilerRegistered)
{
// ProfilerCLSIDFromString already logged an event if there was a failure
return hr;
LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiler loading from GUID/Path stored from the IPC channel."));
pClsid = &(g_profControlBlock.clsStoredProfilerGuid);

// Convert to string for logging
constexpr size_t guidStringSize = 128;
wszClsid.Assign(new (nothrow) WCHAR[guidStringSize]);
if (wszClsid != NULL)
{
StringFromGUID2(*pClsid, wszClsid, guidStringSize);
}

// Assign, but don't take ownership of, the stored profiler path. This relies on
// g_profControlBlock.sStoredProfilerPath not mutating, which would invalidate the pointer.
cwszProfilerDLL.Assign(g_profControlBlock.sStoredProfilerPath.GetUnicode(), FALSE);
}
else
{
LOG((LF_CORPROF, LL_INFO10, "**PROF: Profiling not enabled.\n"));
return S_FALSE;
}

hr = LoadProfiler(
kStartupLoad,
&clsid,
pClsid,
wszClsid,
wszProfilerDLL,
cwszProfilerDLL,
NULL, // No client data for startup load
0); // No client data for startup load
if (FAILED(hr))
Expand Down
9 changes: 9 additions & 0 deletions src/mono/mono/eventpipe/ds-rt-mono.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,15 @@ ds_rt_profiler_attach (DiagnosticsAttachProfilerCommandPayload *payload)
return DS_IPC_E_NOTSUPPORTED;
}

static
inline
uint32_t
ds_rt_profiler_startup (DiagnosticsStartupProfilerCommandPayload *payload)
{
// TODO: Implement.
return DS_IPC_E_NOTSUPPORTED;
}

/*
* DiagnosticServer.
*/
Expand Down
Loading

0 comments on commit f511a4a

Please sign in to comment.