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 | // |
7 | // #Overview |
8 | // |
9 | // This file provides convenient wrappers for the GC stress functionality. |
10 | // |
11 | // Exposed APIs: |
12 | // GCStressPolicy::InhibitHolder |
13 | // GCStressPolicy::GlobalEnable() |
14 | // GCStressPolicy::GlobalDisable() |
15 | // GCStressPolicy::IsEnabled() |
16 | // |
17 | // GCStress<> template classes with its IsEnabled() & MaybeTrigger members. |
18 | // |
19 | // Use GCStress<> to abstract away the GC stress related decissions. The |
20 | // template definitions will resolve to nothing when STRESS_HEAP is not |
21 | // defined, and will inline the function body at the call site otherwise. |
22 | // |
23 | // Examples: |
24 | // GCStress<cfg_any>::IsEnabled() |
25 | // GCStress<cfg_any, EeconfigFastGcSPolicy, CoopGcModePolicy>::MaybeTrigger() |
26 | // |
27 | |
28 | #ifndef _GC_STRESS_ |
29 | #define _GC_STRESS_ |
30 | |
31 | #include "mpl/type_list" |
32 | |
33 | |
34 | struct alloc_context; |
35 | |
36 | |
37 | enum gcs_trigger_points { |
38 | // generic handling based on EEConfig settings |
39 | cfg_any, // any bit set in EEConfig::iGCStress |
40 | cfg_alloc, // trigger on GC allocations |
41 | cfg_transition, // trigger on transitions |
42 | cfg_instr_jit, // trigger on JITted instructions |
43 | cfg_instr_ngen, // trigger on NGENed instructions |
44 | cfg_easy, // trigger on allocs or transitions |
45 | cfg_instr, // trigger on managed instructions (JITted or NGENed) |
46 | cfg_last, // boundary |
47 | |
48 | // special handling at particular trigger points |
49 | jit_on_create_jump_stub, |
50 | jit_on_create_il_stub, |
51 | gc_on_alloc, |
52 | vsd_on_resolve |
53 | }; |
54 | |
55 | |
56 | namespace GCStressPolicy |
57 | { |
58 | |
59 | #ifdef STRESS_HEAP |
60 | |
61 | #ifdef __GNUC__ |
62 | #define UNUSED_ATTR __attribute__ ((unused)) |
63 | #else // __GNUC__ |
64 | #define UNUSED_ATTR |
65 | #endif // __GNUC__ |
66 | |
67 | #ifndef __UNUSED |
68 | #define __UNUSED(x) ((void)(x)) |
69 | #endif // __UNUSED |
70 | |
71 | class InhibitHolder |
72 | { |
73 | private: |
74 | // This static controls whether GC stress may induce GCs. EEConfig::GetGCStressLevel() still |
75 | // controls when GCs may occur. |
76 | static Volatile<DWORD> s_nGcStressDisabled; |
77 | |
78 | bool m_bAquired; |
79 | |
80 | public: |
81 | InhibitHolder() |
82 | { LIMITED_METHOD_CONTRACT; ++s_nGcStressDisabled; m_bAquired = true; } |
83 | |
84 | ~InhibitHolder() |
85 | { LIMITED_METHOD_CONTRACT; Release(); } |
86 | |
87 | void Release() |
88 | { |
89 | LIMITED_METHOD_CONTRACT; |
90 | if (m_bAquired) |
91 | { |
92 | --s_nGcStressDisabled; |
93 | m_bAquired = false; |
94 | } |
95 | } |
96 | |
97 | friend bool IsEnabled(); |
98 | friend void GlobalDisable(); |
99 | friend void GlobalEnable(); |
100 | } UNUSED_ATTR; |
101 | |
102 | FORCEINLINE bool IsEnabled() |
103 | { return InhibitHolder::s_nGcStressDisabled == 0U; } |
104 | |
105 | FORCEINLINE void GlobalDisable() |
106 | { ++InhibitHolder::s_nGcStressDisabled; } |
107 | |
108 | FORCEINLINE void GlobalEnable() |
109 | { --InhibitHolder::s_nGcStressDisabled; } |
110 | |
111 | #else // STRESS_HEAP |
112 | |
113 | class InhibitHolder |
114 | { void Release() {} }; |
115 | |
116 | FORCEINLINE bool IsEnabled() |
117 | { return false; } |
118 | |
119 | FORCEINLINE void GlobalDisable() |
120 | {} |
121 | |
122 | FORCEINLINE void GlobalEnable() |
123 | {} |
124 | |
125 | #endif // STRESS_HEAP |
126 | } |
127 | |
128 | |
129 | |
130 | namespace _GCStress |
131 | { |
132 | |
133 | #ifdef STRESS_HEAP |
134 | |
135 | // Support classes to allow easy customization of GC Stress policies |
136 | namespace detail |
137 | { |
138 | using namespace mpl; |
139 | |
140 | // Selecting a policy from a type list and a fallback/default policy |
141 | // GetPolicy<>:type will represent either a type in ListT with the same "tag" as DefPolicy |
142 | // or DefPolicy, based on the Traits passed in. |
143 | template < |
144 | typename ListT, |
145 | typename DefPolicy, |
146 | template <typename> class Traits |
147 | > |
148 | struct GetPolicy; |
149 | |
150 | // Common case: recurse over the type list |
151 | template < |
152 | typename HeadT, |
153 | typename TailT, |
154 | typename DefPolicy, |
155 | template<typename> class Traits |
156 | > |
157 | struct GetPolicy<type_list<HeadT, TailT>, DefPolicy, Traits> |
158 | { |
159 | // is true if HeadT and DefPolicy evaluate to the same tag, |
160 | // through Traits<> |
161 | static const bool sameTag = std::is_same< |
162 | typename Traits<HeadT>::tag, |
163 | typename Traits<DefPolicy>::tag |
164 | >::value; |
165 | |
166 | typedef typename std::conditional< |
167 | sameTag, |
168 | HeadT, |
169 | typename GetPolicy<TailT, DefPolicy, Traits>::type |
170 | >::type type; |
171 | }; |
172 | |
173 | // Termination case. |
174 | template < |
175 | typename DefPolicy, |
176 | template<typename> class Traits |
177 | > |
178 | struct GetPolicy <null_type, DefPolicy, Traits> |
179 | { |
180 | typedef DefPolicy type; |
181 | }; |
182 | } |
183 | |
184 | |
185 | // GC stress specific EEConfig accessors |
186 | namespace detail |
187 | { |
188 | // no definition provided so that absence of concrete implementations cause compiler errors |
189 | template <enum gcs_trigger_points> |
190 | bool IsEnabled(); |
191 | |
192 | template<> FORCEINLINE |
193 | bool IsEnabled<cfg_any>() |
194 | { |
195 | // Most correct would be to test for each specific bits, but we've |
196 | // always only tested against 0... |
197 | return g_pConfig->GetGCStressLevel() != 0; |
198 | // return (g_pConfig->GetGCStressLevel() & |
199 | // (EEConfig::GCSTRESS_ALLOC|EEConfig::GCSTRESS_TRANSITION| |
200 | // EEConfig::GCSTRESS_INSTR_JIT|EEConfig::GCSTRESS_INSTR_NGEN) != 0); |
201 | } |
202 | |
203 | #define DefineIsEnabled(cfg_enum, eeconfig_bits) \ |
204 | template<> FORCEINLINE \ |
205 | bool IsEnabled<cfg_enum>() \ |
206 | { \ |
207 | return (g_pConfig->GetGCStressLevel() & (eeconfig_bits)) != 0; \ |
208 | } |
209 | |
210 | DefineIsEnabled(cfg_alloc, EEConfig::GCSTRESS_ALLOC); |
211 | DefineIsEnabled(cfg_transition, EEConfig::GCSTRESS_TRANSITION); |
212 | DefineIsEnabled(cfg_instr_jit, EEConfig::GCSTRESS_INSTR_JIT); |
213 | DefineIsEnabled(cfg_instr_ngen, EEConfig::GCSTRESS_INSTR_NGEN); |
214 | DefineIsEnabled(cfg_easy, EEConfig::GCSTRESS_ALLOC|EEConfig::GCSTRESS_TRANSITION); |
215 | DefineIsEnabled(cfg_instr, EEConfig::GCSTRESS_INSTR_JIT|EEConfig::GCSTRESS_INSTR_NGEN); |
216 | |
217 | #undef DefineIsEnabled |
218 | |
219 | } |
220 | |
221 | |
222 | // |
223 | // GC stress policy classes used by GCSBase and GCStress template classes |
224 | // |
225 | |
226 | // Fast GS stress policies that dictate whether GCStress<>::MaybeTrigger() |
227 | // will consider g_pConfig->FastGCStressLevel() when deciding whether |
228 | // to trigger a GC or not. |
229 | |
230 | // This is the default Fast GC stress policy that ignores the EEConfig |
231 | // setting |
232 | class IgnoreFastGcSPolicy |
233 | { |
234 | public: |
235 | FORCEINLINE |
236 | static bool FastGcSEnabled(DWORD minValue = 0) |
237 | { return false; } |
238 | }; |
239 | |
240 | // This is the overriding Fast GC stress policy that considers the |
241 | // EEConfig setting on checked/debug builds |
242 | class EeconfigFastGcSPolicy |
243 | { |
244 | public: |
245 | FORCEINLINE |
246 | static bool FastGcSEnabled(DWORD minValue = 0) |
247 | { |
248 | #ifdef _DEBUG |
249 | return g_pConfig->FastGCStressLevel() > minValue; |
250 | #else // _DEBUG |
251 | return false; |
252 | #endif // _DEBUG |
253 | } |
254 | }; |
255 | |
256 | // GC Mode policies that determines whether to switch the GC mode before |
257 | // triggering the GC. |
258 | |
259 | // This is the default GC Mode stress policy that does not switch GC modes |
260 | class AnyGcModePolicy |
261 | { |
262 | }; |
263 | |
264 | // This is the overriding GC Mode stress policy that forces a switch to |
265 | // cooperative mode before MaybeTrigger() will trigger a GC |
266 | class CoopGcModePolicy |
267 | { |
268 | #ifndef DACCESS_COMPILE |
269 | // implicit constructor an destructor will do the right thing |
270 | GCCoop m_coop; |
271 | #endif // DACCESS_COMPILE |
272 | |
273 | public: |
274 | FORCEINLINE CoopGcModePolicy() |
275 | { WRAPPER_NO_CONTRACT; } |
276 | FORCEINLINE ~CoopGcModePolicy() |
277 | { WRAPPER_NO_CONTRACT; } |
278 | } UNUSED_ATTR; |
279 | |
280 | // GC Trigger policy classes define how a garbage collection is triggered |
281 | |
282 | // This is the default GC Trigger policy that simply calls |
283 | // IGCHeap::StressHeap |
284 | class StressGcTriggerPolicy |
285 | { |
286 | public: |
287 | FORCEINLINE |
288 | static void Trigger() |
289 | { |
290 | // BUG(github #10318) - when not using allocation contexts, the alloc lock |
291 | // must be acquired here. Until fixed, this assert prevents random heap corruption. |
292 | _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts()); |
293 | GCHeapUtilities::GetGCHeap()->StressHeap(GetThread()->GetAllocContext()); |
294 | } |
295 | |
296 | FORCEINLINE |
297 | static void Trigger(::gc_alloc_context* acontext) |
298 | { GCHeapUtilities::GetGCHeap()->StressHeap(acontext); } |
299 | }; |
300 | |
301 | // This is an overriding GC Trigger policy that triggers a GC by calling |
302 | // PulseGCMode |
303 | class PulseGcTriggerPolicy |
304 | { |
305 | public: |
306 | FORCEINLINE |
307 | static void Trigger() |
308 | { |
309 | DEBUG_ONLY_REGION(); |
310 | GetThread()->PulseGCMode(); |
311 | } |
312 | }; |
313 | |
314 | |
315 | // GC stress policy tags |
316 | struct fast_gcs_policy_tag {}; |
317 | struct gc_mode_policy_tag {}; |
318 | struct gc_trigger_policy_tag {}; |
319 | |
320 | |
321 | template <class GCSPolicy> |
322 | struct GcStressTraits |
323 | { typedef mpl::null_type tag; }; |
324 | |
325 | #define DefineGCStressTraits(Policy, policy_tag) \ |
326 | template <> struct GcStressTraits<Policy> \ |
327 | { typedef policy_tag tag; } |
328 | |
329 | DefineGCStressTraits(IgnoreFastGcSPolicy, fast_gcs_policy_tag); |
330 | DefineGCStressTraits(EeconfigFastGcSPolicy, fast_gcs_policy_tag); |
331 | |
332 | DefineGCStressTraits(AnyGcModePolicy, gc_mode_policy_tag); |
333 | DefineGCStressTraits(CoopGcModePolicy, gc_mode_policy_tag); |
334 | |
335 | DefineGCStressTraits(StressGcTriggerPolicy, gc_trigger_policy_tag); |
336 | DefineGCStressTraits(PulseGcTriggerPolicy, gc_trigger_policy_tag); |
337 | |
338 | #undef DefineGCStressTraits |
339 | |
340 | // Special handling for GC stress policies |
341 | template <class GCPolicies, class DefPolicy> |
342 | struct GetPolicy: |
343 | public detail::GetPolicy<GCPolicies, DefPolicy, GcStressTraits> |
344 | {}; |
345 | |
346 | |
347 | // |
348 | // The base for any customization GCStress class. It accepts an identifying |
349 | // GC stress trigger point and at most three overriding policies. |
350 | // |
351 | // It defines FastGcSPolicy, GcModePolicy, and GcTriggerPolicy as either |
352 | // the overriding policy or the default policy, if no corresponding |
353 | // overriding policy is specified in the list. These names can then be |
354 | // accessed from the derived GCStress class. |
355 | // |
356 | // Additionally it defines the static methods IsEnabled and MaybeTrigger and |
357 | // how the policy classes influence their behavior. |
358 | // |
359 | template < |
360 | enum gcs_trigger_points tp, |
361 | class Policy1 = mpl::null_type, |
362 | class Policy2 = mpl::null_type, |
363 | class Policy3 = mpl::null_type |
364 | > |
365 | class GCSBase |
366 | { |
367 | public: |
368 | typedef typename mpl::make_type_list<Policy1, Policy2, Policy3>::type Policies; |
369 | |
370 | typedef typename GetPolicy<Policies, IgnoreFastGcSPolicy>::type FastGcSPolicy; |
371 | typedef typename GetPolicy<Policies, AnyGcModePolicy>::type GcModePolicy; |
372 | typedef typename GetPolicy<Policies, StressGcTriggerPolicy>::type GcTriggerPolicy; |
373 | |
374 | typedef GCSBase<tp, FastGcSPolicy, GcModePolicy, GcTriggerPolicy> GcStressBase; |
375 | |
376 | // Returns true iff: |
377 | // . the bitflag in EEConfig::GetStressLevel() corresponding to the |
378 | // gc stress trigger point is set AND |
379 | // . when a Fast GC Stress policy is specified, if the minFastGC argument |
380 | // is below the EEConfig::FastGCStressLevel |
381 | FORCEINLINE |
382 | static bool IsEnabled(DWORD minFastGc = 0) |
383 | { |
384 | static_assert(tp < cfg_last, "GCSBase only supports cfg_ trigger points." ); |
385 | return detail::IsEnabled<tp>() && !FastGcSPolicy::FastGcSEnabled(minFastGc); |
386 | } |
387 | |
388 | // Triggers a GC iff |
389 | // . the GC stress is not disabled globally (thru GCStressPolicy::GlobalDisable) |
390 | // AND |
391 | // . IsEnabled() returns true. |
392 | // Additionally it switches the GC mode as specified by GcModePolicy, and it |
393 | // uses GcTriggerPolicy::Trigger() to actually trigger the GC |
394 | FORCEINLINE |
395 | static void MaybeTrigger(DWORD minFastGc = 0) |
396 | { |
397 | if (IsEnabled(minFastGc) && GCStressPolicy::IsEnabled()) |
398 | { |
399 | GcModePolicy gcModeObj; __UNUSED(gcModeObj); |
400 | GcTriggerPolicy::Trigger(); |
401 | } |
402 | } |
403 | |
404 | // Triggers a GC iff |
405 | // . the GC stress is not disabled globally (thru GCStressPolicy::GlobalDisable) |
406 | // AND |
407 | // . IsEnabled() returns true. |
408 | // Additionally it switches the GC mode as specified by GcModePolicy, and it |
409 | // uses GcTriggerPolicy::Trigger(alloc_context*) to actually trigger the GC |
410 | FORCEINLINE |
411 | static void MaybeTrigger(::gc_alloc_context* acontext, DWORD minFastGc = 0) |
412 | { |
413 | if (IsEnabled(minFastGc) && GCStressPolicy::IsEnabled()) |
414 | { |
415 | GcModePolicy gcModeObj; __UNUSED(gcModeObj); |
416 | GcTriggerPolicy::Trigger(acontext); |
417 | } |
418 | } |
419 | }; |
420 | |
421 | template < |
422 | enum gcs_trigger_points tp, |
423 | class Policy1 = mpl::null_type, |
424 | class Policy2 = mpl::null_type, |
425 | class Policy3 = mpl::null_type |
426 | > |
427 | class GCStress |
428 | : public GCSBase<tp, Policy1, Policy2, Policy3> |
429 | { |
430 | }; |
431 | |
432 | |
433 | // |
434 | // Partial specializations of GCStress for trigger points requiring non-default |
435 | // handling. |
436 | // |
437 | |
438 | template <> |
439 | class GCStress<jit_on_create_jump_stub> |
440 | : public GCSBase<cfg_any> |
441 | { |
442 | public: |
443 | |
444 | FORCEINLINE |
445 | static void MaybeTrigger(DWORD minFastGc = 0) |
446 | { |
447 | if ((GetThreadNULLOk() != NULL) && (GetThreadNULLOk()->PreemptiveGCDisabled())) |
448 | { |
449 | // Force a GC if the stress level is high enough |
450 | GcStressBase::MaybeTrigger(minFastGc); |
451 | } |
452 | } |
453 | |
454 | }; |
455 | |
456 | template <> |
457 | class GCStress<gc_on_alloc> |
458 | : public GCSBase<cfg_alloc> |
459 | { |
460 | public: |
461 | |
462 | FORCEINLINE |
463 | static void MaybeTrigger(::gc_alloc_context* acontext) |
464 | { |
465 | GcStressBase::MaybeTrigger(acontext); |
466 | |
467 | #ifdef _DEBUG |
468 | Thread *pThread = GetThread(); |
469 | if (pThread) |
470 | { |
471 | pThread->EnableStressHeap(); |
472 | } |
473 | #endif //_DEBUG |
474 | } |
475 | }; |
476 | |
477 | template <> |
478 | class GCStress<vsd_on_resolve> |
479 | : public GCSBase<cfg_any> |
480 | { |
481 | public: |
482 | |
483 | // Triggers a GC iff |
484 | // . the GC stress is not disabled globally (thru GCStressPolicy::GlobalDisable) |
485 | // AND |
486 | // . IsEnabled() returns true. |
487 | // Additionally it protects the passed in OBJECTREF&, and it uses |
488 | // GcTriggerPolicy::Trigger() to actually trigger the GC |
489 | // |
490 | // Note: the OBJECTREF must be passed by reference so MaybeTrigger can protect |
491 | // the calling function's stack slot. |
492 | FORCEINLINE |
493 | static void MaybeTriggerAndProtect(OBJECTREF& objref) |
494 | { |
495 | if (GcStressBase::IsEnabled() && GCStressPolicy::IsEnabled()) |
496 | { |
497 | GCPROTECT_BEGIN(objref); |
498 | GcTriggerPolicy::Trigger(); |
499 | GCPROTECT_END(); |
500 | } |
501 | } |
502 | }; |
503 | |
504 | #else // STRESS_HEAP |
505 | |
506 | class IgnoreFastGcSPolicy {}; |
507 | class EeconfigFastGcSPolicy {}; |
508 | class AnyGcModePolicy {}; |
509 | class CoopGcModePolicy {}; |
510 | class StressGcTriggerPolicy {}; |
511 | class PulseGcTriggerPolicy {}; |
512 | |
513 | // Everything here should resolve to inlined empty blocks or "false" |
514 | template < |
515 | enum gcs_trigger_points tp, |
516 | class Policy1 = mpl::null_type, |
517 | class Policy2 = mpl::null_type, |
518 | class Policy3 = mpl::null_type |
519 | > |
520 | class GCStress |
521 | { |
522 | public: |
523 | FORCEINLINE |
524 | static bool IsEnabled(DWORD minFastGc = 0) |
525 | { |
526 | static_assert(tp < cfg_last, "GCStress::IsEnabled only supports cfg_ trigger points." ); |
527 | return false; |
528 | } |
529 | |
530 | FORCEINLINE |
531 | static void MaybeTrigger(DWORD minFastGc = 0) |
532 | {} |
533 | |
534 | FORCEINLINE |
535 | static void MaybeTrigger(::alloc_context* acontext, DWORD minFastGc = 0) |
536 | {} |
537 | |
538 | template<typename T> |
539 | FORCEINLINE |
540 | static void MaybeTrigger(T arg) |
541 | {} |
542 | }; |
543 | |
544 | #endif // STRESS_HEAP |
545 | |
546 | } |
547 | |
548 | using _GCStress::IgnoreFastGcSPolicy; |
549 | using _GCStress::EeconfigFastGcSPolicy; |
550 | using _GCStress::AnyGcModePolicy; |
551 | using _GCStress::CoopGcModePolicy; |
552 | using _GCStress::StressGcTriggerPolicy; |
553 | using _GCStress::PulseGcTriggerPolicy; |
554 | |
555 | using _GCStress::GCStress; |
556 | |
557 | |
558 | |
559 | #endif |
560 | |