Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature to be able to selectively deoptimize methods #88797

Merged
merged 11 commits into from
Jul 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/coreclr/debug/daccess/task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5225,7 +5225,7 @@ EnumMethodInstances::Next(ClrDataAccess* dac,
}
}

if (!m_methodIter.Current()->HasNativeCode())
if (!m_methodIter.Current()->HasNativeCodeReJITAware())
{
goto NextMethod;
}
Expand All @@ -5243,7 +5243,7 @@ EnumMethodInstances::CdStart(MethodDesc* methodDesc,
CLRDATA_ENUM* handle)
{
if (!methodDesc->HasClassOrMethodInstantiation() &&
!methodDesc->HasNativeCode())
!methodDesc->HasNativeCodeReJITAware())
{
*handle = 0;
return S_FALSE;
Expand Down
90 changes: 90 additions & 0 deletions src/coreclr/debug/di/rsfunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ HRESULT CordbFunction::QueryInterface(REFIID id, void **pInterface)
{
*pInterface = static_cast<ICorDebugFunction4*>(this);
}
else if (id == IID_ICorDebugFunction5)
{
*pInterface = static_cast<ICorDebugFunction5*>(this);
}
else if (id == IID_IUnknown)
{
*pInterface = static_cast<IUnknown*>(static_cast<ICorDebugFunction*>(this));
Expand Down Expand Up @@ -606,6 +610,92 @@ HRESULT CordbFunction::CreateNativeBreakpoint(ICorDebugFunctionBreakpoint **ppBr
return hr;
}

//-----------------------------------------------------------------------------
// CordbFunction::DisableOptimizations
// Public method for ICorDebugFunction5::DisableOptimizations.
// Triggers a new JIT so the next time the function is called, it will be unoptimized.
//
// Parameters
//
//
// Returns:
// S_OK on success.
//-----------------------------------------------------------------------------
HRESULT CordbFunction::DisableOptimizations()
{
PUBLIC_API_ENTRY(this);
FAIL_IF_NEUTERED(this);
ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());

HRESULT hr = S_OK;

CordbProcess * pProcess = GetProcess();
RSLockHolder lockHolder(pProcess->GetProcessLock());

DebuggerIPCEvent event;
CordbAppDomain * pAppDomain = GetAppDomain();
_ASSERTE (pAppDomain != NULL);

pProcess->InitIPCEvent(&event, DB_IPCE_DISABLE_OPTS, true, pAppDomain->GetADToken());
event.DisableOptData.funcMetadataToken = m_MDToken;
event.DisableOptData.pModule = m_pModule->GetRuntimeModule();

lockHolder.Release();
hr = pProcess->m_cordb->SendIPCEvent(pProcess, &event, sizeof(DebuggerIPCEvent));
lockHolder.Acquire();

_ASSERTE(event.type == DB_IPCE_DISABLE_OPTS_RESULT);

return event.hr;
}

//-----------------------------------------------------------------------------
// CordbFunction::AreOptimizationsDisabled
// Public method for ICorDebugFunction5::AreOptimizationsDisabled.
// Indicates whether this method had optimizations disabled already.
//
// Parameters:
// BOOL *pOptimizationsDisabled
//
//
// Returns:
// S_OK on success.
//-----------------------------------------------------------------------------
HRESULT CordbFunction::AreOptimizationsDisabled(BOOL *pOptimizationsDisabled)
{
PUBLIC_API_ENTRY(this);
FAIL_IF_NEUTERED(this);
ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());

HRESULT hr = S_OK;

if (pOptimizationsDisabled == NULL)
{
return E_INVALIDARG;
}

CordbProcess * pProcess = GetProcess();
RSLockHolder lockHolder(pProcess->GetProcessLock());

DebuggerIPCEvent event;
CordbAppDomain * pAppDomain = GetAppDomain();
_ASSERTE (pAppDomain != NULL);

pProcess->InitIPCEvent(&event, DB_IPCE_IS_OPTS_DISABLED, true, pAppDomain->GetADToken());
Copy link
Member

Choose a reason for hiding this comment

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

If the process is stopped to be able to do this inspection - is there any reason to not ask this question though the DAC?

Copy link
Member Author

Choose a reason for hiding this comment

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

Since this is already tested and ready to go my plan is to check it in as is and do a follow up PR to address this and the GetFunctionAddress question

event.DisableOptData.funcMetadataToken = m_MDToken;
event.DisableOptData.pModule = m_pModule->GetRuntimeModule();

lockHolder.Release();
hr = pProcess->m_cordb->SendIPCEvent(pProcess, &event, sizeof(DebuggerIPCEvent));
lockHolder.Acquire();

_ASSERTE(event.type == DB_IPCE_IS_OPTS_DISABLED_RESULT);

*pOptimizationsDisabled = event.IsOptsDisabledData.value;

return event.hr;;
}

