diff options
Diffstat (limited to 'src/inc/gcinfodecoder.h')
-rw-r--r-- | src/inc/gcinfodecoder.h | 661 |
1 files changed, 661 insertions, 0 deletions
diff --git a/src/inc/gcinfodecoder.h b/src/inc/gcinfodecoder.h new file mode 100644 index 0000000000..c77c3598b0 --- /dev/null +++ b/src/inc/gcinfodecoder.h @@ -0,0 +1,661 @@ +// 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. + +/***************************************************************** + * + * GC Information Decoding API + * + *****************************************************************/ + +#ifndef _GC_INFO_DECODER_ +#define _GC_INFO_DECODER_ + +#include "gcinfotypes.h" + +#define _max(a, b) (((a) > (b)) ? (a) : (b)) +#define _min(a, b) (((a) < (b)) ? (a) : (b)) + +#ifndef GCINFODECODER_NO_EE + +#include "eetwain.h" + +#else // GCINFODECODER_NO_EE + +// Stuff from cgencpu.h: + +#ifndef __cgencpu_h__ + +inline void SetIP(T_CONTEXT* context, PCODE rip) +{ + _ASSERTE(!"don't call this"); +} + +inline TADDR GetSP(T_CONTEXT* context) +{ +#ifdef _TARGET_AMD64_ + return (TADDR)context->Rsp; +#elif defined(_TARGET_ARM_) + return (TADDR)context->Sp; +#elif defined(_TARGET_ARM64_) + return (TADDR)context->Sp; +#else + _ASSERTE(!"nyi for platform"); +#endif +} + +inline PCODE GetIP(T_CONTEXT* context) +{ +#ifdef _TARGET_AMD64_ + return (PCODE) context->Rip; +#elif defined(_TARGET_ARM_) + return (PCODE)context->Pc; +#elif defined(_TARGET_ARM64_) + return (PCODE)context->Pc; +#else + _ASSERTE(!"nyi for platform"); +#endif +} + +#endif // !__cgencpu_h__ + +// Misc. VM types: + +#ifndef DEFINE_OBJECTREF +#define DEFINE_OBJECTREF +class Object; +typedef Object *OBJECTREF; +#endif +typedef SIZE_T TADDR; + +// Stuff from gc.h: + +#ifndef __GC_H + +#define GC_CALL_INTERIOR 0x1 +#define GC_CALL_PINNED 0x2 + +#endif // !__GC_H + +// Stuff from stdmacros.h (can't include because it includes contract.h, which uses #pragma once) + +#ifndef _stdmacros_h_ + +inline BOOL IS_ALIGNED( size_t val, size_t alignment ) +{ + // alignment must be a power of 2 for this implementation to work (need modulo otherwise) + _ASSERTE( 0 == (alignment & (alignment - 1)) ); + return 0 == (val & (alignment - 1)); +} +inline BOOL IS_ALIGNED( void* val, size_t alignment ) +{ + return IS_ALIGNED( (size_t) val, alignment ); +} + +#define FMT_REG "r%d " +#define FMT_STK "sp%s0x%02x " + +#define DBG_STK(off) \ + (off >= 0) ? "+" : "-", \ + (off >= 0) ? off : -off + +#endif + +// Stuff from check.h: + +#ifndef UNREACHABLE +#define UNREACHABLE() __assume(0) +#endif + +// Stuff from eetwain.h: + +#ifndef _EETWAIN_H + +typedef void (*GCEnumCallback)( + LPVOID hCallback, // callback data + OBJECTREF* pObject, // address of obect-reference we are reporting + uint32_t flags // is this a pinned and/or interior pointer +); + +#ifndef _strike_h + +enum ICodeManagerFlags +{ + ActiveStackFrame = 0x0001, // this is the currently active function + ExecutionAborted = 0x0002, // execution of this function has been aborted + // (i.e. it will not continue execution at the + // current location) + ParentOfFuncletStackFrame + = 0x0040, // A funclet for this frame was previously reported + + NoReportUntracked + = 0x0080, // EnumGCRefs/EnumerateLiveSlots should *not* include + // any untracked slots +}; + +#endif // !_strike_h + +#if !defined(_TARGET_X86_) +#define USE_GC_INFO_DECODER +#endif + +#include "regdisp.h" + +#endif // !_EETWAIN_H + +#endif // GCINFODECODER_NO_EE + +#include "gcinfotypes.h" + +#ifdef _DEBUG + #define MAX_PREDECODED_SLOTS 4 +#else + #define MAX_PREDECODED_SLOTS 64 +#endif + +#if defined(FEATURE_PAL) && !defined(STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY) +#define STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY +#endif + + + +enum GcInfoDecoderFlags +{ + DECODE_EVERYTHING = 0x0, + DECODE_SECURITY_OBJECT = 0x01, // stack location of security object + DECODE_CODE_LENGTH = 0x02, + DECODE_VARARG = 0x04, + DECODE_INTERRUPTIBILITY = 0x08, + DECODE_GC_LIFETIMES = 0x10, + DECODE_NO_VALIDATION = 0x20, + DECODE_PSP_SYM = 0x40, + DECODE_GENERICS_INST_CONTEXT = 0x80, // stack location of instantiation context for generics + // (this may be either the 'this' ptr or the instantiation secret param) + DECODE_GS_COOKIE = 0x100, // stack location of the GS cookie + DECODE_FOR_RANGES_CALLBACK = 0x200, + DECODE_PROLOG_LENGTH = 0x400, // length of the prolog (used to avoid reporting generics context) + DECODE_EDIT_AND_CONTINUE = 0x800, + DECODE_REVERSE_PINVOKE_VAR = 0x1000, + DECODE_RETURN_KIND = 0x2000 +}; + +enum GcInfoHeaderFlags +{ + GC_INFO_IS_VARARG = 0x1, + GC_INFO_HAS_SECURITY_OBJECT = 0x2, + GC_INFO_HAS_GS_COOKIE = 0x4, + GC_INFO_HAS_PSP_SYM = 0x8, + GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK = 0x30, + GC_INFO_HAS_GENERICS_INST_CONTEXT_NONE = 0x00, + GC_INFO_HAS_GENERICS_INST_CONTEXT_MT = 0x10, + GC_INFO_HAS_GENERICS_INST_CONTEXT_MD = 0x20, + GC_INFO_HAS_GENERICS_INST_CONTEXT_THIS = 0x30, + GC_INFO_HAS_STACK_BASE_REGISTER = 0x40, + GC_INFO_WANTS_REPORT_ONLY_LEAF = 0x80, + GC_INFO_HAS_EDIT_AND_CONTINUE_PRESERVED_SLOTS = 0x100, + GC_INFO_REVERSE_PINVOKE_FRAME = 0x200, + + GC_INFO_FLAGS_BIT_SIZE_VERSION_1 = 9, + GC_INFO_FLAGS_BIT_SIZE = 10, +}; + +class BitStreamReader +{ +public: + BitStreamReader() + { + SUPPORTS_DAC; + } + + BitStreamReader( PTR_CBYTE pBuffer ) + { + SUPPORTS_DAC; + + _ASSERTE( pBuffer != NULL ); + + m_pCurrent = m_pBuffer = dac_cast<PTR_size_t>((size_t)dac_cast<TADDR>(pBuffer) & ~((size_t)sizeof(size_t)-1)); + m_RelPos = m_InitialRelPos = (int)((size_t)dac_cast<TADDR>(pBuffer) % sizeof(size_t)) * 8; + } + + BitStreamReader(const BitStreamReader& other) + { + SUPPORTS_DAC; + + m_pBuffer = other.m_pBuffer; + m_InitialRelPos = other.m_InitialRelPos; + m_pCurrent = other.m_pCurrent; + m_RelPos = other.m_RelPos; + } + + const BitStreamReader& operator=(const BitStreamReader& other) + { + SUPPORTS_DAC; + + m_pBuffer = other.m_pBuffer; + m_InitialRelPos = other.m_InitialRelPos; + m_pCurrent = other.m_pCurrent; + m_RelPos = other.m_RelPos; + return *this; + } + + // NOTE: This routine is perf-critical + __forceinline size_t Read( int numBits ) + { + SUPPORTS_DAC; + + _ASSERTE(numBits > 0 && numBits <= BITS_PER_SIZE_T); + + size_t result = (*m_pCurrent) >> m_RelPos; + int newRelPos = m_RelPos + numBits; + if(newRelPos >= BITS_PER_SIZE_T) + { + m_pCurrent++; + newRelPos -= BITS_PER_SIZE_T; + if(newRelPos > 0) + { + size_t extraBits = (*m_pCurrent) << (numBits - newRelPos); + result ^= extraBits; + } + } + m_RelPos = newRelPos; + result &= SAFE_SHIFT_LEFT(1, numBits) - 1; + return result; + } + + // This version reads one bit, returning zero/non-zero (not 0/1) + // NOTE: This routine is perf-critical + __forceinline size_t ReadOneFast() + { + SUPPORTS_DAC; + + size_t result = (*m_pCurrent) & (((size_t)1) << m_RelPos); + if(++m_RelPos == BITS_PER_SIZE_T) + { + m_pCurrent++; + m_RelPos = 0; + } + return result; + } + + + __forceinline size_t GetCurrentPos() + { + SUPPORTS_DAC; + return (size_t) ((m_pCurrent - m_pBuffer) * BITS_PER_SIZE_T + m_RelPos - m_InitialRelPos); + } + + __forceinline void SetCurrentPos( size_t pos ) + { + STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; // note: this will set only the host instance, not the target instance + + size_t adjPos = pos + m_InitialRelPos; + m_pCurrent = m_pBuffer + adjPos / BITS_PER_SIZE_T; + m_RelPos = (int)(adjPos % BITS_PER_SIZE_T); + _ASSERTE(GetCurrentPos() == pos); + } + + __forceinline void Skip( SSIZE_T numBitsToSkip ) + { + SUPPORTS_DAC; + + SetCurrentPos(GetCurrentPos() + numBitsToSkip); + } + + __forceinline void AlignUpToByte() + { + if(m_RelPos <= BITS_PER_SIZE_T - 8) + { + m_RelPos = (m_RelPos + 7) & ~7; + } + else + { + m_RelPos = 0; + m_pCurrent++; + } + } + + __forceinline size_t ReadBitAtPos( size_t pos ) + { + size_t adjPos = pos + m_InitialRelPos; + size_t* ptr = m_pBuffer + adjPos / BITS_PER_SIZE_T; + int relPos = (int)(adjPos % BITS_PER_SIZE_T); + return (*ptr) & (((size_t)1) << relPos); + } + + + //-------------------------------------------------------------------------- + // Decode variable length numbers + // See the corresponding methods on BitStreamWriter for more information on the format + //-------------------------------------------------------------------------- + + inline size_t DecodeVarLengthUnsigned( int base ) + { + _ASSERTE((base > 0) && (base < (int)BITS_PER_SIZE_T)); + size_t numEncodings = 1 << base; + size_t result = 0; + for(int shift=0; ; shift+=base) + { + _ASSERTE(shift+base <= (int)BITS_PER_SIZE_T); + + size_t currentChunk = Read(base+1); + result |= (currentChunk & (numEncodings-1)) << shift; + if(!(currentChunk & numEncodings)) + { + // Extension bit is not set, we're done. + return result; + } + } + } + + inline SSIZE_T DecodeVarLengthSigned( int base ) + { + _ASSERTE((base > 0) && (base < (int)BITS_PER_SIZE_T)); + size_t numEncodings = 1 << base; + SSIZE_T result = 0; + for(int shift=0; ; shift+=base) + { + _ASSERTE(shift+base <= (int)BITS_PER_SIZE_T); + + size_t currentChunk = Read(base+1); + result |= (currentChunk & (numEncodings-1)) << shift; + if(!(currentChunk & numEncodings)) + { + // Extension bit is not set, sign-extend and we're done. + int sbits = BITS_PER_SIZE_T - (shift+base); + result <<= sbits; + result >>= sbits; // This provides the sign extension + return result; + } + } + } + +private: + PTR_size_t m_pBuffer; + int m_InitialRelPos; + PTR_size_t m_pCurrent; + int m_RelPos; +}; + +struct GcSlotDesc +{ + union + { + UINT32 RegisterNumber; + GcStackSlot Stack; + } Slot; + GcSlotFlags Flags; +}; + +class GcSlotDecoder +{ +public: + GcSlotDecoder() + {} + + void DecodeSlotTable(BitStreamReader& reader); + + UINT32 GetNumSlots() + { + return m_NumSlots; + } + + UINT32 GetNumUntracked() + { + return m_NumUntracked; + } + + UINT32 GetNumTracked() + { + return m_NumSlots - m_NumUntracked; + } + + UINT32 GetNumRegisters() + { + return m_NumRegisters; + } + + const GcSlotDesc* GetSlotDesc(UINT32 slotIndex); + +private: + GcSlotDesc m_SlotArray[MAX_PREDECODED_SLOTS]; + BitStreamReader m_SlotReader; + UINT32 m_NumSlots; + UINT32 m_NumRegisters; + UINT32 m_NumUntracked; + + UINT32 m_NumDecodedSlots; + GcSlotDesc* m_pLastSlot; +}; + +class GcInfoDecoder +{ +public: + + // If you are not insterested in interruptibility or gc lifetime information, pass 0 as instructionOffset + GcInfoDecoder( + GCInfoToken gcInfoToken, + GcInfoDecoderFlags flags = DECODE_EVERYTHING, + UINT32 instructionOffset = 0 + ); + + //------------------------------------------------------------------------ + // Interruptibility + //------------------------------------------------------------------------ + + bool IsInterruptible(); + +#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED + // This is used for gccoverage + bool IsSafePoint(UINT32 codeOffset); + + typedef void EnumerateSafePointsCallback (UINT32 offset, LPVOID hCallback); + void EnumerateSafePoints(EnumerateSafePointsCallback *pCallback, LPVOID hCallback); + +#endif + // Returns true to stop enumerating. + typedef bool EnumerateInterruptibleRangesCallback (UINT32 startOffset, UINT32 stopOffset, LPVOID hCallback); + + void EnumerateInterruptibleRanges ( + EnumerateInterruptibleRangesCallback *pCallback, + LPVOID hCallback); + + //------------------------------------------------------------------------ + // GC lifetime information + //------------------------------------------------------------------------ + + bool EnumerateLiveSlots( + PREGDISPLAY pRD, + bool reportScratchSlots, + unsigned flags, + GCEnumCallback pCallBack, + LPVOID hCallBack + ); + + // Public for the gc info dumper + void EnumerateUntrackedSlots( + PREGDISPLAY pRD, + unsigned flags, + GCEnumCallback pCallBack, + LPVOID hCallBack + ); + + //------------------------------------------------------------------------ + // Miscellaneous method information + //------------------------------------------------------------------------ + + INT32 GetSecurityObjectStackSlot(); + INT32 GetGSCookieStackSlot(); + UINT32 GetGSCookieValidRangeStart(); + UINT32 GetGSCookieValidRangeEnd(); + UINT32 GetPrologSize(); + INT32 GetPSPSymStackSlot(); + INT32 GetGenericsInstContextStackSlot(); + INT32 GetReversePInvokeStackSlot(); + bool HasMethodDescGenericsInstContext(); + bool HasMethodTableGenericsInstContext(); + bool GetIsVarArg(); + bool WantsReportOnlyLeaf(); + ReturnKind GetReturnKind(); + UINT32 GetCodeLength(); + UINT32 GetStackBaseRegister(); + UINT32 GetSizeOfEditAndContinuePreservedArea(); + size_t GetNumBytesRead(); + +#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA + UINT32 GetSizeOfStackParameterArea(); +#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA + + +private: + BitStreamReader m_Reader; + UINT32 m_InstructionOffset; + + // Pre-decoded information + bool m_IsInterruptible; + bool m_IsVarArg; + bool m_GenericSecretParamIsMD; + bool m_GenericSecretParamIsMT; + bool m_WantsReportOnlyLeaf; + INT32 m_SecurityObjectStackSlot; + INT32 m_GSCookieStackSlot; + INT32 m_ReversePInvokeStackSlot; + UINT32 m_ValidRangeStart; + UINT32 m_ValidRangeEnd; + INT32 m_PSPSymStackSlot; + INT32 m_GenericsInstContextStackSlot; + UINT32 m_CodeLength; + UINT32 m_StackBaseRegister; + UINT32 m_SizeOfEditAndContinuePreservedArea; + INT32 m_ReversePInvokeFrameSlot; + ReturnKind m_ReturnKind; +#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED + UINT32 m_NumSafePoints; + UINT32 m_SafePointIndex; + UINT32 FindSafePoint(UINT32 codeOffset); +#endif + UINT32 m_NumInterruptibleRanges; + +#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA + UINT32 m_SizeOfStackOutgoingAndScratchArea; +#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA + +#ifdef _DEBUG + GcInfoDecoderFlags m_Flags; + PTR_CBYTE m_GcInfoAddress; +#endif + UINT32 m_Version; + + static bool SetIsInterruptibleCB (UINT32 startOffset, UINT32 stopOffset, LPVOID hCallback); + + OBJECTREF* GetRegisterSlot( + int regNum, + PREGDISPLAY pRD + ); + +#ifdef FEATURE_PAL + OBJECTREF* GetCapturedRegister( + int regNum, + PREGDISPLAY pRD + ); +#endif // FEATURE_PAL + + OBJECTREF* GetStackSlot( + INT32 spOffset, + GcStackSlotBase spBase, + PREGDISPLAY pRD + ); + +#ifdef DACCESS_COMPILE + int GetStackReg(int spBase); +#endif // DACCESS_COMPILE + + bool IsScratchRegister(int regNum, PREGDISPLAY pRD); + bool IsScratchStackSlot(INT32 spOffset, GcStackSlotBase spBase, PREGDISPLAY pRD); + + void ReportUntrackedSlots( + GcSlotDecoder& slotDecoder, + PREGDISPLAY pRD, + unsigned flags, + GCEnumCallback pCallBack, + LPVOID hCallBack + ); + + void ReportRegisterToGC( + int regNum, + unsigned gcFlags, + PREGDISPLAY pRD, + unsigned flags, + GCEnumCallback pCallBack, + LPVOID hCallBack + ); + + void ReportStackSlotToGC( + INT32 spOffset, + GcStackSlotBase spBase, + unsigned gcFlags, + PREGDISPLAY pRD, + unsigned flags, + GCEnumCallback pCallBack, + LPVOID hCallBack + ); + + + inline void ReportSlotToGC( + GcSlotDecoder& slotDecoder, + UINT32 slotIndex, + PREGDISPLAY pRD, + bool reportScratchSlots, + unsigned inputFlags, + GCEnumCallback pCallBack, + LPVOID hCallBack + ) + { + _ASSERTE(slotIndex < slotDecoder.GetNumSlots()); + const GcSlotDesc* pSlot = slotDecoder.GetSlotDesc(slotIndex); + + if(slotIndex < slotDecoder.GetNumRegisters()) + { + UINT32 regNum = pSlot->Slot.RegisterNumber; + if( reportScratchSlots || !IsScratchRegister( regNum, pRD ) ) + { + ReportRegisterToGC( + regNum, + pSlot->Flags, + pRD, + inputFlags, + pCallBack, + hCallBack + ); + } + else + { + LOG((LF_GCROOTS, LL_INFO1000, "\"Live\" scratch register " FMT_REG " not reported\n", regNum)); + } + } + else + { + INT32 spOffset = pSlot->Slot.Stack.SpOffset; + GcStackSlotBase spBase = pSlot->Slot.Stack.Base; + if( reportScratchSlots || !IsScratchStackSlot(spOffset, spBase, pRD) ) + { + ReportStackSlotToGC( + spOffset, + spBase, + pSlot->Flags, + pRD, + inputFlags, + pCallBack, + hCallBack + ); + } + else + { + LOG((LF_GCROOTS, LL_INFO1000, "\"Live\" scratch stack slot " FMT_STK " not reported\n", DBG_STK(spOffset))); + } + } + } +}; + + +#endif // _GC_INFO_DECODER_ + |