| 1 | // Licensed to the .NET Foundation under one or more agreements. | 
|---|
| 2 | // The .NET Foundation licenses this file to you under the MIT license. | 
|---|
| 3 | // See the LICENSE file in the project root for more information. | 
|---|
| 4 |  | 
|---|
| 5 | /* | 
|---|
| 6 | * GCSCAN.CPP | 
|---|
| 7 | * | 
|---|
| 8 | * GC Root Scanning | 
|---|
| 9 | * | 
|---|
| 10 |  | 
|---|
| 11 | * | 
|---|
| 12 | */ | 
|---|
| 13 |  | 
|---|
| 14 | #include "common.h" | 
|---|
| 15 |  | 
|---|
| 16 | #include "gcenv.h" | 
|---|
| 17 |  | 
|---|
| 18 | #include "gcscan.h" | 
|---|
| 19 | #include "gc.h" | 
|---|
| 20 | #include "objecthandle.h" | 
|---|
| 21 |  | 
|---|
| 22 | VOLATILE(int32_t) GCScan::m_GcStructuresInvalidCnt = 1; | 
|---|
| 23 |  | 
|---|
| 24 | bool GCScan::GetGcRuntimeStructuresValid () | 
|---|
| 25 | { | 
|---|
| 26 | LIMITED_METHOD_CONTRACT; | 
|---|
| 27 | SUPPORTS_DAC; | 
|---|
| 28 | _ASSERTE ((int32_t)m_GcStructuresInvalidCnt >= 0); | 
|---|
| 29 | return (int32_t)m_GcStructuresInvalidCnt == 0; | 
|---|
| 30 | } | 
|---|
| 31 |  | 
|---|
| 32 | #ifndef DACCESS_COMPILE | 
|---|
| 33 |  | 
|---|
| 34 | // | 
|---|
| 35 | // Dependent handle promotion scan support | 
|---|
| 36 | // | 
|---|
| 37 |  | 
|---|
| 38 | // This method is called first during the mark phase. It's job is to set up the context for further scanning | 
|---|
| 39 | // (remembering the scan parameters the GC gives us and initializing some state variables we use to determine | 
|---|
| 40 | // whether further scans will be required or not). | 
|---|
| 41 | // | 
|---|
| 42 | // This scan is not guaranteed to return complete results due to the GC context in which we are called. In | 
|---|
| 43 | // particular it is possible, due to either a mark stack overflow or unsynchronized operation in server GC | 
|---|
| 44 | // mode, that not all reachable objects will be reported as promoted yet. However, the operations we perform | 
|---|
| 45 | // will still be correct and this scan allows us to spot a common optimization where no dependent handles are | 
|---|
| 46 | // due for retirement in this particular GC. This is an important optimization to take advantage of since | 
|---|
| 47 | // synchronizing the GC to calculate complete results is a costly operation. | 
|---|
| 48 | void GCScan::GcDhInitialScan(promote_func* fn, int condemned, int max_gen, ScanContext* sc) | 
|---|
| 49 | { | 
|---|
| 50 | // We allocate space for dependent handle scanning context during Ref_Initialize. Under server GC there | 
|---|
| 51 | // are actually as many contexts as heaps (and CPUs). Ref_GetDependentHandleContext() retrieves the | 
|---|
| 52 | // correct context for the current GC thread based on the ScanContext passed to us by the GC. | 
|---|
| 53 | DhContext *pDhContext = Ref_GetDependentHandleContext(sc); | 
|---|
| 54 |  | 
|---|
| 55 | // Record GC callback parameters in the DH context so that the GC doesn't continually have to pass the | 
|---|
| 56 | // same data to each call. | 
|---|
| 57 | pDhContext->m_pfnPromoteFunction = fn; | 
|---|
| 58 | pDhContext->m_iCondemned = condemned; | 
|---|
| 59 | pDhContext->m_iMaxGen = max_gen; | 
|---|
| 60 | pDhContext->m_pScanContext = sc; | 
|---|
| 61 |  | 
|---|
| 62 | // Look for dependent handle whose primary has been promoted but whose secondary has not. Promote the | 
|---|
| 63 | // secondary in those cases. Additionally this scan sets the m_fUnpromotedPrimaries and m_fPromoted state | 
|---|
| 64 | // flags in the DH context. The m_fUnpromotedPrimaries flag is the most interesting here: if this flag is | 
|---|
| 65 | // false after the scan then it doesn't matter how many object promotions might currently be missing since | 
|---|
| 66 | // there are no secondary objects that are currently unpromoted anyway. This is the (hopefully common) | 
|---|
| 67 | // circumstance under which we don't have to perform any costly additional re-scans. | 
|---|
| 68 | Ref_ScanDependentHandlesForPromotion(pDhContext); | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 | // This method is called after GcDhInitialScan and before each subsequent scan (GcDhReScan below). It | 
|---|
| 72 | // determines whether any handles are left that have unpromoted secondaries. | 
|---|
| 73 | bool GCScan::GcDhUnpromotedHandlesExist(ScanContext* sc) | 
|---|
| 74 | { | 
|---|
| 75 | WRAPPER_NO_CONTRACT; | 
|---|
| 76 | // Locate our dependent handle context based on the GC context. | 
|---|
| 77 | DhContext *pDhContext = Ref_GetDependentHandleContext(sc); | 
|---|
| 78 |  | 
|---|
| 79 | return pDhContext->m_fUnpromotedPrimaries; | 
|---|
| 80 | } | 
|---|
| 81 |  | 
|---|
| 82 | // Perform a re-scan of dependent handles, promoting secondaries associated with newly promoted primaries as | 
|---|
| 83 | // above. We may still need to call this multiple times since promotion of a secondary late in the table could | 
|---|
| 84 | // promote a primary earlier in the table. Also, GC graph promotions are not guaranteed to be complete by the | 
|---|
| 85 | // time the promotion callback returns (the mark stack can overflow). As a result the GC might have to call | 
|---|
| 86 | // this method in a loop. The scan records state that let's us know when to terminate (no further handles to | 
|---|
| 87 | // be promoted or no promotions in the last scan). Returns true if at least one object was promoted as a | 
|---|
| 88 | // result of the scan. | 
|---|
| 89 | bool GCScan::GcDhReScan(ScanContext* sc) | 
|---|
| 90 | { | 
|---|
| 91 | // Locate our dependent handle context based on the GC context. | 
|---|
| 92 | DhContext *pDhContext = Ref_GetDependentHandleContext(sc); | 
|---|
| 93 |  | 
|---|
| 94 | return Ref_ScanDependentHandlesForPromotion(pDhContext); | 
|---|
| 95 | } | 
|---|
| 96 |  | 
|---|
| 97 | /* | 
|---|
| 98 | * Scan for dead weak pointers | 
|---|
| 99 | */ | 
|---|
| 100 |  | 
|---|
| 101 | void GCScan::GcWeakPtrScan( promote_func* fn, int condemned, int max_gen, ScanContext* sc ) | 
|---|
| 102 | { | 
|---|
| 103 | // Clear out weak pointers that are no longer live. | 
|---|
| 104 | Ref_CheckReachable(condemned, max_gen, (uintptr_t)sc); | 
|---|
| 105 |  | 
|---|
| 106 | // Clear any secondary objects whose primary object is now definitely dead. | 
|---|
| 107 | Ref_ScanDependentHandlesForClearing(condemned, max_gen, sc, fn); | 
|---|
| 108 | } | 
|---|
| 109 |  | 
|---|
| 110 | static void CALLBACK CheckPromoted(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t * /*pExtraInfo*/, uintptr_t /*lp1*/, uintptr_t /*lp2*/) | 
|---|
| 111 | { | 
|---|
| 112 | LIMITED_METHOD_CONTRACT; | 
|---|
| 113 |  | 
|---|
| 114 | LOG((LF_GC, LL_INFO100000, LOG_HANDLE_OBJECT_CLASS( "Checking referent of Weak-", pObjRef, "to ", *pObjRef))); | 
|---|
| 115 |  | 
|---|
| 116 | Object **pRef = (Object **)pObjRef; | 
|---|
| 117 | if (!g_theGCHeap->IsPromoted(*pRef)) | 
|---|
| 118 | { | 
|---|
| 119 | LOG((LF_GC, LL_INFO100, LOG_HANDLE_OBJECT_CLASS( "Severing Weak-", pObjRef, "to unreachable ", *pObjRef))); | 
|---|
| 120 |  | 
|---|
| 121 | *pRef = NULL; | 
|---|
| 122 | } | 
|---|
| 123 | else | 
|---|
| 124 | { | 
|---|
| 125 | LOG((LF_GC, LL_INFO1000000, "reachable "LOG_OBJECT_CLASS(*pObjRef))); | 
|---|
| 126 | } | 
|---|
| 127 | } | 
|---|
| 128 |  | 
|---|
| 129 | void GCScan::GcWeakPtrScanBySingleThread( int condemned, int max_gen, ScanContext* sc ) | 
|---|
| 130 | { | 
|---|
| 131 | UNREFERENCED_PARAMETER(condemned); | 
|---|
| 132 | UNREFERENCED_PARAMETER(max_gen); | 
|---|
| 133 | GCToEEInterface::SyncBlockCacheWeakPtrScan(&CheckPromoted, (uintptr_t)sc, 0); | 
|---|
| 134 | } | 
|---|
| 135 |  | 
|---|
| 136 | void GCScan::GcScanSizedRefs(promote_func* fn, int condemned, int max_gen, ScanContext* sc) | 
|---|
| 137 | { | 
|---|
| 138 | Ref_ScanSizedRefHandles(condemned, max_gen, sc, fn); | 
|---|
| 139 | } | 
|---|
| 140 |  | 
|---|
| 141 | void GCScan::GcShortWeakPtrScan(promote_func* fn,  int condemned, int max_gen, | 
|---|
| 142 | ScanContext* sc) | 
|---|
| 143 | { | 
|---|
| 144 | UNREFERENCED_PARAMETER(fn); | 
|---|
| 145 | Ref_CheckAlive(condemned, max_gen, (uintptr_t)sc); | 
|---|
| 146 | } | 
|---|
| 147 |  | 
|---|
| 148 | /* | 
|---|
| 149 | * Scan all stack roots in this 'namespace' | 
|---|
| 150 | */ | 
|---|
| 151 |  | 
|---|
| 152 | void GCScan::GcScanRoots(promote_func* fn,  int condemned, int max_gen, | 
|---|
| 153 | ScanContext* sc) | 
|---|
| 154 | { | 
|---|
| 155 | GCToEEInterface::GcScanRoots(fn, condemned, max_gen, sc); | 
|---|
| 156 | } | 
|---|
| 157 |  | 
|---|
| 158 | /* | 
|---|
| 159 | * Scan all handle roots in this 'namespace' | 
|---|
| 160 | */ | 
|---|
| 161 |  | 
|---|
| 162 |  | 
|---|
| 163 | void GCScan::GcScanHandles (promote_func* fn,  int condemned, int max_gen, | 
|---|
| 164 | ScanContext* sc) | 
|---|
| 165 | { | 
|---|
| 166 | STRESS_LOG1(LF_GC|LF_GCROOTS, LL_INFO10, "GcScanHandles (Promotion Phase = %d)\n", sc->promotion); | 
|---|
| 167 | if (sc->promotion) | 
|---|
| 168 | { | 
|---|
| 169 | Ref_TracePinningRoots(condemned, max_gen, sc, fn); | 
|---|
| 170 | Ref_TraceNormalRoots(condemned, max_gen, sc, fn); | 
|---|
| 171 | } | 
|---|
| 172 | else | 
|---|
| 173 | { | 
|---|
| 174 | Ref_UpdatePointers(condemned, max_gen, sc, fn); | 
|---|
| 175 | Ref_UpdatePinnedPointers(condemned, max_gen, sc, fn); | 
|---|
| 176 | Ref_ScanDependentHandlesForRelocation(condemned, max_gen, sc, fn); | 
|---|
| 177 | } | 
|---|
| 178 | } | 
|---|
| 179 |  | 
|---|
| 180 | /* | 
|---|
| 181 | * Scan all handle roots in this 'namespace' for profiling | 
|---|
| 182 | */ | 
|---|
| 183 |  | 
|---|
| 184 | void GCScan::GcScanHandlesForProfilerAndETW (int max_gen, ScanContext* sc, handle_scan_fn fn) | 
|---|
| 185 | { | 
|---|
| 186 | LIMITED_METHOD_CONTRACT; | 
|---|
| 187 |  | 
|---|
| 188 | #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) | 
|---|
| 189 | LOG((LF_GC|LF_GCROOTS, LL_INFO10, "Profiler Root Scan Phase, Handles\n")); | 
|---|
| 190 | Ref_ScanHandlesForProfilerAndETW(max_gen, (uintptr_t)sc, fn); | 
|---|
| 191 | #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) | 
|---|
| 192 | } | 
|---|
| 193 |  | 
|---|
| 194 | /* | 
|---|
| 195 | * Scan dependent handles in this 'namespace' for profiling | 
|---|
| 196 | */ | 
|---|
| 197 | void GCScan::GcScanDependentHandlesForProfilerAndETW (int max_gen, ScanContext* sc, handle_scan_fn fn) | 
|---|
| 198 | { | 
|---|
| 199 | LIMITED_METHOD_CONTRACT; | 
|---|
| 200 |  | 
|---|
| 201 | #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) | 
|---|
| 202 | LOG((LF_GC|LF_GCROOTS, LL_INFO10, "Profiler Root Scan Phase, DependentHandles\n")); | 
|---|
| 203 | Ref_ScanDependentHandlesForProfilerAndETW(max_gen, sc, fn); | 
|---|
| 204 | #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) | 
|---|
| 205 | } | 
|---|
| 206 |  | 
|---|
| 207 | void GCScan::GcRuntimeStructuresValid (BOOL bValid) | 
|---|
| 208 | { | 
|---|
| 209 | WRAPPER_NO_CONTRACT; | 
|---|
| 210 | if (!bValid) | 
|---|
| 211 | { | 
|---|
| 212 | int32_t result; | 
|---|
| 213 | result = Interlocked::Increment (&m_GcStructuresInvalidCnt); | 
|---|
| 214 | _ASSERTE (result > 0); | 
|---|
| 215 | } | 
|---|
| 216 | else | 
|---|
| 217 | { | 
|---|
| 218 | int32_t result; | 
|---|
| 219 | result = Interlocked::Decrement (&m_GcStructuresInvalidCnt); | 
|---|
| 220 | _ASSERTE (result >= 0); | 
|---|
| 221 | } | 
|---|
| 222 | } | 
|---|
| 223 |  | 
|---|
| 224 | void GCScan::GcDemote (int condemned, int max_gen, ScanContext* sc) | 
|---|
| 225 | { | 
|---|
| 226 | Ref_RejuvenateHandles (condemned, max_gen, (uintptr_t)sc); | 
|---|
| 227 | if (!IsServerHeap() || sc->thread_number == 0) | 
|---|
| 228 | GCToEEInterface::SyncBlockCacheDemote(max_gen); | 
|---|
| 229 | } | 
|---|
| 230 |  | 
|---|
| 231 | void GCScan::GcPromotionsGranted (int condemned, int max_gen, ScanContext* sc) | 
|---|
| 232 | { | 
|---|
| 233 | Ref_AgeHandles(condemned, max_gen, (uintptr_t)sc); | 
|---|
| 234 | if (!IsServerHeap() || sc->thread_number == 0) | 
|---|
| 235 | GCToEEInterface::SyncBlockCachePromotionsGranted(max_gen); | 
|---|
| 236 | } | 
|---|
| 237 |  | 
|---|
| 238 |  | 
|---|
| 239 | size_t GCScan::AskForMoreReservedMemory (size_t old_size, size_t need_size) | 
|---|
| 240 | { | 
|---|
| 241 | LIMITED_METHOD_CONTRACT; | 
|---|
| 242 |  | 
|---|
| 243 | #if !defined(FEATURE_CORECLR) && !defined(FEATURE_REDHAWK) | 
|---|
| 244 | // call the host.... | 
|---|
| 245 |  | 
|---|
| 246 | IGCHostControl *pGCHostControl = CorHost::GetGCHostControl(); | 
|---|
| 247 |  | 
|---|
| 248 | if (pGCHostControl) | 
|---|
| 249 | { | 
|---|
| 250 | size_t new_max_limit_size = need_size; | 
|---|
| 251 | pGCHostControl->RequestVirtualMemLimit (old_size, | 
|---|
| 252 | (SIZE_T*)&new_max_limit_size); | 
|---|
| 253 | return new_max_limit_size; | 
|---|
| 254 | } | 
|---|
| 255 | #endif | 
|---|
| 256 |  | 
|---|
| 257 | return old_size + need_size; | 
|---|
| 258 | } | 
|---|
| 259 |  | 
|---|
| 260 | void GCScan::VerifyHandleTable(int condemned, int max_gen, ScanContext* sc) | 
|---|
| 261 | { | 
|---|
| 262 | LIMITED_METHOD_CONTRACT; | 
|---|
| 263 | Ref_VerifyHandleTable(condemned, max_gen, sc); | 
|---|
| 264 | } | 
|---|
| 265 |  | 
|---|
| 266 | #endif // !DACCESS_COMPILE | 
|---|
| 267 |  | 
|---|