// determine whether we have a native-only implementation
// Arguments:
// Input: none (we use information in various data members of this instance of CordbFunction: m_isNativeImpl,
Expand Down
9 changes: 8 additions & 1 deletion src/coreclr/debug/di/rspriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -5349,7 +5349,8 @@ class CordbFunction : public CordbBase,
public ICorDebugFunction,
public ICorDebugFunction2,
public ICorDebugFunction3,
public ICorDebugFunction4
public ICorDebugFunction4,
public ICorDebugFunction5
{
public:
//-----------------------------------------------------------
Expand Down Expand Up @@ -5412,6 +5413,12 @@ class CordbFunction : public CordbBase,
//-----------------------------------------------------------
COM_METHOD CreateNativeBreakpoint(ICorDebugFunctionBreakpoint **ppBreakpoint);

//-----------------------------------------------------------
// ICorDebugFunction5
//-----------------------------------------------------------
COM_METHOD AreOptimizationsDisabled(BOOL *pOptimizationsDisabled);
COM_METHOD DisableOptimizations();

//-----------------------------------------------------------
// Internal members
//-----------------------------------------------------------
Expand Down
199 changes: 199 additions & 0 deletions src/coreclr/debug/ee/debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10442,6 +10442,60 @@ bool Debugger::HandleIPCEvent(DebuggerIPCEvent * pEvent)
break;
}

case DB_IPCE_DISABLE_OPTS:
{
Module *pModule = pEvent->DisableOptData.pModule.GetRawPtr();
mdToken methodDef = pEvent->DisableOptData.funcMetadataToken;
_ASSERTE(TypeFromToken(methodDef) == mdtMethodDef);

HRESULT hr = E_INVALIDARG;
EX_TRY
{
hr = DeoptimizeMethod(pModule, methodDef);
}
EX_CATCH_HRESULT(hr);

DebuggerIPCEvent * pIPCResult = m_pRCThread->GetIPCEventReceiveBuffer();

InitIPCEvent(pIPCResult,
DB_IPCE_DISABLE_OPTS_RESULT,
g_pEEInterface->GetThread(),
pEvent->vmAppDomain);

pIPCResult->hr = hr;

m_pRCThread->SendIPCReply();
}
break;

case DB_IPCE_IS_OPTS_DISABLED:
{
Module *pModule = pEvent->DisableOptData.pModule.GetRawPtr();
mdToken methodDef = pEvent->DisableOptData.funcMetadataToken;
_ASSERTE(TypeFromToken(methodDef) == mdtMethodDef);

HRESULT hr = E_INVALIDARG;
BOOL deoptimized = FALSE;
EX_TRY
{
hr = IsMethodDeoptimized(pModule, methodDef, &deoptimized);
}
EX_CATCH_HRESULT(hr);

DebuggerIPCEvent * pIPCResult = m_pRCThread->GetIPCEventReceiveBuffer();

InitIPCEvent(pIPCResult,
DB_IPCE_IS_OPTS_DISABLED_RESULT,
g_pEEInterface->GetThread(),
pEvent->vmAppDomain);

pIPCResult->IsOptsDisabledData.value = deoptimized;
pIPCResult->hr = hr;

m_pRCThread->SendIPCReply();
}
break;

case DB_IPCE_BREAKPOINT_ADD:
{

Expand Down Expand Up @@ -12211,6 +12265,151 @@ HRESULT Debugger::ReleaseRemoteBuffer(void *pBuffer, bool removeFromBlobList)
return S_OK;
}

#ifndef DACCESS_COMPILE
HRESULT Debugger::DeoptimizeMethodHelper(Module* pModule, mdMethodDef methodDef)
{
CONTRACTL
{
THROWS;
CAN_TAKE_LOCK;
GC_NOTRIGGER;
}
CONTRACTL_END;

_ASSERTE(!CodeVersionManager::IsLockOwnedByCurrentThread());
HRESULT hr = S_OK;
ILCodeVersion ilCodeVersion;
CodeVersionManager *pCodeVersionManager = pModule->GetCodeVersionManager();

{
CodeVersionManager::LockHolder codeVersioningLockHolder;
if (FAILED(hr = pCodeVersionManager->AddILCodeVersion(pModule, methodDef, &ilCodeVersion, TRUE)))
{
LOG((LF_TIEREDCOMPILATION, LL_INFO100, "TieredCompilationManager::DeOptimizeMethodHelper Module=0x%x Method=0x%x, AddILCodeVersion returned hr 0x%x\n",
pModule, methodDef,
hr));
return hr;
}

// We are using the profiler ReJIT infrastructure to trigger a new jit. We don't want to modify the IL or
// call back in to anything so set it all here to match the original IL and debug codegen flags
ilCodeVersion.SetIL(ILCodeVersion(pModule, methodDef).GetIL());
ilCodeVersion.SetJitFlags(COR_PRF_CODEGEN_DISABLE_ALL_OPTIMIZATIONS | COR_PRF_CODEGEN_DEBUG_INFO);
ilCodeVersion.SetRejitState(ILCodeVersion::kStateActive);
ilCodeVersion.SetEnableReJITCallback(false);
}

_ASSERTE(!ilCodeVersion.IsNull());
{
if (FAILED(hr = pCodeVersionManager->SetActiveILCodeVersions(&ilCodeVersion, 1, NULL)))
{
LOG((LF_TIEREDCOMPILATION, LL_INFO100, "TieredCompilationManager::DeOptimizeMethodHelper Module=0x%x Method=0x%x, SetActiveILCodeVersions returned hr 0x%x\n",
pModule, methodDef,
hr));
return hr;
}
}

return hr;
}

