size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock, unsigned methodSize, bool verifyGCTables) { GCInfoToken gcInfoToken = { dac_cast<PTR_VOID>(gcInfoBlock), gcInfoVersion }; GcInfoDecoder hdrdecoder(gcInfoToken, (GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT | DECODE_GS_COOKIE | DECODE_CODE_LENGTH | DECODE_PSP_SYM | DECODE_VARARG | DECODE_GENERICS_INST_CONTEXT | DECODE_GC_LIFETIMES | DECODE_PROLOG_LENGTH | DECODE_RETURN_KIND), 0); if (NO_SECURITY_OBJECT != hdrdecoder.GetSecurityObjectStackSlot() || NO_GENERICS_INST_CONTEXT != hdrdecoder.GetGenericsInstContextStackSlot() || NO_GS_COOKIE == hdrdecoder.GetGSCookieStackSlot()) { gcPrintf("Prolog size: "); UINT32 prologSize = hdrdecoder.GetPrologSize(); gcPrintf("%d\n", prologSize); } gcPrintf("Security object: "); if (NO_SECURITY_OBJECT == hdrdecoder.GetSecurityObjectStackSlot()) { gcPrintf("<none>\n"); } else { INT32 ofs = hdrdecoder.GetSecurityObjectStackSlot(); char sign = '+'; if (ofs < 0) { sign = '-'; ofs = -ofs; } gcPrintf("caller.sp%c%x\n", sign, ofs); } gcPrintf("GS cookie: "); if (NO_GS_COOKIE == hdrdecoder.GetGSCookieStackSlot()) { gcPrintf("<none>\n"); } else { INT32 ofs = hdrdecoder.GetGSCookieStackSlot(); char sign = '+'; if (ofs < 0) { sign = '-'; ofs = -ofs; } gcPrintf("caller.sp%c%x\n", sign, ofs); UINT32 validRangeStart = hdrdecoder.GetGSCookieValidRangeStart(); UINT32 validRangeEnd = hdrdecoder.GetGSCookieValidRangeEnd(); gcPrintf("GS cookie valid range: [%x;%x)\n", validRangeStart, validRangeEnd); } gcPrintf("PSPSym: "); if (NO_PSP_SYM == hdrdecoder.GetPSPSymStackSlot()) { gcPrintf("<none>\n"); } else { INT32 ofs = hdrdecoder.GetPSPSymStackSlot(); char sign = '+'; if (ofs < 0) { sign = '-'; ofs = -ofs; } #ifdef _TARGET_AMD64_ // The PSPSym is relative to InitialSP on X64 and CallerSP on other platforms. gcPrintf("initial.sp%c%x\n", sign, ofs); #else gcPrintf("caller.sp%c%x\n", sign, ofs); #endif } gcPrintf("Generics inst context: "); if (NO_GENERICS_INST_CONTEXT == hdrdecoder.GetGenericsInstContextStackSlot()) { gcPrintf("<none>\n"); } else { INT32 ofs = hdrdecoder.GetGenericsInstContextStackSlot(); char sign = '+'; if (ofs < 0) { sign = '-'; ofs = -ofs; } gcPrintf("caller.sp%c%x\n", sign, ofs); } gcPrintf("PSP slot: "); if (NO_PSP_SYM == hdrdecoder.GetPSPSymStackSlot()) { gcPrintf("<none>\n"); } else { INT32 ofs = hdrdecoder.GetPSPSymStackSlot(); char sign = '+'; if (ofs < 0) { sign = '-'; ofs = -ofs; } gcPrintf("caller.sp%c%x\n", sign, ofs); } gcPrintf("GenericInst slot: "); if (NO_GENERICS_INST_CONTEXT == hdrdecoder.GetGenericsInstContextStackSlot()) { gcPrintf("<none>\n"); } else { INT32 ofs = hdrdecoder.GetGenericsInstContextStackSlot(); char sign = '+'; if (ofs < 0) { sign = '-'; ofs = -ofs; } gcPrintf("caller.sp%c%x ", sign, ofs); if (hdrdecoder.HasMethodDescGenericsInstContext()) gcPrintf("(GENERIC_PARAM_CONTEXT_METHODDESC)\n"); else if (hdrdecoder.HasMethodTableGenericsInstContext()) gcPrintf("(GENERIC_PARAM_CONTEXT_METHODHANDLE)\n"); else gcPrintf("(GENERIC_PARAM_CONTEXT_THIS)\n"); } gcPrintf("Varargs: %u\n", hdrdecoder.GetIsVarArg()); gcPrintf("Frame pointer: %s\n", NO_STACK_BASE_REGISTER == hdrdecoder.GetStackBaseRegister() ? "<none>" : GetRegName(hdrdecoder.GetStackBaseRegister())); gcPrintf("Wants Report Only Leaf: %u\n", hdrdecoder.WantsReportOnlyLeaf()); #ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA gcPrintf("Size of parameter area: %x\n", hdrdecoder.GetSizeOfStackParameterArea()); #endif ReturnKind returnKind = hdrdecoder.GetReturnKind(); gcPrintf("Return Kind: %s\n", ReturnKindToString(returnKind)); UINT32 cbEncodedMethodSize = hdrdecoder.GetCodeLength(); gcPrintf("Code size: %x\n", cbEncodedMethodSize); GcInfoDumper dumper(gcInfoToken); GcInfoDumpState state; state.LastCodeOffset = -1; state.fAnythingPrinted = FALSE; state.fSafePoint = FALSE; state.FrameRegister = hdrdecoder.GetStackBaseRegister(); state.pfnPrintf = gcPrintf; GcInfoDumper::EnumerateStateChangesResults result = dumper.EnumerateStateChanges( &InterruptibleStateChangeCallback, &RegisterStateChangeCallback, &StackSlotStateChangeCallback, &SafePointCallback, &state); if (state.fAnythingPrinted) gcPrintf("\n"); switch (result) { case GcInfoDumper::SUCCESS: // do nothing break; case GcInfoDumper::OUT_OF_MEMORY: gcPrintf("out of memory\n"); break; case GcInfoDumper::REPORTED_REGISTER_IN_CALLERS_FRAME: gcPrintf("reported register in caller's frame\n"); break; case GcInfoDumper::REPORTED_FRAME_POINTER: gcPrintf("reported frame register\n"); break; case GcInfoDumper::REPORTED_INVALID_BASE_REGISTER: gcPrintf("reported pointer relative to wrong base register\n"); break; case GcInfoDumper::REPORTED_INVALID_POINTER: gcPrintf("reported invalid pointer\n"); break; case GcInfoDumper::DECODER_FAILED: gcPrintf("decoder failed\n"); break; default: gcPrintf("invalid GC info\n"); break; } return (result == GcInfoDumper::SUCCESS) ? dumper.GetGCInfoSize() : 0; }
GcInfoDumper::EnumerateStateChangesResults GcInfoDumper::EnumerateStateChanges ( InterruptibleStateChangeProc *pfnInterruptibleStateChange, RegisterStateChangeProc *pfnRegisterStateChange, StackSlotStateChangeProc *pfnStackSlotStateChange, OnSafePointProc *pfnSafePointFunc, PVOID pvData) { m_Error = SUCCESS; // // Save callback functions for use by helper functions // m_pfnRegisterStateChange = pfnRegisterStateChange; m_pfnStackSlotStateChange = pfnStackSlotStateChange; m_pvCallbackData = pvData; // // Decode header information // GcInfoDecoder hdrdecoder(m_pbGCInfo, (GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT | DECODE_CODE_LENGTH | DECODE_GC_LIFETIMES | DECODE_VARARG), 0); UINT32 cbEncodedMethodSize = hdrdecoder.GetCodeLength(); m_StackBaseRegister = hdrdecoder.GetStackBaseRegister(); // // Set up a bogus REGDISPLAY to pass to EnumerateLiveSlots. This will // allow us to later identify registers or stack offsets passed to the // callback. // REGDISPLAY regdisp; ZeroMemory(®disp, sizeof(regdisp)); regdisp.pContext = ®disp.ctxOne; regdisp.IsCallerContextValid = TRUE; regdisp.pCurrentContext = ®disp.ctxOne; regdisp.pCallerContext = ®disp.ctxTwo; #define NEXT_ADDRESS() (UniqueAddress += ADDRESS_SPACING) UINT iReg; #ifdef _WIN64 ULONG64 UniqueAddress = ADDRESS_SPACING*2; ULONG64 *pReg; #else DWORD UniqueAddress = ADDRESS_SPACING*2; DWORD *pReg; #endif #define FILL_REGS(start, count) \ do { \ for (iReg = 0, pReg = ®disp.start; iReg < count; iReg++, pReg++) \ { \ *pReg = NEXT_ADDRESS(); \ } \ } while (0) #ifdef _TARGET_AMD64_ FILL_REGS(pCurrentContext->Rax, 16); FILL_REGS(pCallerContext->Rax, 16); regdisp.pCurrentContextPointers = ®disp.ctxPtrsOne; regdisp.pCallerContextPointers = ®disp.ctxPtrsTwo; ULONGLONG **ppCurrentRax = ®disp.pCurrentContextPointers->Rax; ULONGLONG **ppCallerRax = ®disp.pCallerContextPointers ->Rax; for (iReg = 0; iReg < 16; iReg++) { *(ppCurrentRax + iReg) = ®disp.pCurrentContext->Rax + iReg; *(ppCallerRax + iReg) = ®disp.pCallerContext ->Rax + iReg; } #elif defined(_TARGET_ARM_) FILL_REGS(pCurrentContext->R0, 16); FILL_REGS(pCallerContext->R0, 16); regdisp.pCurrentContextPointers = ®disp.ctxPtrsOne; regdisp.pCallerContextPointers = ®disp.ctxPtrsTwo; ULONG **ppCurrentReg = ®disp.pCurrentContextPointers->R4; ULONG **ppCallerReg = ®disp.pCallerContextPointers->R4; for (iReg = 0; iReg < 8; iReg++) { *(ppCurrentReg + iReg) = ®disp.pCurrentContext->R4 + iReg; *(ppCallerReg + iReg) = ®disp.pCallerContext->R4 + iReg; } /// Set Lr *(ppCurrentReg + 8) = ®disp.pCurrentContext->R4 + 10; *(ppCallerReg + 8) = ®disp.pCallerContext->R4 + 10; ULONG **ppVolatileReg = ®disp.volatileCurrContextPointers.R0; for (iReg = 0; iReg < 4; iReg++) { *(ppVolatileReg+iReg) = ®disp.pCurrentContext->R0 + iReg; } /// Set R12 *(ppVolatileReg+4) = ®disp.pCurrentContext->R0+12; #endif #undef FILL_REGS #undef NEXT_ADDRESS SyncRegDisplayToCurrentContext(®disp); // // Enumerate pointers at every possible offset. // #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED GcInfoDecoder safePointDecoder(m_pbGCInfo, (GcInfoDecoderFlags)0, 0); #endif { GcInfoDecoder untrackedDecoder(m_pbGCInfo, DECODE_GC_LIFETIMES, 0); untrackedDecoder.EnumerateUntrackedSlots(®disp, 0, &LivePointerCallback, this); BOOL fStop = ReportPointerDifferences( -2, ®disp, NULL); FreePointerRecords(m_pRecords); m_pRecords = NULL; if (fStop || m_Error) return m_Error; } LivePointerRecord *pLastState = NULL; BOOL fPrevInterruptible = FALSE; for (UINT32 offset = 0; offset <= cbEncodedMethodSize; offset++) { BOOL fNewInterruptible = FALSE; GcInfoDecoder decoder1(m_pbGCInfo, (GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT | DECODE_CODE_LENGTH | DECODE_VARARG | DECODE_INTERRUPTIBILITY), offset); fNewInterruptible = decoder1.IsInterruptible(); if (fNewInterruptible != fPrevInterruptible) { if (pfnInterruptibleStateChange(offset, fNewInterruptible, pvData)) break; fPrevInterruptible = fNewInterruptible; } unsigned flags = ActiveStackFrame; #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED UINT32 safePointOffset = offset; #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM_) safePointOffset++; #endif if(safePointDecoder.IsSafePoint(safePointOffset)) { _ASSERTE(!fNewInterruptible); if (pfnSafePointFunc(offset, pvData)) break; flags = 0; } #endif GcInfoDecoder decoder2(m_pbGCInfo, (GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT | DECODE_CODE_LENGTH | DECODE_VARARG | DECODE_GC_LIFETIMES | DECODE_NO_VALIDATION), offset); _ASSERTE(!m_pRecords); if(!fNewInterruptible && (flags == ActiveStackFrame)) { // Decoding at non-interruptible offsets is only // valid in the ExecutionAborted case flags |= ExecutionAborted; } if (!decoder2.EnumerateLiveSlots( ®disp, true, flags | NoReportUntracked, &LivePointerCallback, this)) { m_Error = DECODER_FAILED; } if (m_Error) break; if (ReportPointerDifferences( offset, ®disp, pLastState)) { break; } if (m_Error) break; FreePointerRecords(pLastState); pLastState = m_pRecords; m_pRecords = NULL; size_t tempSize = decoder2.GetNumBytesRead(); if( m_gcInfoSize < tempSize ) m_gcInfoSize = tempSize; } FreePointerRecords(pLastState); FreePointerRecords(m_pRecords); m_pRecords = NULL; return m_Error; }