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