Skip to content

Commit f30ea37

Browse files
authored
Improve call counting mechanism (#32250)
* Improve call counting mechanism - Commit 1 - Reverts commit f954c6b, which reverted PR #1457 due to issues - Commit 2 - Fixes crashes and assertion failures seen by the original change, fixes #29934 - The crashes were caused by commit 6aa3c70 in the original PR - Call counting infos cannot be deleted when the corresponding call counting stubs may still run, because: - The remaining call count decremented by the stub is in the call counting info - The only way to get a code version / method desc from a stub is to go through the call counting info - Got one repro of the assertion failure in #22786 and it is most likely caused by the same issue, following heap corruption from modifying a deleted call counting info where the memory is reused for a `NativeCodeVersionNode`, messing up the method desc pointer - Fixed with a partial revert of the above commit. Added back the `Complete` stage and then call counting infos are deleted only after it's ensured that call counting stubs won't be used (shortly before deleting them). - Commit 3 - Public static functions of `CallCountingManager` that may be called through the debugger may occur before static initialization, added a check for null as suggested in #29892 * Fix crashes and assertion failures seen by the original change * Add check for null for some functions callable from the debugger
2 parents e6bb46c + ca19f1c commit f30ea37

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+4208
-1495
lines changed

docs/design/features/code-versioning.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -330,11 +330,7 @@ to update the active child at either of those levels (ReJIT uses SetActiveILCode
330330
In order to do step 3 the `CodeVersionManager` relies on one of three different mechanisms, a `FixupPrecode`, a `JumpStamp`, or backpatching entry point slots. In [method.hpp](https://github.com/dotnet/runtime/blob/master/src/coreclr/src/vm/method.hpp) these mechanisms are described in the `MethodDesc::IsVersionableWith*()` functions, and all methods have been classified to use at most one of the techniques, based on the `MethodDesc::IsVersionableWith*()` functions.
331331

332332
### Thread-safety ###
333-
CodeVersionManager is designed for use in a free-threaded environment, in many cases by requiring the caller to acquire a lock before calling. This lock can be acquired by constructing an instance of the
334-
335-
```
336-
CodeVersionManager::TableLockHolder(CodeVersionManager*)
337-
```
333+
CodeVersionManager is designed for use in a free-threaded environment, in many cases by requiring the caller to acquire a lock before calling. This lock can be acquired by constructing an instance of `CodeVersionManager::LockHolder`.
338334

339335
in some scope for the CodeVersionManager being operated on. CodeVersionManagers from different domains should not have their locks taken by the same thread with one exception, it is OK to take the shared domain manager lock and one AppDomain manager lock in that order. The lock is required to change the shape of the tree or traverse it but not to read/write configuration properties from each node. A few special cases:
340336

src/coreclr/src/debug/daccess/request.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4281,7 +4281,7 @@ HRESULT ClrDataAccess::GetPendingReJITID(CLRDATA_ADDRESS methodDesc, int *pRejit
42814281
PTR_MethodDesc pMD = PTR_MethodDesc(TO_TADDR(methodDesc));
42824282

42834283
CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager();
4284-
CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
4284+
CodeVersionManager::LockHolder codeVersioningLockHolder;
42854285
ILCodeVersion ilVersion = pCodeVersionManager->GetActiveILCodeVersion(pMD);
42864286
if (ilVersion.IsNull())
42874287
{
@@ -4313,7 +4313,7 @@ HRESULT ClrDataAccess::GetReJITInformation(CLRDATA_ADDRESS methodDesc, int rejit
43134313
PTR_MethodDesc pMD = PTR_MethodDesc(TO_TADDR(methodDesc));
43144314

43154315
CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager();
4316-
CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
4316+
CodeVersionManager::LockHolder codeVersioningLockHolder;
43174317
ILCodeVersion ilVersion = pCodeVersionManager->GetILCodeVersion(pMD, rejitId);
43184318
if (ilVersion.IsNull())
43194319
{
@@ -4365,7 +4365,7 @@ HRESULT ClrDataAccess::GetProfilerModifiedILInformation(CLRDATA_ADDRESS methodDe
43654365
PTR_MethodDesc pMD = PTR_MethodDesc(TO_TADDR(methodDesc));
43664366

43674367
CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager();
4368-
CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
4368+
CodeVersionManager::LockHolder codeVersioningLockHolder;
43694369
ILCodeVersion ilVersion = pCodeVersionManager->GetActiveILCodeVersion(pMD);
43704370
if (ilVersion.GetRejitState() != ILCodeVersion::kStateActive || !ilVersion.HasDefaultIL())
43714371
{
@@ -4398,7 +4398,7 @@ HRESULT ClrDataAccess::GetMethodsWithProfilerModifiedIL(CLRDATA_ADDRESS mod, CLR
43984398

43994399
PTR_Module pModule = PTR_Module(TO_TADDR(mod));
44004400
CodeVersionManager* pCodeVersionManager = pModule->GetCodeVersionManager();
4401-
CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
4401+
CodeVersionManager::LockHolder codeVersioningLockHolder;
44024402

44034403
LookupMap<PTR_MethodTable>::Iterator typeIter(&pModule->m_TypeDefToMethodTableMap);
44044404
for (int i = 0; typeIter.Next(); i++)

src/coreclr/src/debug/ee/debugger.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3633,7 +3633,7 @@ HRESULT Debugger::SetIP( bool fCanSetIPOnly, Thread *thread,Module *module,
36333633

36343634
CodeVersionManager *pCodeVersionManager = module->GetCodeVersionManager();
36353635
{
3636-
CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
3636+
CodeVersionManager::LockHolder codeVersioningLockHolder;
36373637
ILCodeVersion ilCodeVersion = pCodeVersionManager->GetActiveILCodeVersion(module, mdMeth);
36383638
if (!ilCodeVersion.IsDefaultVersion())
36393639
{
@@ -15243,6 +15243,15 @@ HRESULT Debugger::FuncEvalSetup(DebuggerIPCE_FuncEvalInfo *pEvalInfo,
1524315243
return CORDBG_E_FUNC_EVAL_BAD_START_POINT;
1524415244
}
1524515245

15246+
if (MethodDescBackpatchInfoTracker::IsLockOwnedByAnyThread())
15247+
{
15248+
// A thread may have suspended for the debugger while holding the slot backpatching lock while trying to enter
15249+
// cooperative GC mode. If the FuncEval calls a method that is eligible for slot backpatching (virtual or interface
15250+
// methods that are eligible for tiering), the FuncEval may deadlock on trying to acquire the same lock. Fail the
15251+
// FuncEval to avoid the issue.
15252+
return CORDBG_E_FUNC_EVAL_BAD_START_POINT;
15253+
}
15254+
1524615255
// Create a DebuggerEval to hold info about this eval while its in progress. Constructor copies the thread's
1524715256
// CONTEXT.
1524815257
DebuggerEval *pDE = new (interopsafe, nothrow) DebuggerEval(filterContext, pEvalInfo, fInException);

src/coreclr/src/debug/ee/functioninfo.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,7 @@ void DebuggerJitInfo::LazyInitBounds()
932932
LOG((LF_CORDB,LL_EVERYTHING, "DJI::LazyInitBounds: this=0x%x GetBoundariesAndVars success=0x%x\n", this, fSuccess));
933933

934934
// SetBoundaries uses the CodeVersionManager, need to take it now for lock ordering reasons
935-
CodeVersionManager::TableLockHolder lockHolder(mdesc->GetCodeVersionManager());
935+
CodeVersionManager::LockHolder codeVersioningLockHolder;
936936
Debugger::DebuggerDataLockHolder debuggerDataLockHolder(g_pDebugger);
937937

938938
if (!m_fAttemptInit)
@@ -1058,7 +1058,7 @@ void DebuggerJitInfo::SetBoundaries(ULONG32 cMap, ICorDebugInfo::OffsetMapping *
10581058
// Pick a unique initial value (-10) so that the 1st doesn't accidentally match.
10591059
int ilPrevOld = -10;
10601060

1061-
_ASSERTE(m_nativeCodeVersion.GetMethodDesc()->GetCodeVersionManager()->LockOwnedByCurrentThread());
1061+
_ASSERTE(CodeVersionManager::IsLockOwnedByCurrentThread());
10621062

10631063
InstrumentedILOffsetMapping mapping;
10641064

@@ -1605,8 +1605,8 @@ DebuggerJitInfo *DebuggerMethodInfo::FindOrCreateInitAndAddJitInfo(MethodDesc* f
16051605
NativeCodeVersion nativeCodeVersion;
16061606
if (fd->IsVersionable())
16071607
{
1608-
CodeVersionManager::TableLockHolder lockHolder(fd->GetCodeVersionManager());
16091608
CodeVersionManager *pCodeVersionManager = fd->GetCodeVersionManager();
1609+
CodeVersionManager::LockHolder codeVersioningLockHolder;
16101610
nativeCodeVersion = pCodeVersionManager->GetNativeCodeVersion(fd, startAddr);
16111611
if (nativeCodeVersion.IsNull())
16121612
{
@@ -2086,7 +2086,7 @@ void DebuggerMethodInfo::CreateDJIsForMethodDesc(MethodDesc * pMethodDesc)
20862086
CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager();
20872087
// grab the code version lock to iterate available versions of the code
20882088
{
2089-
CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
2089+
CodeVersionManager::LockHolder codeVersioningLockHolder;
20902090
NativeCodeVersionCollection nativeCodeVersions = pCodeVersionManager->GetNativeCodeVersions(pMethodDesc);
20912091

20922092
for (NativeCodeVersionIterator itr = nativeCodeVersions.Begin(), end = nativeCodeVersions.End(); itr != end; itr++)

src/coreclr/src/inc/CrstTypes.def

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ Crst NativeImageCache
280280
End
281281

282282
Crst GCCover
283-
AcquiredBefore LoaderHeap ReJITDomainTable
283+
AcquiredBefore LoaderHeap CodeVersioning
284284
End
285285

286286
Crst GCMemoryPressure
@@ -486,7 +486,7 @@ Crst Reflection
486486
End
487487

488488
// Used to synchronize all rejit information stored in a given AppDomain.
489-
Crst ReJITDomainTable
489+
Crst CodeVersioning
490490
AcquiredBefore LoaderHeap SingleUseLock DeadlockDetection JumpStubCache DebuggerController FuncPtrStubs
491491
AcquiredAfter ReJITGlobalRequest ThreadStore GlobalStrLiteralMap SystemDomain DebuggerMutex MethodDescBackpatchInfoTracker
492492
End
@@ -495,7 +495,7 @@ End
495495
// new functions to rejit tables, or request Reverts on existing functions in the rejit
496496
// tables. One of these crsts exist per runtime.
497497
Crst ReJITGlobalRequest
498-
AcquiredBefore ThreadStore ReJITDomainTable SystemDomain JitInlineTrackingMap
498+
AcquiredBefore ThreadStore CodeVersioning SystemDomain JitInlineTrackingMap
499499
End
500500

501501
// ETW infrastructure uses this crst to protect a hash table of TypeHandles which is
@@ -679,7 +679,7 @@ Crst InlineTrackingMap
679679
End
680680

681681
Crst JitInlineTrackingMap
682-
AcquiredBefore ReJITDomainTable ThreadStore LoaderAllocator
682+
AcquiredBefore CodeVersioning ThreadStore LoaderAllocator
683683
End
684684

685685
Crst EventPipe
@@ -695,6 +695,7 @@ Crst ReadyToRunEntryPointToMethodDescMap
695695
End
696696

697697
Crst TieredCompilation
698+
AcquiredAfter CodeVersioning
698699
AcquiredBefore ThreadpoolTimerQueue
699700
End
700701

src/coreclr/src/inc/clrconfigvalues.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,10 +633,17 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_HillClimbing_GainExponent,
633633
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredCompilation, W("TieredCompilation"), 1, "Enables tiered compilation")
634634
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TC_QuickJit, W("TC_QuickJit"), 1, "For methods that would be jitted, enable using quick JIT when appropriate.")
635635
RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_QuickJitForLoops, W("TC_QuickJitForLoops"), 0, "When quick JIT is enabled, quick JIT may also be used for methods that contain loops.")
636+
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TC_AggressiveTiering, W("TC_AggressiveTiering"), 0, "Transition through tiers aggressively.")
636637
RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCountThreshold, W("TC_CallCountThreshold"), 30, "Number of times a method must be called in tier 0 after which it is promoted to the next tier.")
637638
RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCountingDelayMs, W("TC_CallCountingDelayMs"), 100, "A perpetual delay in milliseconds that is applied call counting in tier 0 and jitting at higher tiers, while there is startup-like activity.")
638639
RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DelaySingleProcMultiplier, W("TC_DelaySingleProcMultiplier"), 10, "Multiplier for TC_CallCountingDelayMs that is applied on a single-processor machine or when the process is affinitized to a single processor.")
639640
RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCounting, W("TC_CallCounting"), 1, "Enabled by default (only activates when TieredCompilation is also enabled). If disabled immediately backpatches prestub, and likely prevents any promotion to higher tiers")
641+
RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_UseCallCountingStubs, W("TC_UseCallCountingStubs"), 1, "Uses call counting stubs for faster call counting.")
642+
#ifdef _DEBUG
643+
RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DeleteCallCountingStubsAfter, W("TC_DeleteCallCountingStubsAfter"), 1, "Deletes call counting stubs after this many have completed. Zero to disable deleting.")
644+
#else
645+
RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DeleteCallCountingStubsAfter, W("TC_DeleteCallCountingStubsAfter"), 4096, "Deletes call counting stubs after this many have completed. Zero to disable deleting.")
646+
#endif
640647
#endif
641648

642649
///

0 commit comments

Comments
 (0)