From ef1e2ab328087c61a6878c1e84f4fc5d710aebce Mon Sep 17 00:00:00 2001 From: dotnet-bot Date: Fri, 30 Jan 2015 14:14:42 -0800 Subject: Initial commit to populate CoreCLR repo [tfs-changeset: 1407945] --- src/vm/gcinfodecoder.cpp | 1853 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1853 insertions(+) create mode 100644 src/vm/gcinfodecoder.cpp (limited to 'src/vm/gcinfodecoder.cpp') diff --git a/src/vm/gcinfodecoder.cpp b/src/vm/gcinfodecoder.cpp new file mode 100644 index 0000000000..a10ab93739 --- /dev/null +++ b/src/vm/gcinfodecoder.cpp @@ -0,0 +1,1853 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +#include "common.h" +#include "gcinfodecoder.h" + + +#ifdef USE_GC_INFO_DECODER + +#ifndef CHECK_APP_DOMAIN +#if CHECK_APP_DOMAIN_LEAKS +#define CHECK_APP_DOMAIN GC_CALL_CHECK_APP_DOMAIN +#else +#define CHECK_APP_DOMAIN 0 +#endif +#endif + +#ifndef GCINFODECODER_CONTRACT +#define GCINFODECODER_CONTRACT(contract) contract +#endif // !GCINFODECODER_CONTRACT + + +#ifndef GET_CALLER_SP +#define GET_CALLER_SP(pREGDISPLAY) EECodeManager::GetCallerSp(pREGDISPLAY) +#endif // !GET_CALLER_SP + +#ifndef VALIDATE_OBJECTREF +#if defined(DACCESS_COMPILE) || defined(CROSSGEN_COMPILE) +#define VALIDATE_OBJECTREF(objref, fDeep) +#else // DACCESS_COMPILE || CROSSGEN_COMPILE +#define VALIDATE_OBJECTREF(objref, fDeep) OBJECTREF_TO_UNCHECKED_OBJECTREF(objref)->Validate(fDeep) +#endif // DACCESS_COMPILE || CROSSGEN_COMPILE +#endif // !VALIDATE_OBJECTREF + +#ifndef VALIDATE_ROOT +#include "gcenv.h" +#define VALIDATE_ROOT(isInterior, hCallBack, pObjRef) \ + do { \ + /* Only call Object::Validate() with bDeep == TRUE if we are in the promote phase. */ \ + /* We should call Validate() with bDeep == FALSE if we are in the relocation phase. */ \ + /* Actually with the introduction of the POPO feature, we cannot validate during */ \ + /* relocate because POPO might have written over the object. It will require non */ \ + /* trivial amount of work to make this work.*/ \ + \ + GCCONTEXT* pGCCtx = (GCCONTEXT*)(hCallBack); \ + \ + if (!(isInterior) && !(m_Flags & DECODE_NO_VALIDATION) && (pGCCtx->sc->promotion)) { \ + VALIDATE_OBJECTREF(*(pObjRef), pGCCtx->sc->promotion == TRUE); \ + } \ + } while (0) +#endif // !VALIDATE_ROOT + + +bool GcInfoDecoder::SetIsInterruptibleCB (UINT32 startOffset, UINT32 stopOffset, LPVOID hCallback) +{ + GcInfoDecoder *pThis = (GcInfoDecoder*)hCallback; + + + bool fStop = pThis->m_InstructionOffset >= startOffset && pThis->m_InstructionOffset < stopOffset; + + if (fStop) + pThis->m_IsInterruptible = true; + + return fStop; +} + + +GcInfoDecoder::GcInfoDecoder( + PTR_CBYTE gcInfoAddr, + GcInfoDecoderFlags flags, + UINT32 breakOffset + ) + : m_Reader( gcInfoAddr +#ifdef VERIFY_GCINFO + + sizeof(size_t) +#endif + ) + , m_InstructionOffset(breakOffset) + , m_IsInterruptible(false) +#ifdef _DEBUG + , m_Flags( flags ) + , m_GcInfoAddress(gcInfoAddr) +#endif +#ifdef VERIFY_GCINFO + , m_DbgDecoder(gcInfoAddr+ + (((UINT32)((PTR_BYTE)(TADDR)gcInfoAddr)[3])<<24)+ + (((UINT32)((PTR_BYTE)(TADDR)gcInfoAddr)[2])<<16)+ + (((UINT32)((PTR_BYTE)(TADDR)gcInfoAddr)[1])<<8)+ + ((PTR_BYTE)(TADDR)gcInfoAddr)[0], + flags, breakOffset) +#endif +{ + _ASSERTE( (flags & (DECODE_INTERRUPTIBILITY | DECODE_GC_LIFETIMES)) || (0 == breakOffset) ); + + // The current implementation doesn't support the two flags together + _ASSERTE( + ((flags & (DECODE_INTERRUPTIBILITY | DECODE_GC_LIFETIMES)) != (DECODE_INTERRUPTIBILITY | DECODE_GC_LIFETIMES)) + ); + + //-------------------------------------------- + // Pre-decode information + //-------------------------------------------- + + GcInfoHeaderFlags headerFlags; + bool slimHeader = (m_Reader.ReadOneFast() == 0); + + if (slimHeader) + { + headerFlags = (GcInfoHeaderFlags)(m_Reader.ReadOneFast() ? GC_INFO_HAS_STACK_BASE_REGISTER : 0); + } + else + { + headerFlags = (GcInfoHeaderFlags) m_Reader.Read(GC_INFO_FLAGS_BIT_SIZE); + } + + m_IsVarArg = headerFlags & GC_INFO_IS_VARARG; + int hasSecurityObject = headerFlags & GC_INFO_HAS_SECURITY_OBJECT; + int hasGSCookie = headerFlags & GC_INFO_HAS_GS_COOKIE; + int hasPSPSym = headerFlags & GC_INFO_HAS_PSP_SYM; + int hasGenericsInstContext = (headerFlags & GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK) != GC_INFO_HAS_GENERICS_INST_CONTEXT_NONE; + m_GenericSecretParamIsMD = (headerFlags & GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK) == GC_INFO_HAS_GENERICS_INST_CONTEXT_MD; + m_GenericSecretParamIsMT = (headerFlags & GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK) == GC_INFO_HAS_GENERICS_INST_CONTEXT_MT; + int hasStackBaseRegister = headerFlags & GC_INFO_HAS_STACK_BASE_REGISTER; + m_WantsReportOnlyLeaf = ((headerFlags & GC_INFO_WANTS_REPORT_ONLY_LEAF) != 0); + int hasSizeOfEditAndContinuePreservedArea = headerFlags & GC_INFO_HAS_EDIT_AND_CONTINUE_PRESERVED_SLOTS; + + m_CodeLength = (UINT32) DENORMALIZE_CODE_LENGTH((UINT32) m_Reader.DecodeVarLengthUnsigned(CODE_LENGTH_ENCBASE)); + + if (flags == DECODE_CODE_LENGTH) + { + // If we are only interested in the code length, then bail out now. + return; + } + + if (hasGSCookie) + { + // Note that normalization as a code offset can be different than + // normalization as code legnth + UINT32 normCodeLength = NORMALIZE_CODE_OFFSET(m_CodeLength); + + // Decode prolog/epilog information + UINT32 normPrologSize = (UINT32) m_Reader.DecodeVarLengthUnsigned(NORM_PROLOG_SIZE_ENCBASE) + 1; + UINT32 normEpilogSize = (UINT32) m_Reader.DecodeVarLengthUnsigned(NORM_EPILOG_SIZE_ENCBASE); + + m_ValidRangeStart = (UINT32) DENORMALIZE_CODE_OFFSET(normPrologSize); + m_ValidRangeEnd = (UINT32) DENORMALIZE_CODE_OFFSET(normCodeLength - normEpilogSize); + _ASSERTE(m_ValidRangeStart < m_ValidRangeEnd); + } + else if (hasSecurityObject || hasGenericsInstContext) + { + // Decode prolog information + UINT32 normPrologSize = (UINT32) m_Reader.DecodeVarLengthUnsigned(NORM_PROLOG_SIZE_ENCBASE) + 1; + m_ValidRangeStart = (UINT32) DENORMALIZE_CODE_OFFSET(normPrologSize); + // satisfy asserts that assume m_GSCookieValidRangeStart != 0 ==> m_GSCookieValidRangeStart < m_GSCookieValidRangeEnd + m_ValidRangeEnd = m_ValidRangeStart + 1; + } + else + { + m_ValidRangeStart = m_ValidRangeEnd = 0; + } + + if (flags == DECODE_PROLOG_LENGTH) + { + // if we are only interested in the prolog size, then bail out now + return; + } + + // Decode the offset to the security object. + if(hasSecurityObject) + { + m_SecurityObjectStackSlot = (INT32) DENORMALIZE_STACK_SLOT(m_Reader.DecodeVarLengthSigned(SECURITY_OBJECT_STACK_SLOT_ENCBASE)); + } + else + { + m_SecurityObjectStackSlot = NO_SECURITY_OBJECT; + } + if (flags == DECODE_SECURITY_OBJECT) + { + // If we are only interested in the security object, then bail out now. + return; + } + + // Decode the offset to the GS cookie. + if(hasGSCookie) + { + m_GSCookieStackSlot = (INT32) DENORMALIZE_STACK_SLOT(m_Reader.DecodeVarLengthSigned(GS_COOKIE_STACK_SLOT_ENCBASE)); + } + else + { + m_GSCookieStackSlot = NO_GS_COOKIE; + } + if (flags == DECODE_GS_COOKIE) + { + // If we are only interested in the GS cookie, then bail out now. + return; + } + + // Decode the offset to the PSPSym. + // The PSPSym is relative to the caller SP on IA64 and the initial stack pointer before any stack allocation on X64 (InitialSP). + if(hasPSPSym) + { + m_PSPSymStackSlot = (INT32) DENORMALIZE_STACK_SLOT(m_Reader.DecodeVarLengthSigned(PSP_SYM_STACK_SLOT_ENCBASE)); + } + else + { + m_PSPSymStackSlot = NO_PSP_SYM; + } + if (flags == DECODE_PSP_SYM) + { + // If we are only interested in the PSPSym, then bail out now. + return; + } + + // Decode the offset to the generics type context. + if(hasGenericsInstContext) + { + m_GenericsInstContextStackSlot = (INT32) DENORMALIZE_STACK_SLOT(m_Reader.DecodeVarLengthSigned(GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE)); + } + else + { + m_GenericsInstContextStackSlot = NO_GENERICS_INST_CONTEXT; + } + if (flags == DECODE_GENERICS_INST_CONTEXT) + { + // If we are only interested in the generics token, then bail out now. + return; + } + + if(hasStackBaseRegister) + { + if (slimHeader) + { + m_StackBaseRegister = (UINT32) DENORMALIZE_STACK_BASE_REGISTER(0); + } + else + { + m_StackBaseRegister = (UINT32) DENORMALIZE_STACK_BASE_REGISTER(m_Reader.DecodeVarLengthUnsigned(STACK_BASE_REGISTER_ENCBASE)); + } + } + else + { + m_StackBaseRegister = NO_STACK_BASE_REGISTER; + } + + if (hasSizeOfEditAndContinuePreservedArea) + { + m_SizeOfEditAndContinuePreservedArea = (UINT32) m_Reader.DecodeVarLengthUnsigned(SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE); + } + else + { + m_SizeOfEditAndContinuePreservedArea = NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA; + } + +#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA + if (slimHeader) + { + m_SizeOfStackOutgoingAndScratchArea = 0; + } + else + { + m_SizeOfStackOutgoingAndScratchArea = (UINT32)DENORMALIZE_SIZE_OF_STACK_AREA(m_Reader.DecodeVarLengthUnsigned(SIZE_OF_STACK_AREA_ENCBASE)); + } +#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA + +#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED + m_NumSafePoints = (UINT32) DENORMALIZE_NUM_SAFE_POINTS(m_Reader.DecodeVarLengthUnsigned(NUM_SAFE_POINTS_ENCBASE)); +#endif + + if (slimHeader) + { + m_NumInterruptibleRanges = 0; + } + else + { + m_NumInterruptibleRanges = (UINT32) DENORMALIZE_NUM_INTERRUPTIBLE_RANGES(m_Reader.DecodeVarLengthUnsigned(NUM_INTERRUPTIBLE_RANGES_ENCBASE)); + } + +#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED + if(flags & (DECODE_INTERRUPTIBILITY | DECODE_GC_LIFETIMES)) + { + if(m_NumSafePoints) + { + m_SafePointIndex = FindSafePoint(m_InstructionOffset); + } + else + { + m_SafePointIndex = 0; + } + } + else if(flags & DECODE_FOR_RANGES_CALLBACK) + { + // Note that normalization as a code offset can be different than + // normalization as code legnth + UINT32 normCodeLength = NORMALIZE_CODE_OFFSET(m_CodeLength); + + UINT32 numBitsPerOffset = CeilOfLog2(normCodeLength); + m_Reader.Skip(m_NumSafePoints * numBitsPerOffset); + } +#endif + + if(!m_IsInterruptible && (flags & DECODE_INTERRUPTIBILITY)) + { + EnumerateInterruptibleRanges(&SetIsInterruptibleCB, this); + } + +#ifdef VERIFY_GCINFO +#if 0 + if(flags & DECODE_INTERRUPTIBILITY) + _ASSERTE(IsInterruptible() == m_DbgDecoder.IsInterruptible()); +#endif + if(flags & DECODE_SECURITY_OBJECT) + _ASSERTE(GetSecurityObjectStackSlot() == m_DbgDecoder.GetSecurityObjectStackSlot()); + if(flags & DECODE_GENERICS_INST_CONTEXT) + { + _ASSERTE(GetGenericsInstContextStackSlot() == m_DbgDecoder.GetGenericsInstContextStackSlot()); + _ASSERTE(GetPSPSymStackSlot() == m_DbgDecoder.GetPSPSymStackSlot()); + } + if(flags & DECODE_VARARG) + _ASSERTE(GetIsVarArg() == m_DbgDecoder.GetIsVarArg()); + if(flags & DECODE_CODE_LENGTH) + _ASSERTE(GetCodeLength() == m_DbgDecoder.GetCodeLength()); + _ASSERTE(GetStackBaseRegister() == m_DbgDecoder.GetStackBaseRegister()); + _ASSERTE(GetSizeOfEditAndContinuePreservedArea() == m_DbgDecoder.GetSizeOfEditAndContinuePreservedArea()); +#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA + _ASSERTE(GetSizeOfStackParameterArea() == m_DbgDecoder.GetSizeOfStackParameterArea()); +#endif +#endif + +} + +bool GcInfoDecoder::IsInterruptible() +{ + _ASSERTE( m_Flags & DECODE_INTERRUPTIBILITY ); + return m_IsInterruptible; +} + +bool GcInfoDecoder::HasMethodDescGenericsInstContext() +{ + _ASSERTE( m_Flags & DECODE_GENERICS_INST_CONTEXT ); + return m_GenericSecretParamIsMD; +} + +bool GcInfoDecoder::HasMethodTableGenericsInstContext() +{ + _ASSERTE( m_Flags & DECODE_GENERICS_INST_CONTEXT ); + return m_GenericSecretParamIsMT; +} + +#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED + +// This is used for gccoverage: is the given offset +// a call-return offset with partially-interruptible GC info? +bool GcInfoDecoder::IsSafePoint(UINT32 codeOffset) +{ + _ASSERTE(m_Flags == 0 && m_InstructionOffset == 0); + if(m_NumSafePoints == 0) + return false; + +#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM_) + // Safepoints are encoded with a -1 adjustment + codeOffset--; +#endif + size_t savedPos = m_Reader.GetCurrentPos(); + UINT32 safePointIndex = FindSafePoint(codeOffset); + m_Reader.SetCurrentPos(savedPos); + return (bool) (safePointIndex != m_NumSafePoints); + +} + +UINT32 GcInfoDecoder::FindSafePoint(UINT32 breakOffset) +{ + if(m_NumSafePoints == 0) + return 0; + + const size_t savedPos = m_Reader.GetCurrentPos(); + const UINT32 numBitsPerOffset = CeilOfLog2(NORMALIZE_CODE_OFFSET(m_CodeLength)); + UINT32 result = m_NumSafePoints; + +#if defined(_TARGET_ARM_) + // Safepoints are encoded with a -1 adjustment + // but normalizing them masks off the low order bit + // Thus only bother looking if the address is odd + if ((breakOffset & 1) != 0) +#endif + { + const UINT32 normBreakOffset = NORMALIZE_CODE_OFFSET(breakOffset); + + INT32 low = 0; + INT32 high = (INT32)m_NumSafePoints; + + while(low < high) + { + const INT32 mid = (low+high)/2; + _ASSERTE(mid >= 0 && mid < (INT32)m_NumSafePoints); + m_Reader.SetCurrentPos(savedPos + (UINT32)mid * numBitsPerOffset); + UINT32 normOffset = (UINT32)m_Reader.Read(numBitsPerOffset); + if(normOffset == normBreakOffset) + { + result = (UINT32) mid; + break; + } + + if(normOffset < normBreakOffset) + low = mid+1; + else + high = mid; + } + } + + m_Reader.SetCurrentPos(savedPos + m_NumSafePoints * numBitsPerOffset); + return result; +} + +void GcInfoDecoder::EnumerateSafePoints(EnumerateSafePointsCallback *pCallback, LPVOID hCallback) +{ + if(m_NumSafePoints == 0) + return; + + const UINT32 numBitsPerOffset = CeilOfLog2(NORMALIZE_CODE_OFFSET(m_CodeLength)); + + for(UINT i = 0; i < m_NumSafePoints; i++) + { + UINT32 normOffset = (UINT32)m_Reader.Read(numBitsPerOffset); + UINT32 offset = DENORMALIZE_CODE_OFFSET(normOffset) + 2; + pCallback(offset, hCallback); + } +} +#endif + +void GcInfoDecoder::EnumerateInterruptibleRanges ( + EnumerateInterruptibleRangesCallback *pCallback, + LPVOID hCallback) +{ + // If no info is found for the call site, we default to fully-interruptbile + LOG((LF_GCROOTS, LL_INFO1000000, "No GC info found for call site at offset %x. Defaulting to fully-interruptible information.\n", (int) m_InstructionOffset)); + + UINT32 lastInterruptibleRangeStopOffsetNormalized = 0; + + for(UINT32 i=0; i= normStart && normBreakOffset < normStop) + { + _ASSERTE(pseudoBreakOffset == 0); + _ASSERTE(dbgCountIntersections++ == 0); + pseudoBreakOffset = numInterruptibleLength + normBreakOffset - normStart; + } + numInterruptibleLength += normStopDelta; + lastNormStop = normStop; + } + _ASSERTE(dbgCountIntersections == 1); + } +#else // DISABLE_EH_VECTORS + if(m_SafePointIndex < m_NumSafePoints && !executionAborted) + { + // Skip interruptibility information + for(UINT32 i=0; i= normStart && normBreakOffset < normStop) + { + _ASSERTE(pseudoBreakOffset == 0); + countIntersections++; + pseudoBreakOffset = numInterruptibleLength + normBreakOffset - normStart; + } + numInterruptibleLength += normStopDelta; + lastNormStop = normStop; + } + _ASSERTE(countIntersections <= 1); + if(countIntersections == 0) + { + _ASSERTE(executionAborted); + LOG((LF_GCROOTS, LL_INFO100000, "Not reporting this frame because it is aborted and not fully interruptible.\n")); + goto ExitSuccess; + } + } +#endif // DISABLE_EH_VECTORS +#else // !PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED + + // Skip interruptibility information + for(UINT32 i=0; i 0) && m_Reader.ReadOneFast()) + { + numBitsPerOffset = (UINT32) m_Reader.DecodeVarLengthUnsigned(POINTER_SIZE_ENCBASE) + 1; + _ASSERTE(numBitsPerOffset != 0); + } + + //------------------------------------------------------------------------------ + // Try partially interruptible first + //------------------------------------------------------------------------------ + + if(executionAborted) + { +#ifndef DISABLE_EH_VECTORS + m_Reader.Skip(m_NumSafePoints * numSlots); + + UINT32 numClauses = (UINT32) m_Reader.DecodeVarLengthUnsigned(NUM_EH_CLAUSES_ENCBASE); + + if(numClauses) + { + UINT32 numBitsPerOffset = CeilOfLog2(NORMALIZE_CODE_OFFSET(m_CodeLength)); + + for(UINT32 i = 0; i < numClauses; i++) + { + UINT32 startOffset = (UINT32) DENORMALIZE_CODE_OFFSET(m_Reader.Read(numBitsPerOffset)); + UINT32 stopOffset = (UINT32) DENORMALIZE_CODE_OFFSET(m_Reader.Read(numBitsPerOffset) + 1); + + if(m_InstructionOffset >= startOffset + && m_InstructionOffset < stopOffset) + { + for(UINT32 slotIndex = 0; slotIndex < numSlots; slotIndex++) + { + if(m_Reader.ReadOneFast()) + { + ReportSlotToGC( + slotDecoder, + slotIndex, + pRD, + reportScratchSlots, + inputFlags, + pCallBack, + hCallBack + ); + } + } + } + else + { + m_Reader.Skip(numSlots); + } + } + } + goto ReportUntracked; +#else //DISABLE_EH_VECTORS + + _ASSERTE(m_NumSafePoints == 0); + m_Reader.Skip(m_NumSafePoints * numSlots); + +#endif //DISABLE_EH_VECTORS + } + else if( m_SafePointIndex != m_NumSafePoints ) + { + if (numBitsPerOffset) + { + const size_t offsetTablePos = m_Reader.GetCurrentPos(); + m_Reader.Skip(m_SafePointIndex * numBitsPerOffset); + const size_t liveStatesOffset = m_Reader.Read(numBitsPerOffset); + const size_t liveStatesStart = ((offsetTablePos + m_NumSafePoints * numBitsPerOffset + 7) & (~7)); + m_Reader.SetCurrentPos(liveStatesStart + liveStatesOffset); + if (m_Reader.ReadOneFast()) { + // RLE encoded + bool fSkip = (m_Reader.ReadOneFast() == 0); + bool fReport = true; + UINT32 readSlots = (UINT32)m_Reader.DecodeVarLengthUnsigned( fSkip ? LIVESTATE_RLE_SKIP_ENCBASE : LIVESTATE_RLE_RUN_ENCBASE ); + fSkip = !fSkip; + while (readSlots < numSlots) + { + UINT32 cnt = (UINT32)m_Reader.DecodeVarLengthUnsigned( fSkip ? LIVESTATE_RLE_SKIP_ENCBASE : LIVESTATE_RLE_RUN_ENCBASE ) + 1; + if (fReport) + { + for(UINT32 slotIndex = readSlots; slotIndex < readSlots + cnt; slotIndex++) + { + ReportSlotToGC(slotDecoder, + slotIndex, + pRD, + reportScratchSlots, + inputFlags, + pCallBack, + hCallBack + ); + } + } + readSlots += cnt; + fSkip = !fSkip; + fReport = !fReport; + } + _ASSERTE(readSlots == numSlots); + goto ReportUntracked; + } + // Just a normal live state (1 bit per slot), so use the normal decoding loop + } + else + { + m_Reader.Skip(m_SafePointIndex * numSlots); + } + + for(UINT32 slotIndex = 0; slotIndex < numSlots; slotIndex++) + { + if(m_Reader.ReadOneFast()) + { + ReportSlotToGC( + slotDecoder, + slotIndex, + pRD, + reportScratchSlots, + inputFlags, + pCallBack, + hCallBack + ); + } + } + goto ReportUntracked; + } + else + { + m_Reader.Skip(m_NumSafePoints * numSlots); + +#ifndef DISABLE_EH_VECTORS + UINT32 numClauses = (UINT32) m_Reader.DecodeVarLengthUnsigned(NUM_EH_CLAUSES_ENCBASE); + UINT32 numBitsPerOffset = CeilOfLog2(NORMALIZE_CODE_OFFSET(m_CodeLength)); + + m_Reader.Skip((numBitsPerOffset * 2 + numSlots) * numClauses); +#endif //DISABLE_EH_VECTORS + } + +#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED + + _ASSERTE(m_NumInterruptibleRanges); + _ASSERTE(numInterruptibleLength); + + // If no info is found for the call site, we default to fully-interruptbile + LOG((LF_GCROOTS, LL_INFO1000000, "No GC info found for call site at offset %x. Defaulting to fully-interruptible information.\n", (int) m_InstructionOffset)); + + UINT32 numChunks = (numInterruptibleLength + NUM_NORM_CODE_OFFSETS_PER_CHUNK - 1) / NUM_NORM_CODE_OFFSETS_PER_CHUNK; + UINT32 breakChunk = pseudoBreakOffset / NUM_NORM_CODE_OFFSETS_PER_CHUNK; + _ASSERTE(breakChunk < numChunks); + + UINT32 numBitsPerPointer = (UINT32) m_Reader.DecodeVarLengthUnsigned(POINTER_SIZE_ENCBASE); + + if(!numBitsPerPointer) + goto ReportUntracked; + + size_t pointerTablePos = m_Reader.GetCurrentPos(); + + size_t chunkPointer; + UINT32 chunk = breakChunk; + for(;;) + { + m_Reader.SetCurrentPos(pointerTablePos + chunk * numBitsPerPointer); + chunkPointer = m_Reader.Read(numBitsPerPointer); + if(chunkPointer) + break; + + if(chunk-- == 0) + goto ReportUntracked; + } + + size_t chunksStartPos = ((pointerTablePos + numChunks * numBitsPerPointer + 7) & (~7)); + size_t chunkPos = chunksStartPos + chunkPointer - 1; + m_Reader.SetCurrentPos(chunkPos); + + { + BitStreamReader couldBeLiveReader(m_Reader); + + UINT32 numCouldBeLiveSlots = 0; + // A potentially compressed bit vector of which slots have any lifetimes + if (m_Reader.ReadOneFast()) + { + // RLE encoded + bool fSkip = (m_Reader.ReadOneFast() == 0); + bool fReport = true; + UINT32 readSlots = (UINT32)m_Reader.DecodeVarLengthUnsigned( fSkip ? LIVESTATE_RLE_SKIP_ENCBASE : LIVESTATE_RLE_RUN_ENCBASE ); + fSkip = !fSkip; + while (readSlots < numSlots) + { + UINT32 cnt = (UINT32)m_Reader.DecodeVarLengthUnsigned( fSkip ? LIVESTATE_RLE_SKIP_ENCBASE : LIVESTATE_RLE_RUN_ENCBASE ) + 1; + if (fReport) + { + numCouldBeLiveSlots += cnt; + } + readSlots += cnt; + fSkip = !fSkip; + fReport = !fReport; + } + _ASSERTE(readSlots == numSlots); + + } + else + { + for(UINT32 i = 0; i < numSlots; i++) + { + if(m_Reader.ReadOneFast()) + numCouldBeLiveSlots++; + } + } + _ASSERTE(numCouldBeLiveSlots > 0); + + BitStreamReader finalStateReader(m_Reader); + + m_Reader.Skip(numCouldBeLiveSlots); + + int lifetimeTransitionsCount = 0; + + UINT32 slotIndex = 0; + bool fSimple = (couldBeLiveReader.ReadOneFast() == 0); + bool fSkipFirst = false; // silence the warning + UINT32 cnt = 0; + if (!fSimple) + { + fSkipFirst = (couldBeLiveReader.ReadOneFast() == 0); + slotIndex = -1; + } + for(UINT32 i = 0; i < numCouldBeLiveSlots; i++) + { + if (fSimple) + { + while(!couldBeLiveReader.ReadOneFast()) + slotIndex++; + } + else if (cnt > 0) + { + // We have more from the last run to report + cnt--; + } + // We need to find a new run + else if (fSkipFirst) + { + UINT32 tmp = (UINT32)couldBeLiveReader.DecodeVarLengthUnsigned( LIVESTATE_RLE_SKIP_ENCBASE ) + 1; + slotIndex += tmp; + cnt = (UINT32)couldBeLiveReader.DecodeVarLengthUnsigned( LIVESTATE_RLE_RUN_ENCBASE ); + } + else + { + UINT32 tmp = (UINT32)couldBeLiveReader.DecodeVarLengthUnsigned( LIVESTATE_RLE_RUN_ENCBASE ) + 1; + slotIndex += tmp; + cnt = (UINT32)couldBeLiveReader.DecodeVarLengthUnsigned( LIVESTATE_RLE_SKIP_ENCBASE ); + } + + UINT32 isLive = (UINT32) finalStateReader.Read(1); + + if(chunk == breakChunk) + { + // Read transitions + UINT32 normBreakOffsetDelta = pseudoBreakOffset % NUM_NORM_CODE_OFFSETS_PER_CHUNK; + for(;;) + { + if(!m_Reader.ReadOneFast()) + break; + + UINT32 transitionOffset = (UINT32) m_Reader.Read(NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2); + + lifetimeTransitionsCount++; + _ASSERTE(transitionOffset && transitionOffset < NUM_NORM_CODE_OFFSETS_PER_CHUNK); + if(transitionOffset > normBreakOffsetDelta) + { + isLive ^= 1; + } + } + } + + if(isLive) + { + ReportSlotToGC( + slotDecoder, + slotIndex, + pRD, + reportScratchSlots, + inputFlags, + pCallBack, + hCallBack + ); + } + + slotIndex++; + } + + LOG((LF_GCROOTS, LL_INFO1000000, "Decoded %d lifetime transitions.\n", (int) lifetimeTransitionsCount )); + } + } + +ReportUntracked: + + //------------------------------------------------------------------------------ + // Last report anything untracked + // But only for the leaf funclet/frame + // Turned on in the VM for regular GC reporting and the DAC for !CLRStack -gc + // But turned off in the #includes for nidump and sos's !u -gcinfo and !gcinfo + //------------------------------------------------------------------------------ + + if (slotDecoder.GetNumUntracked() && !(inputFlags & (ParentOfFuncletStackFrame | NoReportUntracked))) + { + ReportUntrackedSlots(slotDecoder, pRD, inputFlags, pCallBack, hCallBack); + } + +#ifdef DISABLE_EH_VECTORS +ExitSuccess: +#endif + +#ifdef VERIFY_GCINFO +#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED + if(!executionAborted) +#endif + m_DbgDecoder.DoFinalVerification(); +#endif + + return true; +} + +void GcInfoDecoder::EnumerateUntrackedSlots( + PREGDISPLAY pRD, + unsigned inputFlags, + GCEnumCallback pCallBack, + LPVOID hCallBack + ) +{ + _ASSERTE(GC_SLOT_INTERIOR == GC_CALL_INTERIOR); + _ASSERTE(GC_SLOT_PINNED == GC_CALL_PINNED); + + _ASSERTE( m_Flags & DECODE_GC_LIFETIMES ); + + GcSlotDecoder slotDecoder; + + // Skip interruptibility information + for(UINT32 i=0; i 0) + { + // We certainly predecode the first register + + _ASSERTE(i < MAX_PREDECODED_SLOTS); + + UINT32 normRegNum = (UINT32) reader.DecodeVarLengthUnsigned(REGISTER_ENCBASE); + UINT32 regNum = DENORMALIZE_REGISTER(normRegNum); + GcSlotFlags flags = (GcSlotFlags) reader.Read(2); + + m_SlotArray[0].Slot.RegisterNumber = regNum; + m_SlotArray[0].Flags = flags; + + UINT32 loopEnd = min(m_NumRegisters, MAX_PREDECODED_SLOTS); + for(i++; i < loopEnd; i++) + { + if(flags) + { + normRegNum = (UINT32) reader.DecodeVarLengthUnsigned(REGISTER_ENCBASE); + regNum = DENORMALIZE_REGISTER(normRegNum); + flags = (GcSlotFlags) reader.Read(2); + } + else + { + UINT32 normRegDelta = (UINT32) reader.DecodeVarLengthUnsigned(REGISTER_DELTA_ENCBASE) + 1; + normRegNum += normRegDelta; + regNum = DENORMALIZE_REGISTER(normRegNum); + } + + m_SlotArray[i].Slot.RegisterNumber = regNum; + m_SlotArray[i].Flags = flags; + } + } + + if((numStackSlots > 0) && (i < MAX_PREDECODED_SLOTS)) + { + // We have stack slots left and more room to predecode + + GcStackSlotBase spBase = (GcStackSlotBase) reader.Read(2); + UINT32 normSpOffset = (INT32) reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE); + INT32 spOffset = DENORMALIZE_STACK_SLOT(normSpOffset); + GcSlotFlags flags = (GcSlotFlags) reader.Read(2); + + m_SlotArray[i].Slot.Stack.SpOffset = spOffset; + m_SlotArray[i].Slot.Stack.Base = spBase; + m_SlotArray[i].Flags = flags; + + UINT32 loopEnd = min(m_NumRegisters + numStackSlots, MAX_PREDECODED_SLOTS); + for(i++; i < loopEnd; i++) + { + spBase = (GcStackSlotBase) reader.Read(2); + + if(flags) + { + normSpOffset = (INT32) reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE); + spOffset = DENORMALIZE_STACK_SLOT(normSpOffset); + flags = (GcSlotFlags) reader.Read(2); + } + else + { + INT32 normSpOffsetDelta = (INT32) reader.DecodeVarLengthUnsigned(STACK_SLOT_DELTA_ENCBASE); + normSpOffset += normSpOffsetDelta; + spOffset = DENORMALIZE_STACK_SLOT(normSpOffset); + } + + m_SlotArray[i].Slot.Stack.SpOffset = spOffset; + m_SlotArray[i].Slot.Stack.Base = spBase; + m_SlotArray[i].Flags = flags; + } + } + + if((m_NumUntracked > 0) && (i < MAX_PREDECODED_SLOTS)) + { + // We have untracked stack slots left and more room to predecode + + GcStackSlotBase spBase = (GcStackSlotBase) reader.Read(2); + UINT32 normSpOffset = (INT32) reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE); + INT32 spOffset = DENORMALIZE_STACK_SLOT(normSpOffset); + GcSlotFlags flags = (GcSlotFlags) reader.Read(2); + + m_SlotArray[i].Slot.Stack.SpOffset = spOffset; + m_SlotArray[i].Slot.Stack.Base = spBase; + m_SlotArray[i].Flags = flags; + + UINT32 loopEnd = min(m_NumSlots, MAX_PREDECODED_SLOTS); + for(i++; i < loopEnd; i++) + { + spBase = (GcStackSlotBase) reader.Read(2); + + if(flags) + { + normSpOffset = (INT32) reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE); + spOffset = DENORMALIZE_STACK_SLOT(normSpOffset); + flags = (GcSlotFlags) reader.Read(2); + } + else + { + INT32 normSpOffsetDelta = (INT32) reader.DecodeVarLengthUnsigned(STACK_SLOT_DELTA_ENCBASE); + normSpOffset += normSpOffsetDelta; + spOffset = DENORMALIZE_STACK_SLOT(normSpOffset); + } + + m_SlotArray[i].Slot.Stack.SpOffset = spOffset; + m_SlotArray[i].Slot.Stack.Base = spBase; + m_SlotArray[i].Flags = flags; + } + } + + // Done pre-decoding + + if(i < m_NumSlots) + { + // Prepare for lazy decoding + + _ASSERTE(i == MAX_PREDECODED_SLOTS); + m_NumDecodedSlots = i; + m_pLastSlot = &m_SlotArray[MAX_PREDECODED_SLOTS - 1]; + + m_SlotReader = reader; + + // Move the argument reader past the end of the table + + GcSlotFlags flags = m_pLastSlot->Flags; + + // Skip any remaining registers + + for(; i < m_NumRegisters; i++) + { + if(flags) + { + reader.DecodeVarLengthUnsigned(REGISTER_ENCBASE); + flags = (GcSlotFlags) reader.Read(2); + } + else + { + reader.DecodeVarLengthUnsigned(REGISTER_DELTA_ENCBASE); + } + } + + if(numStackSlots > 0) + { + if(i == m_NumRegisters) + { + // Skip the first stack slot + + reader.Read(2); + reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE); + flags = (GcSlotFlags) reader.Read(2); + i++; + } + + // Skip any remaining stack slots + + const UINT32 loopEnd = m_NumRegisters + numStackSlots; + for(; i < loopEnd; i++) + { + reader.Read(2); + + if(flags) + { + reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE); + flags = (GcSlotFlags) reader.Read(2); + } + else + { + reader.DecodeVarLengthUnsigned(STACK_SLOT_DELTA_ENCBASE); + } + } + } + + if(m_NumUntracked > 0) + { + if(i == m_NumRegisters + numStackSlots) + { + // Skip the first untracked slot + + reader.Read(2); + reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE); + flags = (GcSlotFlags) reader.Read(2); + i++; + } + + // Skip any remaining untracked slots + + for(; i < m_NumSlots; i++) + { + reader.Read(2); + + if(flags) + { + reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE); + flags = (GcSlotFlags) reader.Read(2); + } + else + { + reader.DecodeVarLengthUnsigned(STACK_SLOT_DELTA_ENCBASE); + } + } + } + } +} + +const GcSlotDesc* GcSlotDecoder::GetSlotDesc(UINT32 slotIndex) +{ + _ASSERTE(slotIndex < m_NumSlots); + + if(slotIndex < MAX_PREDECODED_SLOTS) + { + return &m_SlotArray[slotIndex]; + } + + _ASSERTE(m_NumDecodedSlots >= MAX_PREDECODED_SLOTS && m_NumDecodedSlots < m_NumSlots); + _ASSERTE(m_NumDecodedSlots <= slotIndex); + + while(m_NumDecodedSlots <= slotIndex) + { + if(m_NumDecodedSlots < m_NumRegisters) + { + // + // Decode a register + // + + if(m_NumDecodedSlots == 0) + { + // Decode the first register + UINT32 normRegNum = (UINT32) m_SlotReader.DecodeVarLengthUnsigned(REGISTER_ENCBASE); + m_pLastSlot->Slot.RegisterNumber = DENORMALIZE_REGISTER(normRegNum); + m_pLastSlot->Flags = (GcSlotFlags) m_SlotReader.Read(2); + } + else + { + if(m_pLastSlot->Flags) + { + UINT32 normRegNum = (UINT32) m_SlotReader.DecodeVarLengthUnsigned(REGISTER_ENCBASE); + m_pLastSlot->Slot.RegisterNumber = DENORMALIZE_REGISTER(normRegNum); + m_pLastSlot->Flags = (GcSlotFlags) m_SlotReader.Read(2); + } + else + { + UINT32 normRegDelta = (UINT32) m_SlotReader.DecodeVarLengthUnsigned(REGISTER_DELTA_ENCBASE) + 1; + UINT32 normRegNum = normRegDelta + NORMALIZE_REGISTER(m_pLastSlot->Slot.RegisterNumber); + m_pLastSlot->Slot.RegisterNumber = DENORMALIZE_REGISTER(normRegNum); + } + } + } + else + { + // + // Decode a stack slot + // + + if((m_NumDecodedSlots == m_NumRegisters) || (m_NumDecodedSlots == GetNumTracked())) + { + // Decode the first stack slot or first untracked slot + m_pLastSlot->Slot.Stack.Base = (GcStackSlotBase) m_SlotReader.Read(2); + UINT32 normSpOffset = (INT32) m_SlotReader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE); + m_pLastSlot->Slot.Stack.SpOffset = DENORMALIZE_STACK_SLOT(normSpOffset); + m_pLastSlot->Flags = (GcSlotFlags) m_SlotReader.Read(2); + } + else + { + m_pLastSlot->Slot.Stack.Base = (GcStackSlotBase) m_SlotReader.Read(2); + + if(m_pLastSlot->Flags) + { + INT32 normSpOffset = (INT32) m_SlotReader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE); + m_pLastSlot->Slot.Stack.SpOffset = DENORMALIZE_STACK_SLOT(normSpOffset); + m_pLastSlot->Flags = (GcSlotFlags) m_SlotReader.Read(2); + } + else + { + INT32 normSpOffsetDelta = (INT32) m_SlotReader.DecodeVarLengthUnsigned(STACK_SLOT_DELTA_ENCBASE); + INT32 normSpOffset = normSpOffsetDelta + NORMALIZE_STACK_SLOT(m_pLastSlot->Slot.Stack.SpOffset); + m_pLastSlot->Slot.Stack.SpOffset = DENORMALIZE_STACK_SLOT(normSpOffset); + } + } + } + + m_NumDecodedSlots++; + } + + return m_pLastSlot; +} + + +//----------------------------------------------------------------------------- +// Platform-specific methods +//----------------------------------------------------------------------------- + +#if defined(_TARGET_AMD64_) + + +OBJECTREF* GcInfoDecoder::GetRegisterSlot( + int regNum, + PREGDISPLAY pRD + ) +{ + _ASSERTE(regNum >= 0 && regNum <= 16); + _ASSERTE(regNum != 4); // rsp + + // The fields of KNONVOLATILE_CONTEXT_POINTERS are in the same order as + // the processor encoding numbers. + + ULONGLONG **ppRax; + ppRax = &pRD->pCurrentContextPointers->Rax; + + return (OBJECTREF*)*(ppRax + regNum); +} + + +bool GcInfoDecoder::IsScratchRegister(int regNum, PREGDISPLAY pRD) +{ + _ASSERTE(regNum >= 0 && regNum <= 16); + _ASSERTE(regNum != 4); // rsp + + UINT16 PreservedRegMask = + (1 << 3) // rbx + | (1 << 5) // rbp + | (1 << 6) // rsi + | (1 << 7) // rdi + | (1 << 12) // r12 + | (1 << 13) // r13 + | (1 << 14) // r14 + | (1 << 15); // r15 + + return !(PreservedRegMask & (1 << regNum)); +} + + +bool GcInfoDecoder::IsScratchStackSlot(INT32 spOffset, GcStackSlotBase spBase, PREGDISPLAY pRD) +{ +#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA + _ASSERTE( m_Flags & DECODE_GC_LIFETIMES ); + + ULONGLONG pSlot = (ULONGLONG) GetStackSlot(spOffset, spBase, pRD); + _ASSERTE(pSlot >= pRD->SP); + + return (pSlot < pRD->SP + m_SizeOfStackOutgoingAndScratchArea); +#else + return FALSE; +#endif +} + + +void GcInfoDecoder::ReportRegisterToGC( // AMD64 + int regNum, + unsigned gcFlags, + PREGDISPLAY pRD, + unsigned flags, + GCEnumCallback pCallBack, + LPVOID hCallBack) +{ + GCINFODECODER_CONTRACT(CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END); + + _ASSERTE(regNum >= 0 && regNum <= 16); + _ASSERTE(regNum != 4); // rsp + + LOG((LF_GCROOTS, LL_INFO1000, "Reporting " FMT_REG, regNum )); + + OBJECTREF* pObjRef = GetRegisterSlot( regNum, pRD ); + +#ifdef _DEBUG + if(IsScratchRegister(regNum, pRD)) + { + // Scratch registers cannot be reported for non-leaf frames + _ASSERTE(flags & ActiveStackFrame); + } + + LOG((LF_GCROOTS, LL_INFO1000, /* Part Two */ + "at" FMT_ADDR "as ", DBG_ADDR(pObjRef) )); + + VALIDATE_ROOT((gcFlags & GC_CALL_INTERIOR), hCallBack, pObjRef); + + LOG((LF_GCROOTS, LL_INFO1000, /* Part Three */ + LOG_PIPTR_OBJECT_CLASS(OBJECTREF_TO_UNCHECKED_OBJECTREF(*pObjRef), (gcFlags & GC_CALL_PINNED), (gcFlags & GC_CALL_INTERIOR)))); +#endif //_DEBUG + + gcFlags |= CHECK_APP_DOMAIN; + + pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(regNum, 0, false))); +} + +#elif defined(_TARGET_ARM_) + +OBJECTREF* GcInfoDecoder::GetRegisterSlot( + int regNum, + PREGDISPLAY pRD + ) +{ + _ASSERTE(regNum >= 0 && regNum <= 14); + _ASSERTE(regNum != 13); // sp + + DWORD **ppReg; + + if(regNum <= 3) + { + ppReg = &pRD->volatileCurrContextPointers.R0; + return (OBJECTREF*)*(ppReg + regNum); + } + else if(regNum == 12) + { + return (OBJECTREF*) pRD->volatileCurrContextPointers.R12; + } + else if(regNum == 14) + { + return (OBJECTREF*) pRD->pCurrentContextPointers->Lr; + } + + ppReg = &pRD->pCurrentContextPointers->R4; + + return (OBJECTREF*)*(ppReg + regNum-4); + +} + + +bool GcInfoDecoder::IsScratchRegister(int regNum, PREGDISPLAY pRD) +{ + _ASSERTE(regNum >= 0 && regNum <= 14); + _ASSERTE(regNum != 13); // sp + + return regNum <= 3 || regNum >= 12; // R12 and R14/LR are both scratch registers +} + + +bool GcInfoDecoder::IsScratchStackSlot(INT32 spOffset, GcStackSlotBase spBase, PREGDISPLAY pRD) +{ +#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA + _ASSERTE( m_Flags & DECODE_GC_LIFETIMES ); + + DWORD pSlot = (DWORD) GetStackSlot(spOffset, spBase, pRD); + _ASSERTE(pSlot >= pRD->SP); + + return (pSlot < pRD->SP + m_SizeOfStackOutgoingAndScratchArea); +#else + return FALSE; +#endif +} + + +void GcInfoDecoder::ReportRegisterToGC( // ARM + int regNum, + unsigned gcFlags, + PREGDISPLAY pRD, + unsigned flags, + GCEnumCallback pCallBack, + LPVOID hCallBack) +{ + GCINFODECODER_CONTRACT(CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END); + + _ASSERTE(regNum >= 0 && regNum <= 14); + _ASSERTE(regNum != 13); // sp + + LOG((LF_GCROOTS, LL_INFO1000, "Reporting " FMT_REG, regNum )); + + OBJECTREF* pObjRef = GetRegisterSlot( regNum, pRD ); + +#ifdef _DEBUG + if(IsScratchRegister(regNum, pRD)) + { + // Scratch registers cannot be reported for non-leaf frames + _ASSERTE(flags & ActiveStackFrame); + } + + LOG((LF_GCROOTS, LL_INFO1000, /* Part Two */ + "at" FMT_ADDR "as ", DBG_ADDR(pObjRef) )); + + VALIDATE_ROOT((gcFlags & GC_CALL_INTERIOR), hCallBack, pObjRef); + + LOG((LF_GCROOTS, LL_INFO1000, /* Part Three */ + LOG_PIPTR_OBJECT_CLASS(OBJECTREF_TO_UNCHECKED_OBJECTREF(*pObjRef), (gcFlags & GC_CALL_PINNED), (gcFlags & GC_CALL_INTERIOR)))); +#endif //_DEBUG + + gcFlags |= CHECK_APP_DOMAIN; + + pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(regNum, 0, false))); +} + +#elif defined(_TARGET_ARM64_) + +OBJECTREF* GcInfoDecoder::GetRegisterSlot( + int regNum, + PREGDISPLAY pRD + ) +{ + _ASSERTE(regNum >= 0 && regNum <= 30); + _ASSERTE(regNum != 18); // TEB + + DWORD64 **ppReg; + + if(regNum <= 17) + { + ppReg = &pRD->volatileCurrContextPointers.X0; + return (OBJECTREF*)*(ppReg + regNum); + } + else if(regNum == 29) + { + return (OBJECTREF*) pRD->pCurrentContextPointers->Fp; + } + else if(regNum == 30) + { + return (OBJECTREF*) pRD->pCurrentContextPointers->Lr; + } + + ppReg = &pRD->pCurrentContextPointers->X19; + + return (OBJECTREF*)*(ppReg + regNum-19); +} + +bool GcInfoDecoder::IsScratchRegister(int regNum, PREGDISPLAY pRD) +{ + _ASSERTE(regNum >= 0 && regNum <= 30); + _ASSERTE(regNum != 18); + + return regNum <= 17 || regNum >= 29; // R12 and R14/LR are both scratch registers +} + +bool GcInfoDecoder::IsScratchStackSlot(INT32 spOffset, GcStackSlotBase spBase, PREGDISPLAY pRD) +{ +#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA + _ASSERTE( m_Flags & DECODE_GC_LIFETIMES ); + + ULONGLONG pSlot = (ULONGLONG) GetStackSlot(spOffset, spBase, pRD); + _ASSERTE(pSlot >= pRD->SP); + + return (pSlot < pRD->SP + m_SizeOfStackOutgoingAndScratchArea); +#else + return FALSE; +#endif + +} + +void GcInfoDecoder::ReportRegisterToGC( // ARM64 + int regNum, + unsigned gcFlags, + PREGDISPLAY pRD, + unsigned flags, + GCEnumCallback pCallBack, + LPVOID hCallBack) +{ + GCINFODECODER_CONTRACT(CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END); + + _ASSERTE(regNum >= 0 && regNum <= 30); + _ASSERTE(regNum != 18); + + LOG((LF_GCROOTS, LL_INFO1000, "Reporting " FMT_REG, regNum )); + + OBJECTREF* pObjRef = GetRegisterSlot( regNum, pRD ); + +#ifdef _DEBUG + if(IsScratchRegister(regNum, pRD)) + { + // Scratch registers cannot be reported for non-leaf frames + _ASSERTE(flags & ActiveStackFrame); + } + + LOG((LF_GCROOTS, LL_INFO1000, /* Part Two */ + "at" FMT_ADDR "as ", DBG_ADDR(pObjRef) )); + + VALIDATE_ROOT((gcFlags & GC_CALL_INTERIOR), hCallBack, pObjRef); + + LOG((LF_GCROOTS, LL_INFO1000, /* Part Three */ + LOG_PIPTR_OBJECT_CLASS(OBJECTREF_TO_UNCHECKED_OBJECTREF(*pObjRef), (gcFlags & GC_CALL_PINNED), (gcFlags & GC_CALL_INTERIOR)))); +#endif //_DEBUG + + gcFlags |= CHECK_APP_DOMAIN; + + pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(regNum, 0, false))); +} + +#else // Unknown platform + +OBJECTREF* GcInfoDecoder::GetRegisterSlot( + int regNum, + PREGDISPLAY pRD + ) +{ + PORTABILITY_ASSERT("GcInfoDecoder::GetRegisterSlot"); + return NULL; +} + +bool GcInfoDecoder::IsScratchRegister(int regNum, PREGDISPLAY pRD) +{ + PORTABILITY_ASSERT("GcInfoDecoder::IsScratchRegister"); + return false; +} + +bool GcInfoDecoder::IsScratchStackSlot(INT32 spOffset, GcStackSlotBase spBase, PREGDISPLAY pRD) +{ + _ASSERTE( !"NYI" ); + return false; +} + +void GcInfoDecoder::ReportRegisterToGC( + int regNum, + unsigned gcFlags, + PREGDISPLAY pRD, + unsigned flags, + GCEnumCallback pCallBack, + LPVOID hCallBack) +{ + _ASSERTE( !"NYI" ); +} + +#endif // Unknown platform + + +OBJECTREF* GcInfoDecoder::GetStackSlot( + INT32 spOffset, + GcStackSlotBase spBase, + PREGDISPLAY pRD + ) +{ +#ifdef CROSSGEN_COMPILE + _ASSERTE(!"GcInfoDecoder::GetStackSlot not supported in this build configuration"); + return NULL; +#else // CROSSGEN_COMPILE + OBJECTREF* pObjRef; + + if( GC_SP_REL == spBase ) + { + pObjRef = (OBJECTREF*) ((SIZE_T)GetRegdisplaySP(pRD) + spOffset); + } + else if( GC_CALLER_SP_REL == spBase ) + { + pObjRef = (OBJECTREF*) (GET_CALLER_SP(pRD) + spOffset); + } + else + { + _ASSERTE( GC_FRAMEREG_REL == spBase ); + _ASSERTE( NO_STACK_BASE_REGISTER != m_StackBaseRegister ); + + pObjRef = (OBJECTREF*)((*((SIZE_T*)(GetRegisterSlot( m_StackBaseRegister, pRD )))) + spOffset); + } + + return pObjRef; +#endif // CROSSGEN_COMPILE +} + +#ifdef DACCESS_COMPILE +int GcInfoDecoder::GetStackReg(int spBase) +{ +#if defined(_TARGET_AMD64_) + int esp = 4; +#elif defined(_TARGET_ARM_) + int esp = 13; +#elif defined(_TARGET_ARM64_) + _ASSERTE("ARM64:NYI"); + int esp = 30; +#endif + + if( GC_SP_REL == spBase ) + return esp; + else if ( GC_CALLER_SP_REL == spBase ) + return -(esp+1); + else + return m_StackBaseRegister; +} +#endif // DACCESS_COMPILE + +void GcInfoDecoder::ReportStackSlotToGC( + INT32 spOffset, + GcStackSlotBase spBase, + unsigned gcFlags, + PREGDISPLAY pRD, + unsigned flags, + GCEnumCallback pCallBack, + LPVOID hCallBack) +{ + GCINFODECODER_CONTRACT(CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END); + + OBJECTREF* pObjRef = GetStackSlot(spOffset, spBase, pRD); + _ASSERTE( IS_ALIGNED( pObjRef, sizeof( Object* ) ) ); + +#ifdef _DEBUG + LOG((LF_GCROOTS, LL_INFO1000, /* Part One */ + "Reporting %s" FMT_STK, + ( (GC_SP_REL == spBase) ? "" : + ((GC_CALLER_SP_REL == spBase) ? "caller's " : + ((GC_FRAMEREG_REL == spBase) ? "frame " : " "))), + DBG_STK(spOffset) )); + + LOG((LF_GCROOTS, LL_INFO1000, /* Part Two */ + "at" FMT_ADDR "as ", DBG_ADDR(pObjRef) )); + + VALIDATE_ROOT((gcFlags & GC_CALL_INTERIOR), hCallBack, pObjRef); + + LOG((LF_GCROOTS, LL_INFO1000, /* Part Three */ + LOG_PIPTR_OBJECT_CLASS(OBJECTREF_TO_UNCHECKED_OBJECTREF(*pObjRef), (gcFlags & GC_CALL_PINNED), (gcFlags & GC_CALL_INTERIOR)))); +#endif + + gcFlags |= CHECK_APP_DOMAIN; + + pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(GetStackReg(spBase), spOffset, true))); +} + + +#endif // USE_GC_INFO_DECODER + -- cgit v1.2.3