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