diff options
Diffstat (limited to 'src/vm/gcstress.h')
-rw-r--r-- | src/vm/gcstress.h | 554 |
1 files changed, 554 insertions, 0 deletions
diff --git a/src/vm/gcstress.h b/src/vm/gcstress.h new file mode 100644 index 0000000000..609276e148 --- /dev/null +++ b/src/vm/gcstress.h @@ -0,0 +1,554 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +// +// #Overview +// +// This file provides convenient wrappers for the GC stress functionality. +// +// Exposed APIs: +// GCStressPolicy::InhibitHolder +// GCStressPolicy::GlobalEnable() +// GCStressPolicy::GlobalDisable() +// GCStressPolicy::IsEnabled() +// +// GCStress<> template classes with its IsEnabled() & MaybeTrigger members. +// +// Use GCStress<> to abstract away the GC stress related decissions. The +// template definitions will resolve to nothing when STRESS_HEAP is not +// defined, and will inline the function body at the call site otherwise. +// +// Examples: +// GCStress<cfg_any>::IsEnabled() +// GCStress<cfg_any, EeconfigFastGcSPolicy, CoopGcModePolicy>::MaybeTrigger() +// + +#ifndef _GC_STRESS_ +#define _GC_STRESS_ + +#include "mpl/type_list" + + +struct alloc_context; + + +enum gcs_trigger_points { + // generic handling based on EEConfig settings + cfg_any, // any bit set in EEConfig::iGCStress + cfg_alloc, // trigger on GC allocations + cfg_transition, // trigger on transitions + cfg_instr_jit, // trigger on JITted instructions + cfg_instr_ngen, // trigger on NGENed instructions + cfg_easy, // trigger on allocs or transitions + cfg_instr, // trigger on managed instructions (JITted or NGENed) + cfg_last, // boundary + + // special handling at particular trigger points + jit_on_create_jump_stub, + jit_on_create_il_stub, + gc_on_alloc, + vsd_on_resolve +}; + + +namespace GCStressPolicy +{ + +#ifdef STRESS_HEAP + +#ifdef __GNUC__ +#define UNUSED_ATTR __attribute__ ((unused)) +#else // __GNUC__ +#define UNUSED_ATTR +#endif // __GNUC__ + +#ifndef __UNUSED +#define __UNUSED(x) ((void)(x)) +#endif // __UNUSED + + class InhibitHolder + { + private: + // This static controls whether GC stress may induce GCs. EEConfig::GetGCStressLevel() still + // controls when GCs may occur. + static Volatile<DWORD> s_nGcStressDisabled; + + bool m_bAquired; + + public: + InhibitHolder() + { LIMITED_METHOD_CONTRACT; ++s_nGcStressDisabled; m_bAquired = true; } + + ~InhibitHolder() + { LIMITED_METHOD_CONTRACT; Release(); } + + void Release() + { + LIMITED_METHOD_CONTRACT; + if (m_bAquired) + { + --s_nGcStressDisabled; + m_bAquired = false; + } + } + + friend bool IsEnabled(); + friend void GlobalDisable(); + friend void GlobalEnable(); + } UNUSED_ATTR; + + FORCEINLINE bool IsEnabled() + { return InhibitHolder::s_nGcStressDisabled == 0U; } + + FORCEINLINE void GlobalDisable() + { ++InhibitHolder::s_nGcStressDisabled; } + + FORCEINLINE void GlobalEnable() + { --InhibitHolder::s_nGcStressDisabled; } + +#else // STRESS_HEAP + + class InhibitHolder + { void Release() {} }; + + FORCEINLINE bool IsEnabled() + { return false; } + + FORCEINLINE void GlobalDisable() + {} + + FORCEINLINE void GlobalEnable() + {} + +#endif // STRESS_HEAP +} + + + +namespace _GCStress +{ + +#ifdef STRESS_HEAP + + // Support classes to allow easy customization of GC Stress policies + namespace detail + { + using namespace mpl; + + // Selecting a policy from a type list and a fallback/default policy + // GetPolicy<>:type will represent either a type in ListT with the same "tag" as DefPolicy + // or DefPolicy, based on the Traits passed in. + template < + typename ListT, + typename DefPolicy, + template <typename> class Traits + > + struct GetPolicy; + + // Common case: recurse over the type list + template < + typename HeadT, + typename TailT, + typename DefPolicy, + template<typename> class Traits + > + struct GetPolicy<type_list<HeadT, TailT>, DefPolicy, Traits> + { + // is true if HeadT and DefPolicy evaluate to the same tag, + // through Traits<> + static const bool sameTag = std::is_same< + typename Traits<HeadT>::tag, + typename Traits<DefPolicy>::tag + >::value; + + typedef typename std::conditional< + sameTag, + HeadT, + typename GetPolicy<TailT, DefPolicy, Traits>::type + >::type type; + }; + + // Termination case. + template < + typename DefPolicy, + template<typename> class Traits + > + struct GetPolicy <null_type, DefPolicy, Traits> + { + typedef DefPolicy type; + }; + } + + + // GC stress specific EEConfig accessors + namespace detail + { + // no definition provided so that absence of concrete implementations cause compiler errors + template <enum gcs_trigger_points> + bool IsEnabled(); + + template<> FORCEINLINE + bool IsEnabled<cfg_any>() + { + // Most correct would be to test for each specific bits, but we've + // always only tested against 0... + return g_pConfig->GetGCStressLevel() != 0; + // return (g_pConfig->GetGCStressLevel() & + // (EEConfig::GCSTRESS_ALLOC|EEConfig::GCSTRESS_TRANSITION| + // EEConfig::GCSTRESS_INSTR_JIT|EEConfig::GCSTRESS_INSTR_NGEN) != 0); + } + + #define DefineIsEnabled(cfg_enum, eeconfig_bits) \ + template<> FORCEINLINE \ + bool IsEnabled<cfg_enum>() \ + { \ + return (g_pConfig->GetGCStressLevel() & (eeconfig_bits)) != 0; \ + } + + DefineIsEnabled(cfg_alloc, EEConfig::GCSTRESS_ALLOC); + DefineIsEnabled(cfg_transition, EEConfig::GCSTRESS_TRANSITION); + DefineIsEnabled(cfg_instr_jit, EEConfig::GCSTRESS_INSTR_JIT); + DefineIsEnabled(cfg_instr_ngen, EEConfig::GCSTRESS_INSTR_NGEN); + DefineIsEnabled(cfg_easy, EEConfig::GCSTRESS_ALLOC|EEConfig::GCSTRESS_TRANSITION); + DefineIsEnabled(cfg_instr, EEConfig::GCSTRESS_INSTR_JIT|EEConfig::GCSTRESS_INSTR_NGEN); + + #undef DefineIsEnabled + + } + + + // + // GC stress policy classes used by GCSBase and GCStress template classes + // + + // Fast GS stress policies that dictate whether GCStress<>::MaybeTrigger() + // will consider g_pConfig->FastGCStressLevel() when deciding whether + // to trigger a GC or not. + + // This is the default Fast GC stress policy that ignores the EEConfig + // setting + class IgnoreFastGcSPolicy + { + public: + FORCEINLINE + static bool FastGcSEnabled(DWORD minValue = 0) + { return false; } + }; + + // This is the overriding Fast GC stress policy that considers the + // EEConfig setting on checked/debug builds + class EeconfigFastGcSPolicy + { + public: + FORCEINLINE + static bool FastGcSEnabled(DWORD minValue = 0) + { + #ifdef _DEBUG + return g_pConfig->FastGCStressLevel() > minValue; + #else // _DEBUG + return false; + #endif // _DEBUG + } + }; + + // GC Mode policies that determines whether to switch the GC mode before + // triggering the GC. + + // This is the default GC Mode stress policy that does not switch GC modes + class AnyGcModePolicy + { + }; + + // This is the overriding GC Mode stress policy that forces a switch to + // cooperative mode before MaybeTrigger() will trigger a GC + class CoopGcModePolicy + { +#ifndef DACCESS_COMPILE + // implicit constructor an destructor will do the right thing + GCCoop m_coop; +#endif // DACCESS_COMPILE + + public: + FORCEINLINE CoopGcModePolicy() + { WRAPPER_NO_CONTRACT; } + FORCEINLINE ~CoopGcModePolicy() + { WRAPPER_NO_CONTRACT; } + } UNUSED_ATTR; + + // GC Trigger policy classes define how a garbage collection is triggered + + // This is the default GC Trigger policy that simply calls + // GCHeap::StressHeap + class StressGcTriggerPolicy + { + public: + FORCEINLINE + static void Trigger() + { GCHeap::GetGCHeap()->StressHeap(); } + + FORCEINLINE + static void Trigger(::alloc_context* acontext) + { GCHeap::GetGCHeap()->StressHeap(acontext); } + }; + + // This is an overriding GC Trigger policy that triggers a GC by calling + // PulseGCMode + class PulseGcTriggerPolicy + { + public: + FORCEINLINE + static void Trigger() + { + DEBUG_ONLY_REGION(); + GetThread()->PulseGCMode(); + } + }; + + + // GC stress policy tags + struct fast_gcs_policy_tag {}; + struct gc_mode_policy_tag {}; + struct gc_trigger_policy_tag {}; + + + template <class GCSPolicy> + struct GcStressTraits + { typedef mpl::null_type tag; }; + + #define DefineGCStressTraits(Policy, policy_tag) \ + template <> struct GcStressTraits<Policy> \ + { typedef policy_tag tag; } + + DefineGCStressTraits(IgnoreFastGcSPolicy, fast_gcs_policy_tag); + DefineGCStressTraits(EeconfigFastGcSPolicy, fast_gcs_policy_tag); + + DefineGCStressTraits(AnyGcModePolicy, gc_mode_policy_tag); + DefineGCStressTraits(CoopGcModePolicy, gc_mode_policy_tag); + + DefineGCStressTraits(StressGcTriggerPolicy, gc_trigger_policy_tag); + DefineGCStressTraits(PulseGcTriggerPolicy, gc_trigger_policy_tag); + + #undef DefineGCStressTraits + + // Special handling for GC stress policies + template <class GCPolicies, class DefPolicy> + struct GetPolicy: + public detail::GetPolicy<GCPolicies, DefPolicy, GcStressTraits> + {}; + + + // + // The base for any customization GCStress class. It accepts an identifying + // GC stress trigger point and at most three overriding policies. + // + // It defines FastGcSPolicy, GcModePolicy, and GcTriggerPolicy as either + // the overriding policy or the default policy, if no corresponding + // overriding policy is specified in the list. These names can then be + // accessed from the derived GCStress class. + // + // Additionally it defines the static methods IsEnabled and MaybeTrigger and + // how the policy classes influence their behavior. + // + template < + enum gcs_trigger_points tp, + class Policy1 = mpl::null_type, + class Policy2 = mpl::null_type, + class Policy3 = mpl::null_type + > + class GCSBase + { + public: + typedef typename mpl::make_type_list<Policy1, Policy2, Policy3>::type Policies; + + typedef typename GetPolicy<Policies, IgnoreFastGcSPolicy>::type FastGcSPolicy; + typedef typename GetPolicy<Policies, AnyGcModePolicy>::type GcModePolicy; + typedef typename GetPolicy<Policies, StressGcTriggerPolicy>::type GcTriggerPolicy; + + typedef GCSBase<tp, FastGcSPolicy, GcModePolicy, GcTriggerPolicy> GcStressBase; + + // Returns true iff: + // . the bitflag in EEConfig::GetStressLevel() corresponding to the + // gc stress trigger point is set AND + // . when a Fast GC Stress policy is specified, if the minFastGC argument + // is below the EEConfig::FastGCStressLevel + FORCEINLINE + static bool IsEnabled(DWORD minFastGc = 0) + { + static_assert(tp < cfg_last, "GCSBase only supports cfg_ trigger points."); + return detail::IsEnabled<tp>() && !FastGcSPolicy::FastGcSEnabled(minFastGc); + } + + // Triggers a GC iff + // . the GC stress is not disabled globally (thru GCStressPolicy::GlobalDisable) + // AND + // . IsEnabled() returns true. + // Additionally it switches the GC mode as specified by GcModePolicy, and it + // uses GcTriggerPolicy::Trigger() to actually trigger the GC + FORCEINLINE + static void MaybeTrigger(DWORD minFastGc = 0) + { + if (IsEnabled(minFastGc) && GCStressPolicy::IsEnabled()) + { + GcModePolicy gcModeObj; __UNUSED(gcModeObj); + GcTriggerPolicy::Trigger(); + } + } + + // Triggers a GC iff + // . the GC stress is not disabled globally (thru GCStressPolicy::GlobalDisable) + // AND + // . IsEnabled() returns true. + // Additionally it switches the GC mode as specified by GcModePolicy, and it + // uses GcTriggerPolicy::Trigger(alloc_context*) to actually trigger the GC + FORCEINLINE + static void MaybeTrigger(::alloc_context* acontext, DWORD minFastGc = 0) + { + if (IsEnabled(minFastGc) && GCStressPolicy::IsEnabled()) + { + GcModePolicy gcModeObj; __UNUSED(gcModeObj); + GcTriggerPolicy::Trigger(acontext); + } + } + }; + + template < + enum gcs_trigger_points tp, + class Policy1 = mpl::null_type, + class Policy2 = mpl::null_type, + class Policy3 = mpl::null_type + > + class GCStress + : public GCSBase<tp, Policy1, Policy2, Policy3> + { + }; + + + // + // Partial specializations of GCStress for trigger points requiring non-default + // handling. + // + + template <> + class GCStress<jit_on_create_jump_stub> + : public GCSBase<cfg_any> + { + public: + + FORCEINLINE + static void MaybeTrigger(DWORD minFastGc = 0) + { + if ((GetThreadNULLOk() != NULL) && (GetThreadNULLOk()->PreemptiveGCDisabled())) + { + // Force a GC if the stress level is high enough + GcStressBase::MaybeTrigger(minFastGc); + } + } + + }; + + template <> + class GCStress<gc_on_alloc> + : public GCSBase<cfg_alloc> + { + public: + + FORCEINLINE + static void MaybeTrigger(::alloc_context* acontext) + { + GcStressBase::MaybeTrigger(acontext); + +#ifdef _DEBUG + Thread *pThread = GetThread(); + if (pThread) + { + pThread->EnableStressHeap(); + } +#endif //_DEBUG + } + }; + + template <> + class GCStress<vsd_on_resolve> + : public GCSBase<cfg_any> + { + public: + + // Triggers a GC iff + // . the GC stress is not disabled globally (thru GCStressPolicy::GlobalDisable) + // AND + // . IsEnabled() returns true. + // Additionally it protects the passed in OBJECTREF&, and it uses + // GcTriggerPolicy::Trigger() to actually trigger the GC + // + // Note: the OBJECTREF must be passed by reference so MaybeTrigger can protect + // the calling function's stack slot. + FORCEINLINE + static void MaybeTriggerAndProtect(OBJECTREF& objref) + { + if (GcStressBase::IsEnabled() && GCStressPolicy::IsEnabled()) + { + GCPROTECT_BEGIN(objref); + GcTriggerPolicy::Trigger(); + GCPROTECT_END(); + } + } + }; + +#else // STRESS_HEAP + + class IgnoreFastGcSPolicy {}; + class EeconfigFastGcSPolicy {}; + class AnyGcModePolicy {}; + class CoopGcModePolicy {}; + class StressGcTriggerPolicy {}; + class PulseGcTriggerPolicy {}; + + // Everything here should resolve to inlined empty blocks or "false" + template < + enum gcs_trigger_points tp, + class Policy1 = mpl::null_type, + class Policy2 = mpl::null_type, + class Policy3 = mpl::null_type + > + class GCStress + { + public: + FORCEINLINE + static bool IsEnabled(DWORD minFastGc = 0) + { + static_assert(tp < cfg_last, "GCStress::IsEnabled only supports cfg_ trigger points."); + return false; + } + + FORCEINLINE + static void MaybeTrigger(DWORD minFastGc = 0) + {} + + FORCEINLINE + static void MaybeTrigger(::alloc_context* acontext, DWORD minFastGc = 0) + {} + + template<typename T> + FORCEINLINE + static void MaybeTrigger(T arg) + {} + }; + +#endif // STRESS_HEAP + +} + +using _GCStress::IgnoreFastGcSPolicy; +using _GCStress::EeconfigFastGcSPolicy; +using _GCStress::AnyGcModePolicy; +using _GCStress::CoopGcModePolicy; +using _GCStress::StressGcTriggerPolicy; +using _GCStress::PulseGcTriggerPolicy; + +using _GCStress::GCStress; + + + +#endif |