diff options
author | Jiyoung Yun <jy910.yun@samsung.com> | 2016-11-23 19:09:09 +0900 |
---|---|---|
committer | Jiyoung Yun <jy910.yun@samsung.com> | 2016-11-23 19:09:09 +0900 |
commit | 4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch) | |
tree | 98110734c91668dfdbb126fcc0e15ddbd93738ca /src/gcdump | |
parent | fa45f57ed55137c75ac870356a1b8f76c84b229c (diff) | |
download | coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2 coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip |
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/gcdump')
-rw-r--r-- | src/gcdump/.gitmirror | 1 | ||||
-rw-r--r-- | src/gcdump/gcdump.cpp | 80 | ||||
-rw-r--r-- | src/gcdump/gcdump.settings.targets | 24 | ||||
-rw-r--r-- | src/gcdump/gcdumpnonx86.cpp | 528 | ||||
-rw-r--r-- | src/gcdump/i386/.gitmirror | 1 | ||||
-rw-r--r-- | src/gcdump/i386/gcdumpx86.cpp | 1068 |
6 files changed, 1702 insertions, 0 deletions
diff --git a/src/gcdump/.gitmirror b/src/gcdump/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/gcdump/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/gcdump/gcdump.cpp b/src/gcdump/gcdump.cpp new file mode 100644 index 0000000000..1c512c88e0 --- /dev/null +++ b/src/gcdump/gcdump.cpp @@ -0,0 +1,80 @@ +// 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. +/***************************************************************************** + * GCDump.cpp + * + * Defines functions to display the GCInfo as defined by the GC-encoding + * spec. The GC information may be either dynamically created by a + * Just-In-Time compiler conforming to the standard code-manager spec, + * or may be persisted by a managed native code compiler conforming + * to the standard code-manager spec. + */ + +#include "utilcode.h" // For _ASSERTE() +#include "gcdump.h" + +/*****************************************************************************/ + + + +GCDump::GCDump(UINT32 gcInfoVer, bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs) + : gcInfoVersion (gcInfoVer), + fDumpEncBytes (encBytes ), + cMaxEncBytes (maxEncBytes ), + fDumpCodeOffsets(dumpCodeOffs) +{ + // By default, use the standard printf function to dump + GCDump::gcPrintf = (printfFtn) ::printf; +} + +/***************************************************************************** + * + * Display the byte encodings for the given range of the GC tables. + */ + +PTR_CBYTE GCDump::DumpEncoding(PTR_CBYTE gcInfoBlock, int cDumpBytes) +{ + _ASSERTE((cDumpBytes >= 0) && (cMaxEncBytes < 256)); + + if (fDumpEncBytes) + { + PTR_CBYTE pCurPos; + unsigned count; + int cBytesLeft; + + for (count = cMaxEncBytes, cBytesLeft = cDumpBytes, pCurPos = gcInfoBlock; + count > 0; + count--, pCurPos++, cBytesLeft--) + { + if (cBytesLeft > 0) + { + if (cBytesLeft > 1 && count == 1) + gcPrintf("..."); + else + gcPrintf("%02X ", *pCurPos); + } + else + gcPrintf(" "); + } + + gcPrintf("| "); + } + + return gcInfoBlock + cDumpBytes; +} + +/*****************************************************************************/ + +void GCDump::DumpOffset(unsigned o) +{ + gcPrintf("%04X", o); +} + +void GCDump::DumpOffsetEx(unsigned o) +{ + if (fDumpCodeOffsets) + DumpOffset(o); +} + +/*****************************************************************************/ diff --git a/src/gcdump/gcdump.settings.targets b/src/gcdump/gcdump.settings.targets new file mode 100644 index 0000000000..9a44330a0d --- /dev/null +++ b/src/gcdump/gcdump.settings.targets @@ -0,0 +1,24 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <!--*****************************************************--> + <!--This MSBuild project file was automatically generated--> + <!--from the original SOURCES/DIRS file by the KBC tool.--> + <!--*****************************************************--> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + <!--Leaf project Properties--> + <PropertyGroup> + <UserIncludes>$(UserIncludes); + .</UserIncludes> + <OutputPath>$(ClrLibDest)</OutputPath> + <TargetType>LIBRARY</TargetType> + <ClAdditionalOptions Condition="'$(USE_ICECAP)' != ''">$(ClAdditionalOptions) -D_ICECAP</ClAdditionalOptions> + </PropertyGroup> + <!--Leaf Project Items--> + <ItemGroup> + <CppCompile Include="..\GCDump.cpp" /> + <CppCompile Include="..\i386\GCDumpX86.cpp" Condition="'$(RealBuildArchitecture)' == 'x86'" /> + <CppCompile Include="..\rotor_x86\GCDumpX86.cpp" Condition="'$(_BuildArch)' == 'rotor_x86'" /> + </ItemGroup> + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> diff --git a/src/gcdump/gcdumpnonx86.cpp b/src/gcdump/gcdumpnonx86.cpp new file mode 100644 index 0000000000..7343ac9771 --- /dev/null +++ b/src/gcdump/gcdumpnonx86.cpp @@ -0,0 +1,528 @@ +// 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. +/***************************************************************************** + * GCDumpNonX86.cpp + */ + +#include "gcdump.h" + +#define LIMITED_METHOD_CONTRACT ((void)0) +#define WRAPPER_NO_CONTRACT ((void)0) + +#define GCINFODECODER_NO_EE +#include "gcinfodecoder.h" +#include "gcinfodumper.h" + + +PCSTR GetRegName (UINT32 regnum) +{ +#ifdef _TARGET_AMD64_ + + switch (regnum) + { + case 0: return "rax"; + case 1: return "rcx"; + case 2: return "rdx"; + case 3: return "rbx"; + case 4: return "rsp"; + case 5: return "rbp"; + case 6: return "rsi"; + case 7: return "rdi"; + case 8: return "r8"; + case 9: return "r9"; + case 10: return "r10"; + case 11: return "r11"; + case 12: return "r12"; + case 13: return "r13"; + case 14: return "r14"; + case 15: return "r15"; + } + + + return "???"; +#elif defined(_TARGET_ARM64_) + + static CHAR szRegName[16]; + if (regnum < 29) + { + _snprintf_s(szRegName, _countof(szRegName), sizeof(szRegName), "X%u", regnum); + return szRegName; + } + else if(regnum == 29) + { + return "Fp"; + } + else if(regnum == 30) + { + return "Lr"; + } + else if(regnum == 31) + { + return "Sp"; + } + + return "???"; +#elif defined(_TARGET_ARM_) + if (regnum > 128) + return "???"; + + static CHAR szRegName[16]; + _snprintf_s(szRegName, _countof(szRegName), sizeof(szRegName), "r%u", regnum); + return szRegName; + +#endif +} + + +/*****************************************************************************/ + + +GCDump::GCDump(UINT32 gcInfoVer, bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs) + : gcInfoVersion(gcInfoVer), + fDumpEncBytes (encBytes ), + cMaxEncBytes (maxEncBytes ), + fDumpCodeOffsets(dumpCodeOffs) +{ +} + + +/*****************************************************************************/ + + +struct GcInfoDumpState +{ + UINT32 LastCodeOffset; + BOOL fAnythingPrinted; + BOOL fSafePoint; + UINT32 FrameRegister; + GCDump::printfFtn pfnPrintf; +}; + + +BOOL InterruptibleStateChangeCallback ( + UINT32 CodeOffset, + BOOL fInterruptible, + PVOID pvData) +{ + GcInfoDumpState *pState = (GcInfoDumpState*)pvData; + + if (pState->fAnythingPrinted) + { + pState->pfnPrintf("\n"); + pState->fAnythingPrinted = FALSE; + pState->fSafePoint = FALSE; + } + + pState->pfnPrintf("%08x%s interruptible\n", CodeOffset, fInterruptible ? "" : " not"); + + pState->LastCodeOffset = -1; + + return FALSE; +} + +BOOL SafePointCallback ( + UINT32 CodeOffset, + PVOID pvData) +{ + GcInfoDumpState *pState = (GcInfoDumpState*)pvData; + + if (pState->fAnythingPrinted) + { + pState->pfnPrintf("\n"); + } + + pState->pfnPrintf("%08x is a safepoint: ", CodeOffset); + + pState->LastCodeOffset = CodeOffset; + pState->fAnythingPrinted = TRUE; + pState->fSafePoint = TRUE; + + return FALSE; +} + + +VOID PrintFlags (GCDump::printfFtn pfnPrintf, GcSlotFlags Flags) +{ + if (Flags & GC_SLOT_PINNED) + pfnPrintf("(pinned)"); + + if (Flags & GC_SLOT_INTERIOR) + pfnPrintf("(interior)"); + + if (Flags & GC_SLOT_UNTRACKED) + pfnPrintf("(untracked)"); +} + + +BOOL RegisterStateChangeCallback ( + UINT32 CodeOffset, + UINT32 RegisterNumber, + GcSlotFlags Flags, + GcSlotState NewState, + PVOID pvData) +{ + GcInfoDumpState *pState = (GcInfoDumpState*)pvData; + + if (pState->fSafePoint && (GC_SLOT_LIVE != NewState)) + { + // Don't print deaths for safepoints + return FALSE; + } + + if (pState->LastCodeOffset != CodeOffset) + { + if (pState->fAnythingPrinted) + pState->pfnPrintf("\n"); + + pState->pfnPrintf("%08x", CodeOffset); + + pState->LastCodeOffset = CodeOffset; + } + + char delta = ((GC_SLOT_LIVE == NewState) ? '+' : '-'); + + pState->pfnPrintf(" %c%s", delta, GetRegName(RegisterNumber)); + + PrintFlags(pState->pfnPrintf, Flags); + + pState->fAnythingPrinted = TRUE; + + return FALSE; +} + + +BOOL StackSlotStateChangeCallback ( + UINT32 CodeOffset, + GcSlotFlags flags, + GcStackSlotBase BaseRegister, + SSIZE_T StackOffset, + GcSlotState NewState, + PVOID pvData) +{ + GcInfoDumpState *pState = (GcInfoDumpState*)pvData; + + if (pState->fSafePoint && (GC_SLOT_LIVE != NewState)) + { + // Don't print deaths for safepoints + return FALSE; + } + + if (pState->LastCodeOffset != CodeOffset) + { + if (pState->fAnythingPrinted) + pState->pfnPrintf("\n"); + + if ((CodeOffset == -2) && !pState->fAnythingPrinted) + pState->pfnPrintf("Untracked:"); + else + pState->pfnPrintf("%08x", CodeOffset); + + pState->LastCodeOffset = CodeOffset; + } + + char delta = ((GC_SLOT_LIVE == NewState) ? '+' : '-'); + + CHAR sign = '+'; + + // the dumper's call back (in GcInfoDumper.cpp) has to "guess" the base register + // for stack slots it usually guesses it wrong ...... + // We try to filter out at least the non-sensical combinations + // - negative offset relative to SP + // - positive offset relative to CALLER_SP + + if (StackOffset < 0) + { + StackOffset = -StackOffset; + sign = '-'; +#ifndef GCINFODUMPER_IS_FIXED + if (BaseRegister == GC_SP_REL) + { // negative offset to SP???? + BaseRegister = GC_CALLER_SP_REL; + } +#endif // !GCINFODUMPER_IS_FIXED + } +#ifndef GCINFODUMPER_IS_FIXED + else if (BaseRegister == GC_CALLER_SP_REL) + { // positive offset to Caller_SP???? + BaseRegister = GC_SP_REL; + } +#endif // !GCINFODUMPER_IS_FIXED + + + + PCSTR pszBaseReg; + + switch (BaseRegister) + { + case GC_CALLER_SP_REL: pszBaseReg = "caller.sp"; break; + case GC_SP_REL: pszBaseReg = "sp"; break; + case GC_FRAMEREG_REL: pszBaseReg = GetRegName(pState->FrameRegister); break; + default: pszBaseReg = "???"; break; + } + + pState->pfnPrintf(" %c%s%c%x", delta, pszBaseReg, sign, StackOffset); + + PrintFlags(pState->pfnPrintf, flags); + + pState->fAnythingPrinted = TRUE; + + return FALSE; +} + + +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; +} + + +/*****************************************************************************/ + +void GCDump::DumpPtrsInFrame(PTR_CBYTE infoBlock, + PTR_CBYTE codeBlock, + unsigned offs, + bool verifyGCTables) +{ + _ASSERTE(!"NYI"); +} + + +#define _common_h_ + +#ifndef LOG +#define LOG(x) ((void)0) +#endif + +#define GCINFODECODER_CONTRACT(contract) +#define GET_CALLER_SP(pREGDISPLAY) ((size_t)GetSP(pREGDISPLAY->pCallerContext)) +#define VALIDATE_OBJECTREF(objref, fDeep) ((void)0) +#define VALIDATE_ROOT(isInterior, hCallBack, pObjRef) ((void)0) +#include "../vm/gcinfodecoder.cpp" +#include "../gcinfo/gcinfodumper.cpp" diff --git a/src/gcdump/i386/.gitmirror b/src/gcdump/i386/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/gcdump/i386/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/gcdump/i386/gcdumpx86.cpp b/src/gcdump/i386/gcdumpx86.cpp new file mode 100644 index 0000000000..70334dee65 --- /dev/null +++ b/src/gcdump/i386/gcdumpx86.cpp @@ -0,0 +1,1068 @@ +// 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. +/***************************************************************************** + * GCDumpX86.cpp + */ + +/*****************************************************************************/ +#ifdef _TARGET_X86_ +/*****************************************************************************/ + +#include "utilcode.h" // For _ASSERTE() +#include "gcdump.h" + + +/*****************************************************************************/ + +#define castto(var,typ) (*(typ *)&var) + +#define sizeto(typ,mem) (offsetof(typ, mem) + sizeof(((typ*)0)->mem)) + +#define CALLEE_SAVED_REG_MAXSZ (4*sizeof(int)) // EBX,ESI,EDI,EBP + +/*****************************************************************************/ + +const char * RegName(unsigned reg) +{ + static const char * const regNames[] = + { + "EAX", + "ECX", + "EDX", + "EBX", + + "ESP", + "EBP", + "ESI", + "EDI" + }; + + _ASSERTE(reg < (sizeof(regNames)/sizeof(regNames[0]))); + + return regNames[reg]; +} + +const char * CalleeSavedRegName(unsigned reg) +{ + static const char * const regNames[] = + { + "EDI", + "ESI", + "EBX", + "EBP" + }; + + _ASSERTE(reg < (sizeof(regNames)/sizeof(regNames[0]))); + + return regNames[reg]; +} + +/*****************************************************************************/ + +unsigned GCDump::DumpInfoHdr (PTR_CBYTE table, + InfoHdr* header, + unsigned * methodSize, + bool verifyGCTables) +{ + unsigned count; + PTR_CBYTE tableStart = table; + PTR_CBYTE bp = table; + + if (verifyGCTables) + _ASSERTE(*castto(table, unsigned short *)++ == 0xFEEF); + + /* Get the method size */ + + table += decodeUnsigned(table, methodSize); + + table = decodeHeader(table, header); + + BOOL hasArgTabOffset = FALSE; + if (header->untrackedCnt == HAS_UNTRACKED) + { + hasArgTabOffset = TRUE; + table += decodeUnsigned(table, &count); + header->untrackedCnt = count; + } + + if (header->varPtrTableSize == HAS_VARPTR) + { + hasArgTabOffset = TRUE; + table += decodeUnsigned(table, &count); + header->varPtrTableSize = count; + } + + if (header->gsCookieOffset == HAS_GS_COOKIE_OFFSET) + { + table += decodeUnsigned(table, &count); + header->gsCookieOffset = count; + } + + if (header->syncStartOffset == HAS_SYNC_OFFSET) + { + table += decodeUnsigned(table, &count); + header->syncStartOffset = count; + table += decodeUnsigned(table, &count); + header->syncEndOffset = count; + } + + // + // First print out all the basic information + // + + gcPrintf(" method size = %04X\n", *methodSize); + gcPrintf(" prolog size = %2u \n", header->prologSize); + gcPrintf(" epilog size = %2u \n", header->epilogSize); + gcPrintf(" epilog count = %2u \n", header->epilogCount); + gcPrintf(" epilog end = %s \n", header->epilogAtEnd ? "yes" : "no"); + + gcPrintf(" callee-saved regs = "); + if (header->ediSaved) gcPrintf("EDI "); + if (header->esiSaved) gcPrintf("ESI "); + if (header->ebxSaved) gcPrintf("EBX "); + if (header->ebpSaved) gcPrintf("EBP "); + gcPrintf("\n"); + + gcPrintf(" ebp frame = %s \n", header->ebpFrame ? "yes" : "no"); + gcPrintf(" fully interruptible= %s \n", header->interruptible ? "yes" : "no"); + gcPrintf(" double align = %s \n", header->doubleAlign ? "yes" : "no"); + gcPrintf(" arguments size = %2u DWORDs\n", header->argCount); + gcPrintf(" stack frame size = %2u DWORDs\n", header->frameSize); + gcPrintf(" untracked count = %2u \n", header->untrackedCnt); + gcPrintf(" var ptr tab count = %2u \n", header->varPtrTableSize); + + // + // Now display optional information + // + + if (header->security) gcPrintf(" security check obj = yes\n"); + if (header->handlers) gcPrintf(" exception handlers = yes\n"); + if (header->localloc) gcPrintf(" localloc = yes\n"); + if (header->editNcontinue) gcPrintf(" edit & continue = yes\n"); + if (header->profCallbacks) gcPrintf(" profiler callbacks = yes\n"); + if (header->varargs) gcPrintf(" varargs = yes\n"); + if (header->gsCookieOffset != INVALID_GS_COOKIE_OFFSET) + gcPrintf(" GuardStack cookie = [%s%u]\n", + header->ebpFrame ? "EBP-" : "ESP+", header->gsCookieOffset); + if (header->syncStartOffset != INVALID_SYNC_OFFSET) + gcPrintf(" Sync region = [%u,%u]\n", + header->syncStartOffset, header->syncEndOffset); + + if (header->epilogCount > 1 || (header->epilogCount != 0 && + header->epilogAtEnd == 0)) + { + if (verifyGCTables) + _ASSERTE(*castto(table, unsigned short *)++ == 0xFACE); + + unsigned offs = 0; + + for (unsigned i = 0; i < header->epilogCount; i++) + { + table += decodeUDelta(table, &offs, offs); + gcPrintf(" epilog #%2u at %04X\n", i, offs); + } + } + else + { + if (header->epilogCount) + gcPrintf(" epilog at %04X\n", (*methodSize - header->epilogSize)); + } + + if (hasArgTabOffset) + { + unsigned argTabOffset; + table += decodeUnsigned(table, &argTabOffset); + gcPrintf(" argTabOffset = %x \n", argTabOffset); + } + + { + unsigned cur = 0; + unsigned last = table-bp; + while (cur < last) + { + unsigned amount = last - cur; + if (amount>5) + amount = 5; + + DumpEncoding(bp+cur, amount); + gcPrintf("\n"); + + cur += amount; + } + } + + return (table - tableStart); +} + +/*****************************************************************************/ + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +size_t GCDump::DumpGCTable(PTR_CBYTE table, + const InfoHdr& header, + unsigned methodSize, + bool verifyGCTables) +{ + int sz; + PTR_CBYTE tableStart = table; + PTR_CBYTE bp; + + unsigned count; + unsigned curOffs; + +//#if!TGT_x86 +// _ASSERTE(!"NYI"); +//#endif + + if (verifyGCTables) + _ASSERTE(*castto(table, unsigned short *)++ == 0xBEEF); + + unsigned calleeSavedRegs = 0; + if (header.doubleAlign) + { + calleeSavedRegs = 0; + if (header.ediSaved) calleeSavedRegs++; + if (header.esiSaved) calleeSavedRegs++; + if (header.ebxSaved) calleeSavedRegs++; + } + + /* Dump the untracked frame variable table */ + + count = header.untrackedCnt; + + int lastStkOffs = 0; + while (count-- > 0) + { + int stkOffsDelta; + unsigned lowBits; + + char reg = header.ebpFrame ? 'B' : 'S'; + + sz = (unsigned int)decodeSigned(table, &stkOffsDelta); + int stkOffs = lastStkOffs - stkOffsDelta; + lastStkOffs = stkOffs; + + table = DumpEncoding(table, sz); + + _ASSERTE(0 == ~OFFSET_MASK % sizeof(void*)); + + lowBits = OFFSET_MASK & stkOffs; + stkOffs &= ~OFFSET_MASK; + + assert(!header.doubleAlign || stkOffs >= 0); + + if (header.doubleAlign && + unsigned(stkOffs) >= sizeof(int)*(header.frameSize+calleeSavedRegs)) + { + reg = 'B'; + stkOffs -= sizeof(int)*(header.frameSize+calleeSavedRegs); + _ASSERTE(stkOffs >= (int) (2*sizeof(int))); + } + + if (stkOffs < 0) + gcPrintf(" [E%cP-%02XH] ", reg, -stkOffs); + else + gcPrintf(" [E%cP+%02XH] ", reg, +stkOffs); + + gcPrintf("an untracked %s%s local\n", + (lowBits & pinned_OFFSET_FLAG) ? "pinned " : "", + (lowBits & byref_OFFSET_FLAG) ? "byref" : "" + ); + } + + if (verifyGCTables) + _ASSERTE(*castto(table, unsigned short *)++ == 0xCAFE); + + /* Dump the frame variable lifetime table */ + + count = header.varPtrTableSize; + curOffs = 0; + + while (count-- > 0) + { + unsigned varOffs; + unsigned begOffs; + unsigned endOffs; + unsigned lowBits; + + bp = table; + + table += decodeUnsigned(table, &varOffs); + table += decodeUDelta (table, &begOffs, curOffs); + table += decodeUDelta (table, &endOffs, begOffs); + + DumpEncoding(bp, table-bp); + + _ASSERTE(0 == ~OFFSET_MASK % sizeof(void*)); + + lowBits = varOffs & 0x3; + varOffs &= ~OFFSET_MASK; + + // [EBP+0] is the return address - cant be a var + _ASSERTE(!header.ebpFrame || varOffs); + + curOffs = begOffs; + + DumpOffset(begOffs); + gcPrintf(".."); + DumpOffset(endOffs); + gcPrintf(" [E%s%02XH] a ", header.ebpFrame ? "BP-" : "SP+", + varOffs); + + gcPrintf("%s%s pointer\n", + (lowBits & byref_OFFSET_FLAG) ? "byref " : "", + (lowBits & this_OFFSET_FLAG) ? "this" : "" + ); + + _ASSERTE(endOffs <= methodSize); + } + + if (verifyGCTables) + _ASSERTE(*castto(table, unsigned short *)++ == 0xBABE); + + /* Dump the pointer table */ + + curOffs = 0; + bp = table; + + if (header.interruptible) + { + // + // Dump the Fully Interruptible pointer table + // + unsigned argCnt = 0; + bool isThis = false; + bool iptr = false; + + for (;;) + { + unsigned isPop; + unsigned argOffs; + unsigned val = *table++; + + _ASSERTE(curOffs <= methodSize); + + if (!(val & 0x80)) + { + /* A small 'regPtr' encoding */ + + curOffs += val & 0x7; + + DumpEncoding(bp, table-bp); bp = table; + DumpOffsetEx(curOffs); + gcPrintf(" reg %s becoming %s", RegName(((val >> 3) & 7)), + (val & 0x40) ? "live" + : "dead"); + if (isThis) + gcPrintf(" 'this'"); + if (iptr) + gcPrintf(" (iptr)"); + gcPrintf("\n"); + + isThis = false; + iptr = false; + continue; + } + + /* This is probably an argument push/pop */ + + argOffs = (val & 0x38) >> 3; + + /* 6 [110] and 7 [111] are reserved for other encodings */ + + if (argOffs < 6) + { + /* A small argument encoding */ + + curOffs += (val & 0x07); + isPop = (val & 0x40); + + ARG: + + if (isPop) + { + // A Pop of 0, means little-delta + + if (argOffs != 0) + { + _ASSERTE(header.ebpFrame || argOffs <= argCnt); + + DumpEncoding(bp, table-bp); bp = table; + DumpOffsetEx(curOffs); + + gcPrintf(" pop %2d ", argOffs); + if (!header.ebpFrame) + { + argCnt -= argOffs; + gcPrintf("args (%d)", argCnt); + } + else + gcPrintf("ptrs"); + + gcPrintf("\n"); + } + } + else + { + _ASSERTE(header.ebpFrame || argOffs >= argCnt); + + DumpEncoding(bp, table-bp); bp = table; + DumpOffsetEx(curOffs); + + gcPrintf(" push ptr %2d", argOffs); + if (!header.ebpFrame) + { + argCnt = argOffs+1; + gcPrintf(" (%d)", argCnt); + } + if (isThis) + gcPrintf(" 'this'"); + if (iptr) + gcPrintf(" (iptr)"); + gcPrintf("\n"); + + isThis = false; + iptr = false; + } + + continue; + } + else if (argOffs == 6) + { + if (val & 0x40) + { + /* Bigger delta 000=8,001=16,010=24,...,111=64 */ + + curOffs += (((val & 0x07) + 1) << 3); + } + else + { + /* non-ptr arg push */ + + curOffs += (val & 0x07); + _ASSERTE(!header.ebpFrame); + argCnt++; + + DumpEncoding(bp, table-bp); bp = table; + DumpOffsetEx(curOffs); + + gcPrintf(" push non-ptr (%d)\n", argCnt); + } + + continue; + } + + /* argOffs was 7 [111] which is reserved for the larger encodings */ + + _ASSERTE(argOffs==7); + + switch (val) + { + case 0xFF: + goto DONE_REGTAB; + + case 0xBC: + isThis = true; + break; + + case 0xBF: + iptr = true; + break; + + case 0xB8: + table += decodeUnsigned(table, &val); + curOffs += val; + break; + + case 0xF8: + case 0xFC: + isPop = val & 0x04; + table += decodeUnsigned(table, &argOffs); + goto ARG; + + case 0xFD: + table += decodeUnsigned(table, &argOffs); + assert(argOffs); + + DumpEncoding(bp, table-bp); bp = table; + DumpOffsetEx(curOffs); + + gcPrintf(" kill args %2d\n", argOffs); + break; + + case 0xF9: + table += decodeUnsigned(table, &argOffs); + argCnt += argOffs; + break; + + default: + gcPrintf("Unexpected special code %04X\n", val); + _ASSERTE(!""); + } + } + } + else if (header.ebpFrame) // interruptible is false + { + // + // Dump the Partially Interruptible, EBP-frame method, pointer table + // + + for (;;) + { + unsigned argMask = 0, byrefArgMask = 0; + unsigned regMask, byrefRegMask = 0; + + unsigned argCnt = 0; + PTR_CBYTE argTab = NULL; + unsigned argTabSize; + + unsigned val, nxt; + + /* Get the next byte and check for a 'special' entry */ + + unsigned encType = *table++; + + _ASSERTE(curOffs <= methodSize); + + switch (encType) + { + + default: + + /* A tiny or small call entry */ + + val = encType; + + if ((val & 0x80) == 0x00) + { + if (val & 0x0F) + { + /* A tiny call entry */ + + curOffs += (val & 0x0F); + regMask = (val & 0x70) >> 4; + argMask = 0; + } + else + { + DumpEncoding(bp, table-bp); bp = table; + + gcPrintf(" thisptr in "); + if (val & 0x10) + gcPrintf("EDI\n"); + else if (val & 0x20) + gcPrintf("ESI\n"); + else if (val & 0x40) + gcPrintf("EBX\n"); + else + _ASSERTE(!"Reserved GC encoding"); + + continue; + } + } + else + { + /* A small call entry */ + + curOffs += (val & 0x7F); + val = *table++; + regMask = val >> 5; + argMask = val & 0x1F; + } + break; + + case 0xFD: // medium encoding + + argMask = *table++; + val = *table++; + argMask |= (val & 0xF0) << 4; + nxt = *table++; + curOffs += (val & 0x0F) + ((nxt & 0x1F) << 4); + regMask = nxt >> 5; // EBX,ESI,EDI + + break; + + case 0xF9: // medium encoding with byrefs + + curOffs += *table++; + val = *table++; + argMask = val & 0x1F; + regMask = val >> 5; + val = *table++; + byrefArgMask = val & 0x1F; + byrefRegMask = val >> 5; + + break; + + case 0xFE: // large encoding + case 0xFA: // large encoding with byrefs + + val = *table++; + regMask = val & 0x7; + byrefRegMask= val >> 4; + curOffs += *PTR_DWORD(table); table += sizeof(DWORD); + argMask = *PTR_DWORD(table); table += sizeof(DWORD); + if (encType == 0xFA) // read byrefArgMask + {byrefArgMask = *PTR_DWORD(table); table += sizeof(DWORD);} + + break; + + case 0xFB: // huge encoding + + val = *table++; + regMask = val & 0x7; + byrefRegMask= val >> 4; + curOffs = *PTR_DWORD(table); table += sizeof(DWORD); + argCnt = *PTR_DWORD(table); table += sizeof(DWORD); + argTabSize = *PTR_DWORD(table); table += sizeof(DWORD); + argTab = table; table += argTabSize; + + break; + + case 0xFF: + goto DONE_REGTAB; + } + + /* + Here we have the following values: + + curOffs ... the code offset of the call + regMask ... mask of live pointer register variables + argMask ... bitmask of pushed pointer arguments + byrefRegMask ... byref qualifier for regMask + byrefArgMask ... byrer qualifier for argMask + */ + + _ASSERTE((byrefArgMask & argMask) == byrefArgMask); + _ASSERTE((byrefRegMask & regMask) == byrefRegMask); + + DumpEncoding(bp, table-bp); bp = table; + DumpOffsetEx(curOffs); + + gcPrintf(" call [ "); + + if (regMask & 1) + gcPrintf("EDI%c", (byrefRegMask & 1) ? '\'' : ' '); + if (regMask & 2) + gcPrintf("ESI%c", (byrefRegMask & 2) ? '\'' : ' '); + if (regMask & 4) + gcPrintf("EBX%c", (byrefRegMask & 4) ? '\'' : ' '); + + if (!header.ebpFrame) + { + if (regMask & 8) + gcPrintf("EBP "); + } + + if (argCnt) + { + gcPrintf("] ptrArgs=["); + + do + { + argTab += decodeUnsigned(argTab, &val); + + assert((val & this_OFFSET_FLAG) == 0); + unsigned stkOffs = val & ~byref_OFFSET_FLAG; + unsigned lowBit = val & byref_OFFSET_FLAG; + + gcPrintf("%u%s", stkOffs, lowBit ? "i" : ""); + if (argCnt > 1) + gcPrintf(" "); + } + while (--argCnt); + assert(argTab == table); + + gcPrintf("]"); + } + else + { + gcPrintf("] argMask=%02X", argMask); + + if (byrefArgMask) gcPrintf(" (iargs=%02X)", byrefArgMask); + } + + gcPrintf("\n"); + } + } + else // interruptible is false, ebpFrame is false + { + // + // Dump the Partially Interruptible, EBP-less method, pointer table + // + unsigned lastSkip = 0; + unsigned imask = 0; + + for (;;) + { + unsigned val = *table++; + + _ASSERTE(curOffs <= methodSize); + + if (!(val & 0x80)) + { + if (!(val & 0x40)) + { + if (!(val & 0x20)) + { + // + // push 000DDDDD push one item, 5-bit delta + // + + curOffs += val & 0x1F; + + DumpEncoding(bp, table-bp); bp = table; + DumpOffsetEx(curOffs); + + gcPrintf(" push\n"); + } + else + { + // + // push 00100000 [pushCount] ESP push multiple items + // + + unsigned pushCount; + + assert(val == 0x20); + table += decodeUnsigned(table, &pushCount); + + DumpEncoding(bp, table-bp); bp = table; + DumpOffsetEx(curOffs); + + gcPrintf(" push %d\n", pushCount); + } + } + else + { + unsigned popSize; + unsigned skip; + + if ((val & 0x3f) == 0) + { + // + // skip 01000000 [Delta] Skip arbitrary sized delta + // + + table += decodeUnsigned(table, &skip); + curOffs += skip; + lastSkip = skip; + } + else + { + // + // pop 01CCDDDD pop CC items, 4-bit delta + // + + popSize = (val & 0x30) >> 4; + skip = val & 0x0f; + curOffs += skip; + + if (popSize > 0) + { + DumpEncoding(bp, table-bp); bp = table; + DumpOffsetEx(curOffs); + + gcPrintf(" pop %d\n", popSize); + } + else + lastSkip = skip; + } + } + } + else + { + unsigned callArgCnt; + unsigned callRegMask; + bool callPndTab = false; + unsigned callPndMask = 0; + unsigned callPndTabCnt = 0, callPndTabSize = 0; + + switch ((val & 0x70) >> 4) + { + default: + // + // call 1PPPPPPP Call Pattern, P=[0..79] + // + decodeCallPattern((val & 0x7f), &callArgCnt, &callRegMask, + &callPndMask, &lastSkip); + curOffs += lastSkip; + + PRINT_CALL: + + DumpEncoding(bp, table-bp); bp = table; + DumpOffsetEx(curOffs); + + gcPrintf(" call %d [ ", callArgCnt); + + unsigned iregMask, iargMask; + + iregMask = imask & 0xF; + iargMask = imask >> 4; + + assert((callRegMask & 0x0F) == callRegMask); + if (callRegMask & 1) + gcPrintf("EDI%c", (iregMask & 1) ? '\'' : ' '); + if (callRegMask & 2) + gcPrintf("ESI%c", (iregMask & 2) ? '\'' : ' '); + if (callRegMask & 4) + gcPrintf("EBX%c", (iregMask & 4) ? '\'' : ' '); + if (callRegMask & 8) + gcPrintf("EBP%c", (iregMask & 8) ? '\'' : ' '); + gcPrintf("]"); + + if (callPndTab) + { +#if defined(_DEBUG) && !defined(STRIKE) + // note: _ASSERTE is a no-op for strike + PTR_CBYTE offsStart = table; +#endif + gcPrintf(" argOffs(%d) =", callPndTabCnt); + for (unsigned i=0; i < callPndTabCnt; i++) + { + unsigned pndOffs; + table += decodeUnsigned(table, &pndOffs); + gcPrintf(" %4X", pndOffs); + } + _ASSERTE(offsStart + callPndTabSize == table); + bp = table; + } + else + { + if (callPndMask) + gcPrintf(" argMask=%02X", callPndMask); + if (iargMask) + gcPrintf(" (iargs=%02X)", iargMask); + } + gcPrintf("\n"); + + imask = lastSkip = 0; + break; + + case 5: + // + // call 1101RRRR DDCCCMMM Call RegMask=RRRR,ArgCnt=CCC, + // ArgMask=MMM Delta=commonDelta[DD] + // + callRegMask = val & 0xf; // EBP,EBX,ESI,EDI + val = *table++; + callPndMask = (val & 0x7); + callArgCnt = (val >> 3) & 0x7; + lastSkip = callCommonDelta[val>>6]; + curOffs += lastSkip; + + goto PRINT_CALL; + + case 6: + // + // call 1110RRRR [ArgCnt] [ArgMask] + // Call ArgCnt,RegMask=RRR,ArgMask + // + callRegMask = val & 0xf; // EBP,EBX,ESI,EDI + table += decodeUnsigned(table, &callArgCnt); + table += decodeUnsigned(table, &callPndMask); + goto PRINT_CALL; + + case 7: + switch (val & 0x0C) + { + case 0x00: + assert(val == 0xF0); + /* iptr 11110000 [IPtrMask] Arbitrary Interior Pointer Mask */ + table += decodeUnsigned(table, &imask); + DumpEncoding(bp, table-bp); bp = table; + gcPrintf(" iptrMask = %02X\n", imask); + break; + + case 0x04: + DumpEncoding(bp, table-bp); bp = table; + gcPrintf(" thisptr in %s\n", CalleeSavedRegName(val&0x3)); + break; + + case 0x08: + val = *table++; + callRegMask = val & 0xF; + imask = val >> 4; + lastSkip = *PTR_DWORD(table); table += sizeof(DWORD); + curOffs += lastSkip; + callArgCnt = *PTR_DWORD(table); table += sizeof(DWORD); + callPndTabCnt = *PTR_DWORD(table); table += sizeof(DWORD); + callPndTabSize = *PTR_DWORD(table); table += sizeof(DWORD); + callPndTab = true; + goto PRINT_CALL; + + case 0x0C: + assert(val==0xff); + goto DONE_REGTAB; + break; + + default: + _ASSERTE(!"reserved GC encoding"); + break; + } + break; + } + } + } + } + +DONE_REGTAB: + + _ASSERTE(curOffs <= methodSize); + + if (verifyGCTables) + _ASSERTE(*castto(table, unsigned short *)++ == 0xBEEB); + + _ASSERTE(table > bp); + + DumpEncoding(bp, table-bp); +// gcPrintf(" "); + gcPrintf("\n"); + + return (table - tableStart); +} +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + + +/*****************************************************************************/ + +void GCDump::DumpPtrsInFrame(PTR_CBYTE infoBlock, + PTR_CBYTE codeBlock, + unsigned offs, + bool verifyGCTables) +{ + PTR_CBYTE table = infoBlock; + + size_t methodSize; + size_t stackSize; + size_t prologSize; + size_t epilogSize; + unsigned epilogCnt; + BOOL epilogEnd; + size_t argSize; + BOOL secCheck; + BOOL dblAlign; + + if (verifyGCTables) + _ASSERTE(*castto(table, unsigned short *)++ == 0xFEEF); + + /* Get hold of the method size */ + + unsigned int methodSizeTemp; + table += decodeUnsigned(table, &methodSizeTemp); + methodSize = methodSizeTemp; + + // + // New style InfoBlk Header + // + // Typically only uses one-byte to store everything. + // + InfoHdr header; + table = decodeHeader(table, &header); + + if (header.untrackedCnt == HAS_UNTRACKED) + { + unsigned count; + table += decodeUnsigned(table, &count); + header.untrackedCnt = count; + } + if (header.varPtrTableSize == HAS_VARPTR) + { + unsigned count; + table += decodeUnsigned(table, &count); + header.varPtrTableSize = count; + } + if (header.gsCookieOffset == HAS_GS_COOKIE_OFFSET) + { + unsigned offset; + table += decodeUnsigned(table, &offset); + header.gsCookieOffset = offset; + _ASSERTE(offset != INVALID_GS_COOKIE_OFFSET); + } + if (header.syncStartOffset == HAS_SYNC_OFFSET) + { + unsigned offset; + table += decodeUnsigned(table, &offset); + header.syncStartOffset = offset; + _ASSERTE(offset != INVALID_SYNC_OFFSET); + table += decodeUnsigned(table, &offset); + header.syncEndOffset = offset; + _ASSERTE(offset != INVALID_SYNC_OFFSET); + } + + prologSize = header.prologSize; + epilogSize = header.epilogSize; + epilogCnt = header.epilogCount; + epilogEnd = header.epilogAtEnd; + secCheck = header.security; + dblAlign = header.doubleAlign; + argSize = header.argCount * 4; + stackSize = header.frameSize; + +#ifdef DEBUG + if (offs == 0) + { + gcPrintf(" method size = %04X\n", methodSize); + gcPrintf(" stack frame size = %3u \n", stackSize); + gcPrintf(" prolog size = %3u \n", prologSize); + gcPrintf(" epilog size = %3u \n", epilogSize); + gcPrintf(" epilog end = %s \n", epilogEnd ? "yes" : "no"); + gcPrintf(" epilog count = %3u \n", epilogCnt ); + gcPrintf(" security = %s \n", secCheck ? "yes" : "no"); + gcPrintf(" dblAlign = %s \n", dblAlign ? "yes" : "no"); + gcPrintf(" untracked count = %3u \n", header.untrackedCnt); + gcPrintf(" var ptr tab count= %3u \n", header.varPtrTableSize); + gcPrintf("\n"); + } +#endif + + /* Are we within the prolog of the method? */ + + if (offs < prologSize) + { + gcPrintf(" Offset %04X is within the method's prolog\n", offs); + return; + } + + /* Are we within an epilog of the method? */ + + if (epilogCnt) + { + unsigned eps; + + if (epilogCnt > 1 || !epilogEnd) + { + if (verifyGCTables) + _ASSERTE(*castto(table, unsigned short *)++ == 0xFACE); + + unsigned prevEps = 0; + for (unsigned i = 0; i < epilogCnt; i++) + { + table += decodeUDelta(table, &eps, prevEps); + + if ((offs >= eps) && (offs < eps + epilogSize)) + goto EPILOG_MSG; + } + } + else + { + eps = (int)(methodSize - epilogSize); + if ((offs >= eps) && (offs < eps + epilogSize)) + { +EPILOG_MSG: gcPrintf(" Offset %04X is within the method's epilog" + " (%02X bytes into it)\n", offs, offs - eps); + return; + } + } + } + gcPrintf(" Offset %04X is within the method's body\n", offs); +} + +/*****************************************************************************/ +#endif // _TARGET_X86_ +/*****************************************************************************/ |