HRESULT Debugger::DeoptimizeMethod(Module* pModule, mdMethodDef methodDef)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
}
CONTRACTL_END;

// First deoptimize the method itself
HRESULT hr = DeoptimizeMethodHelper(pModule, methodDef);
if (FAILED(hr))
{
LOG((LF_TIEREDCOMPILATION, LL_INFO100, "TieredCompilationManager::DeOptimizeMethod Module=0x%x Method=0x%x,, initial ReJIT returned hr 0x%x, aborting\n",
pModule, methodDef, hr));
return hr;
}

// Now deoptimize anything that has inlined it in a R2R method
AppDomain::AssemblyIterator domainAssemblyIterator = SystemDomain::System()->DefaultDomain()->IterateAssembliesEx((AssemblyIterationFlags) (kIncludeLoaded | kIncludeExecution));
CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
NativeImageInliningIterator inlinerIter;
while (domainAssemblyIterator.Next(pDomainAssembly.This()))
{
Module *pCandidateModule = pDomainAssembly->GetModule();
if (pCandidateModule->HasReadyToRunInlineTrackingMap())
{
inlinerIter.Reset(pCandidateModule, MethodInModule(pModule, methodDef));

while (inlinerIter.Next())
{
MethodInModule inliner = inlinerIter.GetMethod();
_ASSERTE(TypeFromToken(inliner.m_methodDef) == mdtMethodDef);
DeoptimizeMethodHelper(inliner.m_module, inliner.m_methodDef);
}
}
}

// Next any JIT methods
MethodDesc *pMethodDesc = pModule->LookupMethodDef(methodDef);
if (pMethodDesc != NULL && pModule->HasJitInlineTrackingMap())
{
InlineSArray<MethodDesc *, 10> inliners;
auto lambda = [&inliners](MethodDesc *inliner, MethodDesc *inlinee)
{
_ASSERTE(!inliner->IsNoMetadata());

if (inliner->IsIL())
{
inliners.Append(inliner);
}

// Keep going
return true;
};

JITInlineTrackingMap *pMap = pModule->GetJitInlineTrackingMap();
pMap->VisitInliners(pMethodDesc, lambda);

for (auto it = inliners.Begin(); it != inliners.End(); ++it)
{
Module *inlinerModule = (*it)->GetModule();
mdMethodDef inlinerMethodDef = (*it)->GetMemberDef();
_ASSERTE(TypeFromToken(inlinerMethodDef) == mdtMethodDef);
DeoptimizeMethodHelper(inlinerModule, inlinerMethodDef);
}
}

return hr;
}
#endif //DACCESS_COMPILE

HRESULT Debugger::IsMethodDeoptimized(Module *pModule, mdMethodDef methodDef, BOOL *pResult)
{
CONTRACTL
{
NOTHROW;
CAN_TAKE_LOCK;
GC_NOTRIGGER;
}
CONTRACTL_END;

if (pModule == NULL || pResult == NULL || TypeFromToken(methodDef) != mdtMethodDef)
{
return E_INVALIDARG;
}

{
CodeVersionManager::LockHolder codeVersioningLockHolder;
CodeVersionManager *pCodeVersionManager = pModule->GetCodeVersionManager();
ILCodeVersion activeILVersion = pCodeVersionManager->GetActiveILCodeVersion(pModule, methodDef);
*pResult = activeILVersion.IsDeoptimized();
}

return S_OK;
}

//
// UnrecoverableError causes the Left Side to enter a state where no more
// debugging can occur and we leave around enough information for the
Expand Down
9 changes: 9 additions & 0 deletions src/coreclr/debug/ee/debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -2212,6 +2212,15 @@ class Debugger : public DebugInterface
return m_trappingRuntimeThreads;
}

#ifndef DACCESS_COMPILE
private:
HRESULT DeoptimizeMethodHelper(Module* pModule, mdMethodDef methodDef);

public:
HRESULT DeoptimizeMethod(Module* pModule, mdMethodDef methodDef);
#endif //DACCESS_COMPILE
HRESULT IsMethodDeoptimized(Module *pModule, mdMethodDef methodDef, BOOL *pResult);

//
// The debugger mutex is used to protect any "global" Left Side
// data structures. The RCThread takes it when handling a Right
Expand Down
6 changes: 5 additions & 1 deletion src/coreclr/debug/ee/functioninfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1580,7 +1580,11 @@ DebuggerJitInfo *DebuggerMethodInfo::FindOrCreateInitAndAddJitInfo(MethodDesc* f
startAddr = g_pEEInterface->GetFunctionAddress(fd);
if (startAddr == NULL)
{
return NULL;
startAddr = fd->GetNativeCodeReJITAware();
if (startAddr == NULL)
{
return NULL;
}
}
}
else
Expand Down
Loading
Loading