1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 /*****************************************************************************
8 /*****************************************************************************/
10 /*****************************************************************************/
12 #include "utilcode.h" // For _ASSERTE()
16 /*****************************************************************************/
18 #define castto(var,typ) (*(typ *)&var)
20 #define sizeto(typ,mem) (offsetof(typ, mem) + sizeof(((typ*)0)->mem))
22 #define CALLEE_SAVED_REG_MAXSZ (4*sizeof(int)) // EBX,ESI,EDI,EBP
24 /*****************************************************************************/
26 const char * RegName(unsigned reg)
28 static const char * const regNames[] =
41 _ASSERTE(reg < (sizeof(regNames)/sizeof(regNames[0])));
46 const char * CalleeSavedRegName(unsigned reg)
48 static const char * const regNames[] =
56 _ASSERTE(reg < (sizeof(regNames)/sizeof(regNames[0])));
61 /*****************************************************************************/
63 unsigned GCDump::DumpInfoHdr (PTR_CBYTE table,
65 unsigned * methodSize,
69 PTR_CBYTE tableStart = table;
73 _ASSERTE(*castto(table, unsigned short *)++ == 0xFEEF);
75 /* Get the method size */
77 table += decodeUnsigned(table, methodSize);
79 table = decodeHeader(table, header);
81 BOOL hasArgTabOffset = FALSE;
82 if (header->untrackedCnt == HAS_UNTRACKED)
84 hasArgTabOffset = TRUE;
85 table += decodeUnsigned(table, &count);
86 header->untrackedCnt = count;
89 if (header->varPtrTableSize == HAS_VARPTR)
91 hasArgTabOffset = TRUE;
92 table += decodeUnsigned(table, &count);
93 header->varPtrTableSize = count;
96 if (header->gsCookieOffset == HAS_GS_COOKIE_OFFSET)
98 table += decodeUnsigned(table, &count);
99 header->gsCookieOffset = count;
102 if (header->syncStartOffset == HAS_SYNC_OFFSET)
104 table += decodeUnsigned(table, &count);
105 header->syncStartOffset = count;
106 table += decodeUnsigned(table, &count);
107 header->syncEndOffset = count;
111 // First print out all the basic information
114 gcPrintf(" method size = %04X\n", *methodSize);
115 gcPrintf(" prolog size = %2u \n", header->prologSize);
116 gcPrintf(" epilog size = %2u \n", header->epilogSize);
117 gcPrintf(" epilog count = %2u \n", header->epilogCount);
118 gcPrintf(" epilog end = %s \n", header->epilogAtEnd ? "yes" : "no");
120 gcPrintf(" callee-saved regs = ");
121 if (header->ediSaved) gcPrintf("EDI ");
122 if (header->esiSaved) gcPrintf("ESI ");
123 if (header->ebxSaved) gcPrintf("EBX ");
124 if (header->ebpSaved) gcPrintf("EBP ");
127 gcPrintf(" ebp frame = %s \n", header->ebpFrame ? "yes" : "no");
128 gcPrintf(" fully interruptible= %s \n", header->interruptible ? "yes" : "no");
129 gcPrintf(" double align = %s \n", header->doubleAlign ? "yes" : "no");
130 gcPrintf(" arguments size = %2u DWORDs\n", header->argCount);
131 gcPrintf(" stack frame size = %2u DWORDs\n", header->frameSize);
132 gcPrintf(" untracked count = %2u \n", header->untrackedCnt);
133 gcPrintf(" var ptr tab count = %2u \n", header->varPtrTableSize);
136 // Now display optional information
139 if (header->security) gcPrintf(" security check obj = yes\n");
140 if (header->handlers) gcPrintf(" exception handlers = yes\n");
141 if (header->localloc) gcPrintf(" localloc = yes\n");
142 if (header->editNcontinue) gcPrintf(" edit & continue = yes\n");
143 if (header->profCallbacks) gcPrintf(" profiler callbacks = yes\n");
144 if (header->varargs) gcPrintf(" varargs = yes\n");
145 if (header->gsCookieOffset != INVALID_GS_COOKIE_OFFSET)
146 gcPrintf(" GuardStack cookie = [%s%u]\n",
147 header->ebpFrame ? "EBP-" : "ESP+", header->gsCookieOffset);
148 if (header->syncStartOffset != INVALID_SYNC_OFFSET)
149 gcPrintf(" Sync region = [%u,%u]\n",
150 header->syncStartOffset, header->syncEndOffset);
152 if (header->epilogCount > 1 || (header->epilogCount != 0 &&
153 header->epilogAtEnd == 0))
156 _ASSERTE(*castto(table, unsigned short *)++ == 0xFACE);
160 for (unsigned i = 0; i < header->epilogCount; i++)
162 table += decodeUDelta(table, &offs, offs);
163 gcPrintf(" epilog #%2u at %04X\n", i, offs);
168 if (header->epilogCount)
169 gcPrintf(" epilog at %04X\n", (*methodSize - header->epilogSize));
174 unsigned argTabOffset;
175 table += decodeUnsigned(table, &argTabOffset);
176 gcPrintf(" argTabOffset = %x \n", argTabOffset);
181 unsigned last = table-bp;
184 unsigned amount = last - cur;
188 DumpEncoding(bp+cur, amount);
195 return (table - tableStart);
198 /*****************************************************************************/
201 #pragma warning(push)
202 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
204 size_t GCDump::DumpGCTable(PTR_CBYTE table,
205 const InfoHdr& header,
210 PTR_CBYTE tableStart = table;
221 _ASSERTE(*castto(table, unsigned short *)++ == 0xBEEF);
223 unsigned calleeSavedRegs = 0;
224 if (header.doubleAlign)
227 if (header.ediSaved) calleeSavedRegs++;
228 if (header.esiSaved) calleeSavedRegs++;
229 if (header.ebxSaved) calleeSavedRegs++;
232 /* Dump the untracked frame variable table */
234 count = header.untrackedCnt;
242 char reg = header.ebpFrame ? 'B' : 'S';
244 sz = (unsigned int)decodeSigned(table, &stkOffsDelta);
245 int stkOffs = lastStkOffs - stkOffsDelta;
246 lastStkOffs = stkOffs;
248 table = DumpEncoding(table, sz);
250 _ASSERTE(0 == ~OFFSET_MASK % sizeof(void*));
252 lowBits = OFFSET_MASK & stkOffs;
253 stkOffs &= ~OFFSET_MASK;
255 assert(!header.doubleAlign || stkOffs >= 0);
257 if (header.doubleAlign &&
258 unsigned(stkOffs) >= sizeof(int)*(header.frameSize+calleeSavedRegs))
261 stkOffs -= sizeof(int)*(header.frameSize+calleeSavedRegs);
262 _ASSERTE(stkOffs >= (int) (2*sizeof(int)));
266 gcPrintf(" [E%cP-%02XH] ", reg, -stkOffs);
268 gcPrintf(" [E%cP+%02XH] ", reg, +stkOffs);
270 gcPrintf("an untracked %s%s local\n",
271 (lowBits & pinned_OFFSET_FLAG) ? "pinned " : "",
272 (lowBits & byref_OFFSET_FLAG) ? "byref" : ""
277 _ASSERTE(*castto(table, unsigned short *)++ == 0xCAFE);
279 /* Dump the frame variable lifetime table */
281 count = header.varPtrTableSize;
293 table += decodeUnsigned(table, &varOffs);
294 table += decodeUDelta (table, &begOffs, curOffs);
295 table += decodeUDelta (table, &endOffs, begOffs);
297 DumpEncoding(bp, table-bp);
299 _ASSERTE(0 == ~OFFSET_MASK % sizeof(void*));
301 lowBits = varOffs & 0x3;
302 varOffs &= ~OFFSET_MASK;
304 // [EBP+0] is the return address - cant be a var
305 _ASSERTE(!header.ebpFrame || varOffs);
312 gcPrintf(" [E%s%02XH] a ", header.ebpFrame ? "BP-" : "SP+",
315 gcPrintf("%s%s pointer\n",
316 (lowBits & byref_OFFSET_FLAG) ? "byref " : "",
317 (lowBits & this_OFFSET_FLAG) ? "this" : ""
320 _ASSERTE(endOffs <= methodSize);
324 _ASSERTE(*castto(table, unsigned short *)++ == 0xBABE);
326 /* Dump the pointer table */
331 if (header.interruptible)
334 // Dump the Fully Interruptible pointer table
344 unsigned val = *table++;
346 _ASSERTE(curOffs <= methodSize);
350 /* A small 'regPtr' encoding */
352 curOffs += val & 0x7;
354 DumpEncoding(bp, table-bp); bp = table;
355 DumpOffsetEx(curOffs);
356 gcPrintf(" reg %s becoming %s", RegName(((val >> 3) & 7)),
357 (val & 0x40) ? "live"
370 /* This is probably an argument push/pop */
372 argOffs = (val & 0x38) >> 3;
374 /* 6 [110] and 7 [111] are reserved for other encodings */
378 /* A small argument encoding */
380 curOffs += (val & 0x07);
381 isPop = (val & 0x40);
387 // A Pop of 0, means little-delta
391 _ASSERTE(header.ebpFrame || argOffs <= argCnt);
393 DumpEncoding(bp, table-bp); bp = table;
394 DumpOffsetEx(curOffs);
396 gcPrintf(" pop %2d ", argOffs);
397 if (!header.ebpFrame)
400 gcPrintf("args (%d)", argCnt);
410 _ASSERTE(header.ebpFrame || argOffs >= argCnt);
412 DumpEncoding(bp, table-bp); bp = table;
413 DumpOffsetEx(curOffs);
415 gcPrintf(" push ptr %2d", argOffs);
416 if (!header.ebpFrame)
419 gcPrintf(" (%d)", argCnt);
433 else if (argOffs == 6)
437 /* Bigger delta 000=8,001=16,010=24,...,111=64 */
439 curOffs += (((val & 0x07) + 1) << 3);
443 /* non-ptr arg push */
445 curOffs += (val & 0x07);
446 _ASSERTE(!header.ebpFrame);
449 DumpEncoding(bp, table-bp); bp = table;
450 DumpOffsetEx(curOffs);
452 gcPrintf(" push non-ptr (%d)\n", argCnt);
458 /* argOffs was 7 [111] which is reserved for the larger encodings */
460 _ASSERTE(argOffs==7);
476 table += decodeUnsigned(table, &val);
483 table += decodeUnsigned(table, &argOffs);
487 table += decodeUnsigned(table, &argOffs);
490 DumpEncoding(bp, table-bp); bp = table;
491 DumpOffsetEx(curOffs);
493 gcPrintf(" kill args %2d\n", argOffs);
497 table += decodeUnsigned(table, &argOffs);
502 gcPrintf("Unexpected special code %04X\n", val);
507 else if (header.ebpFrame) // interruptible is false
510 // Dump the Partially Interruptible, EBP-frame method, pointer table
515 unsigned argMask = 0, byrefArgMask = 0;
516 unsigned regMask, byrefRegMask = 0;
519 PTR_CBYTE argTab = NULL;
524 /* Get the next byte and check for a 'special' entry */
526 unsigned encType = *table++;
528 _ASSERTE(curOffs <= methodSize);
535 /* A tiny or small call entry */
539 if ((val & 0x80) == 0x00)
543 /* A tiny call entry */
545 curOffs += (val & 0x0F);
546 regMask = (val & 0x70) >> 4;
551 DumpEncoding(bp, table-bp); bp = table;
553 gcPrintf(" thisptr in ");
561 _ASSERTE(!"Reserved GC encoding");
568 /* A small call entry */
570 curOffs += (val & 0x7F);
573 argMask = val & 0x1F;
577 case 0xFD: // medium encoding
581 argMask |= (val & 0xF0) << 4;
583 curOffs += (val & 0x0F) + ((nxt & 0x1F) << 4);
584 regMask = nxt >> 5; // EBX,ESI,EDI
588 case 0xF9: // medium encoding with byrefs
592 argMask = val & 0x1F;
595 byrefArgMask = val & 0x1F;
596 byrefRegMask = val >> 5;
600 case 0xFE: // large encoding
601 case 0xFA: // large encoding with byrefs
605 byrefRegMask= val >> 4;
606 curOffs += *PTR_DWORD(table); table += sizeof(DWORD);
607 argMask = *PTR_DWORD(table); table += sizeof(DWORD);
608 if (encType == 0xFA) // read byrefArgMask
609 {byrefArgMask = *PTR_DWORD(table); table += sizeof(DWORD);}
613 case 0xFB: // huge encoding
617 byrefRegMask= val >> 4;
618 curOffs = *PTR_DWORD(table); table += sizeof(DWORD);
619 argCnt = *PTR_DWORD(table); table += sizeof(DWORD);
620 argTabSize = *PTR_DWORD(table); table += sizeof(DWORD);
621 argTab = table; table += argTabSize;
630 Here we have the following values:
632 curOffs ... the code offset of the call
633 regMask ... mask of live pointer register variables
634 argMask ... bitmask of pushed pointer arguments
635 byrefRegMask ... byref qualifier for regMask
636 byrefArgMask ... byrer qualifier for argMask
639 _ASSERTE((byrefArgMask & argMask) == byrefArgMask);
640 _ASSERTE((byrefRegMask & regMask) == byrefRegMask);
642 DumpEncoding(bp, table-bp); bp = table;
643 DumpOffsetEx(curOffs);
645 gcPrintf(" call [ ");
648 gcPrintf("EDI%c", (byrefRegMask & 1) ? '\'' : ' ');
650 gcPrintf("ESI%c", (byrefRegMask & 2) ? '\'' : ' ');
652 gcPrintf("EBX%c", (byrefRegMask & 4) ? '\'' : ' ');
654 if (!header.ebpFrame)
662 gcPrintf("] ptrArgs=[");
666 argTab += decodeUnsigned(argTab, &val);
668 assert((val & this_OFFSET_FLAG) == 0);
669 unsigned stkOffs = val & ~byref_OFFSET_FLAG;
670 unsigned lowBit = val & byref_OFFSET_FLAG;
672 gcPrintf("%u%s", stkOffs, lowBit ? "i" : "");
677 assert(argTab == table);
683 gcPrintf("] argMask=%02X", argMask);
685 if (byrefArgMask) gcPrintf(" (iargs=%02X)", byrefArgMask);
691 else // interruptible is false, ebpFrame is false
694 // Dump the Partially Interruptible, EBP-less method, pointer table
696 unsigned lastSkip = 0;
701 unsigned val = *table++;
703 _ASSERTE(curOffs <= methodSize);
712 // push 000DDDDD push one item, 5-bit delta
715 curOffs += val & 0x1F;
717 DumpEncoding(bp, table-bp); bp = table;
718 DumpOffsetEx(curOffs);
725 // push 00100000 [pushCount] ESP push multiple items
731 table += decodeUnsigned(table, &pushCount);
733 DumpEncoding(bp, table-bp); bp = table;
734 DumpOffsetEx(curOffs);
736 gcPrintf(" push %d\n", pushCount);
744 if ((val & 0x3f) == 0)
747 // skip 01000000 [Delta] Skip arbitrary sized delta
750 table += decodeUnsigned(table, &skip);
757 // pop 01CCDDDD pop CC items, 4-bit delta
760 popSize = (val & 0x30) >> 4;
766 DumpEncoding(bp, table-bp); bp = table;
767 DumpOffsetEx(curOffs);
769 gcPrintf(" pop %d\n", popSize);
779 unsigned callRegMask;
780 bool callPndTab = false;
781 unsigned callPndMask = 0;
782 unsigned callPndTabCnt = 0, callPndTabSize = 0;
784 switch ((val & 0x70) >> 4)
788 // call 1PPPPPPP Call Pattern, P=[0..79]
790 decodeCallPattern((val & 0x7f), &callArgCnt, &callRegMask,
791 &callPndMask, &lastSkip);
796 DumpEncoding(bp, table-bp); bp = table;
797 DumpOffsetEx(curOffs);
799 gcPrintf(" call %d [ ", callArgCnt);
801 unsigned iregMask, iargMask;
803 iregMask = imask & 0xF;
804 iargMask = imask >> 4;
806 assert((callRegMask & 0x0F) == callRegMask);
808 gcPrintf("EDI%c", (iregMask & 1) ? '\'' : ' ');
810 gcPrintf("ESI%c", (iregMask & 2) ? '\'' : ' ');
812 gcPrintf("EBX%c", (iregMask & 4) ? '\'' : ' ');
814 gcPrintf("EBP%c", (iregMask & 8) ? '\'' : ' ');
819 #if defined(_DEBUG) && !defined(STRIKE)
820 // note: _ASSERTE is a no-op for strike
821 PTR_CBYTE offsStart = table;
823 gcPrintf(" argOffs(%d) =", callPndTabCnt);
824 for (unsigned i=0; i < callPndTabCnt; i++)
827 table += decodeUnsigned(table, &pndOffs);
828 gcPrintf(" %4X", pndOffs);
830 _ASSERTE(offsStart + callPndTabSize == table);
836 gcPrintf(" argMask=%02X", callPndMask);
838 gcPrintf(" (iargs=%02X)", iargMask);
842 imask = lastSkip = 0;
847 // call 1101RRRR DDCCCMMM Call RegMask=RRRR,ArgCnt=CCC,
848 // ArgMask=MMM Delta=commonDelta[DD]
850 callRegMask = val & 0xf; // EBP,EBX,ESI,EDI
852 callPndMask = (val & 0x7);
853 callArgCnt = (val >> 3) & 0x7;
854 lastSkip = callCommonDelta[val>>6];
861 // call 1110RRRR [ArgCnt] [ArgMask]
862 // Call ArgCnt,RegMask=RRR,ArgMask
864 callRegMask = val & 0xf; // EBP,EBX,ESI,EDI
865 table += decodeUnsigned(table, &callArgCnt);
866 table += decodeUnsigned(table, &callPndMask);
874 /* iptr 11110000 [IPtrMask] Arbitrary Interior Pointer Mask */
875 table += decodeUnsigned(table, &imask);
876 DumpEncoding(bp, table-bp); bp = table;
877 gcPrintf(" iptrMask = %02X\n", imask);
881 DumpEncoding(bp, table-bp); bp = table;
882 gcPrintf(" thisptr in %s\n", CalleeSavedRegName(val&0x3));
887 callRegMask = val & 0xF;
889 lastSkip = *PTR_DWORD(table); table += sizeof(DWORD);
891 callArgCnt = *PTR_DWORD(table); table += sizeof(DWORD);
892 callPndTabCnt = *PTR_DWORD(table); table += sizeof(DWORD);
893 callPndTabSize = *PTR_DWORD(table); table += sizeof(DWORD);
903 _ASSERTE(!"reserved GC encoding");
914 _ASSERTE(curOffs <= methodSize);
917 _ASSERTE(*castto(table, unsigned short *)++ == 0xBEEB);
919 _ASSERTE(table > bp);
921 DumpEncoding(bp, table-bp);
925 return (table - tableStart);
932 /*****************************************************************************/
934 void GCDump::DumpPtrsInFrame(PTR_CBYTE infoBlock,
939 PTR_CBYTE table = infoBlock;
952 _ASSERTE(*castto(table, unsigned short *)++ == 0xFEEF);
954 /* Get hold of the method size */
956 unsigned int methodSizeTemp;
957 table += decodeUnsigned(table, &methodSizeTemp);
958 methodSize = methodSizeTemp;
961 // New style InfoBlk Header
963 // Typically only uses one-byte to store everything.
966 table = decodeHeader(table, &header);
968 if (header.untrackedCnt == HAS_UNTRACKED)
971 table += decodeUnsigned(table, &count);
972 header.untrackedCnt = count;
974 if (header.varPtrTableSize == HAS_VARPTR)
977 table += decodeUnsigned(table, &count);
978 header.varPtrTableSize = count;
980 if (header.gsCookieOffset == HAS_GS_COOKIE_OFFSET)
983 table += decodeUnsigned(table, &offset);
984 header.gsCookieOffset = offset;
985 _ASSERTE(offset != INVALID_GS_COOKIE_OFFSET);
987 if (header.syncStartOffset == HAS_SYNC_OFFSET)
990 table += decodeUnsigned(table, &offset);
991 header.syncStartOffset = offset;
992 _ASSERTE(offset != INVALID_SYNC_OFFSET);
993 table += decodeUnsigned(table, &offset);
994 header.syncEndOffset = offset;
995 _ASSERTE(offset != INVALID_SYNC_OFFSET);
998 prologSize = header.prologSize;
999 epilogSize = header.epilogSize;
1000 epilogCnt = header.epilogCount;
1001 epilogEnd = header.epilogAtEnd;
1002 secCheck = header.security;
1003 dblAlign = header.doubleAlign;
1004 argSize = header.argCount * 4;
1005 stackSize = header.frameSize;
1010 gcPrintf(" method size = %04X\n", methodSize);
1011 gcPrintf(" stack frame size = %3u \n", stackSize);
1012 gcPrintf(" prolog size = %3u \n", prologSize);
1013 gcPrintf(" epilog size = %3u \n", epilogSize);
1014 gcPrintf(" epilog end = %s \n", epilogEnd ? "yes" : "no");
1015 gcPrintf(" epilog count = %3u \n", epilogCnt );
1016 gcPrintf(" security = %s \n", secCheck ? "yes" : "no");
1017 gcPrintf(" dblAlign = %s \n", dblAlign ? "yes" : "no");
1018 gcPrintf(" untracked count = %3u \n", header.untrackedCnt);
1019 gcPrintf(" var ptr tab count= %3u \n", header.varPtrTableSize);
1024 /* Are we within the prolog of the method? */
1026 if (offs < prologSize)
1028 gcPrintf(" Offset %04X is within the method's prolog\n", offs);
1032 /* Are we within an epilog of the method? */
1038 if (epilogCnt > 1 || !epilogEnd)
1041 _ASSERTE(*castto(table, unsigned short *)++ == 0xFACE);
1043 unsigned prevEps = 0;
1044 for (unsigned i = 0; i < epilogCnt; i++)
1046 table += decodeUDelta(table, &eps, prevEps);
1048 if ((offs >= eps) && (offs < eps + epilogSize))
1054 eps = (int)(methodSize - epilogSize);
1055 if ((offs >= eps) && (offs < eps + epilogSize))
1057 EPILOG_MSG: gcPrintf(" Offset %04X is within the method's epilog"
1058 " (%02X bytes into it)\n", offs, offs - eps);
1063 gcPrintf(" Offset %04X is within the method's body\n", offs);
1066 /*****************************************************************************/
1067 #endif // _TARGET_X86_
1068 /*****************************************************************************/