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 /*****************************************************************************
5 * GCDump.cpp
6 *
7 * Defines functions to display the GCInfo as defined by the GC-encoding
8 * spec. The GC information may be either dynamically created by a
9 * Just-In-Time compiler conforming to the standard code-manager spec,
10 * or may be persisted by a managed native code compiler conforming
11 * to the standard code-manager spec.
12 */
13 #include "common.h"
14
15 #if (defined(_DEBUG) || defined(DACCESS_COMPILE))
16
17 #include "gcenv.h"
18 #include "varint.h"
19 #include "gcinfo.h"
20 #include "gcdump.h"
21
22 /*****************************************************************************/
23
24 #ifdef DACCESS_COMPILE
DacNullPrintf(const char *,...)25 static void DacNullPrintf(const char* , ...) {}
26 #endif
27
GCDump()28 GCDump::GCDump()
29 {
30 #ifndef DACCESS_COMPILE
31 // By default, use the standard printf function to dump
32 GCDump::gcPrintf = (printfFtn) ::printf;
33 #else
34 // Default for DAC is a no-op.
35 GCDump::gcPrintf = DacNullPrintf;
36 #endif
37 }
38
39
40
41 /*****************************************************************************/
42
43 static const char * const calleeSaveRegMaskBitNumberToName[] =
44 {
45 #ifdef _ARM_
46 "R4",
47 "R5",
48 "R6",
49 "R7",
50 "R8",
51 "R9",
52 "R10",
53 "R11",
54 "LR",
55 #else // _ARM_
56 "EBX",
57 "ESI",
58 "EDI",
59 "EBP",
60 "R12",
61 "R13",
62 "R14",
63 "R15"
64 #endif // _ARM_
65 };
66
DumpInfoHeader(PTR_UInt8 gcInfo,Tables * pTables,GCInfoHeader * pHeader)67 size_t FASTCALL GCDump::DumpInfoHeader (PTR_UInt8 gcInfo,
68 Tables * pTables,
69 GCInfoHeader * pHeader /* OUT */
70 )
71 {
72 size_t headerSize = 0;
73 PTR_UInt8 gcInfoStart = gcInfo;
74 PTR_UInt8 pbStackChanges = 0;
75 PTR_UInt8 pbUnwindInfo = 0;
76
77 unsigned unwindInfoBlobOffset = VarInt::ReadUnsigned(gcInfo);
78 bool inlineUnwindInfo = (unwindInfoBlobOffset == 0);
79
80 if (inlineUnwindInfo)
81 {
82 // it is inline..
83 pbUnwindInfo = gcInfo;
84 }
85 else
86 {
87 // The offset was adjusted by 1 to reserve the 0 encoding for the inline case, so we re-adjust it to
88 // the actual offset here.
89 pbUnwindInfo = pTables->pbUnwindInfoBlob + unwindInfoBlobOffset - 1;
90 }
91
92 // @TODO: decode all funclet headers as well.
93 pbStackChanges = pHeader->DecodeHeader(0, pbUnwindInfo, &headerSize );
94
95 if (inlineUnwindInfo)
96 gcInfo += headerSize;
97
98 unsigned epilogCount = pHeader->GetEpilogCount();
99 bool epilogAtEnd = pHeader->IsEpilogAtEnd();
100
101 gcPrintf(" prologSize: %d\n", pHeader->GetPrologSize());
102 if (pHeader->HasVaryingEpilogSizes())
103 gcPrintf(" epilogSize: (varies)\n");
104 else
105 gcPrintf(" epilogSize: %d\n", pHeader->GetFixedEpilogSize());
106
107 gcPrintf(" epilogCount: %d %s\n", epilogCount, epilogAtEnd ? "[end]" : "");
108 const char * returnKind = "????";
109 unsigned reversePinvokeFrameOffset = 0; // it can't be 0 because [ebp+0] is the previous ebp
110 switch (pHeader->GetReturnKind())
111 {
112 case GCInfoHeader::MRK_ReturnsScalar: returnKind = "scalar"; break;
113 case GCInfoHeader::MRK_ReturnsObject: returnKind = "object"; break;
114 case GCInfoHeader::MRK_ReturnsByref: returnKind = "byref"; break;
115 case GCInfoHeader::MRK_ReturnsToNative:
116 returnKind = "to native";
117 reversePinvokeFrameOffset = pHeader->GetReversePinvokeFrameOffset();
118 break;
119 case GCInfoHeader::MRK_Unknown:
120 //ASSERT("Unexpected return kind")
121 break;
122 }
123 gcPrintf(" returnKind: %s\n", returnKind);
124 gcPrintf(" frameKind: %s", pHeader->HasFramePointer() ? "EBP" : "ESP");
125 #ifdef _TARGET_AMD64_
126 if (pHeader->HasFramePointer())
127 gcPrintf(" offset: %d", pHeader->GetFramePointerOffset());
128 #endif // _AMD64_
129 gcPrintf("\n");
130 gcPrintf(" frameSize: %d\n", pHeader->GetFrameSize());
131
132 if (pHeader->HasDynamicAlignment()) {
133 gcPrintf(" alignment: %d\n", (1 << pHeader->GetDynamicAlignment()));
134 if (pHeader->GetParamPointerReg() != RN_NONE) {
135 gcPrintf(" paramReg: %d\n", pHeader->GetParamPointerReg());
136 }
137 }
138
139 gcPrintf(" savedRegs: ");
140 CalleeSavedRegMask savedRegs = pHeader->GetSavedRegs();
141 CalleeSavedRegMask mask = (CalleeSavedRegMask) 1;
142 for (int i = 0; i < RBM_CALLEE_SAVED_REG_COUNT; i++)
143 {
144 if (savedRegs & mask)
145 {
146 gcPrintf("%s ", calleeSaveRegMaskBitNumberToName[i]);
147 }
148 mask = (CalleeSavedRegMask)(mask << 1);
149 }
150 gcPrintf("\n");
151
152 #ifdef _TARGET_ARM_
153 gcPrintf(" parmRegsPushedCount: %d\n", pHeader->ParmRegsPushedCount());
154 #endif
155
156 #ifdef _TARGET_X86_
157 gcPrintf(" returnPopSize: %d\n", pHeader->GetReturnPopSize());
158 if (pHeader->HasStackChanges())
159 {
160 // @TODO: need to read the stack changes string that follows
161 ASSERT(!"NYI -- stack changes for ESP frames");
162 }
163 #endif
164
165 if (reversePinvokeFrameOffset != 0)
166 {
167 gcPrintf(" reversePinvokeFrameOffset: 0x%02x\n", reversePinvokeFrameOffset);
168 }
169
170
171 if (!epilogAtEnd || (epilogCount > 2))
172 {
173 gcPrintf(" epilog offsets: ");
174 unsigned previousOffset = 0;
175 for (unsigned idx = 0; idx < epilogCount; idx++)
176 {
177 unsigned newOffset = previousOffset + VarInt::ReadUnsigned(gcInfo);
178 gcPrintf("0x%04x ", newOffset);
179 if (pHeader->HasVaryingEpilogSizes())
180 gcPrintf("(%u bytes) ", VarInt::ReadUnsigned(gcInfo));
181 previousOffset = newOffset;
182 }
183 gcPrintf("\n");
184 }
185
186 return gcInfo - gcInfoStart;
187 }
188
PrintLocalSlot(UInt32 slotNum,GCInfoHeader const * pHeader)189 void GCDump::PrintLocalSlot(UInt32 slotNum, GCInfoHeader const * pHeader)
190 {
191 // @TODO: print both EBP/ESP offsets where appropriate
192 #ifdef _TARGET_ARM_
193 gcPrintf("local slot 0n%d, [R7+%02X] \n", slotNum,
194 ((GCInfoHeader*)pHeader)->GetFrameSize() - ((slotNum + 1) * POINTER_SIZE));
195 #else
196 const char* regAndSign = "EBP-";
197 size_t offset = pHeader->GetPreservedRegsSaveSize() + (slotNum * POINTER_SIZE);
198 # ifdef _TARGET_AMD64_
199 if (((GCInfoHeader*)pHeader)->GetFramePointerOffset() == 0)
200 {
201 regAndSign = "RBP-";
202 }
203 else
204 {
205 regAndSign = "RBP+";
206 offset = (slotNum * POINTER_SIZE);
207 }
208 # endif
209 gcPrintf("local slot 0n%d, [%s%02X] \n", slotNum, regAndSign, offset);
210 #endif
211 }
212
DumpCallsiteString(UInt32 callsiteOffset,PTR_UInt8 pbCallsiteString,GCInfoHeader const * pHeader)213 void GCDump::DumpCallsiteString(UInt32 callsiteOffset, PTR_UInt8 pbCallsiteString,
214 GCInfoHeader const * pHeader)
215 {
216 gcPrintf("%04x: ", callsiteOffset);
217
218 int count = 0;
219 UInt8 b;
220 PTR_UInt8 pCursor = pbCallsiteString;
221
222 bool last = false;
223 bool first = true;
224
225 do
226 {
227 if (!first)
228 gcPrintf(" ");
229
230 first = false;
231
232 b = *pCursor++;
233 last = ((b & 0x20) == 0x20);
234
235 switch (b & 0xC0)
236 {
237 case 0x00:
238 {
239 // case 2 -- "register set"
240 gcPrintf("%02x | 2 ", b);
241 #ifdef _TARGET_ARM_
242 if (b & CSR_MASK_R4) { gcPrintf("R4 "); count++; }
243 if (b & CSR_MASK_R5) { gcPrintf("R5 "); count++; }
244 if (b & CSR_MASK_R6) { gcPrintf("R6 "); count++; }
245 if (b & CSR_MASK_R7) { gcPrintf("R7 "); count++; }
246 if (b & CSR_MASK_R8) { gcPrintf("R8 "); count++; }
247 #elif defined(_TARGET_ARM64_)
248 // ARM64TODO: not all of these are needed?
249 if (b & CSR_MASK_X19) { gcPrintf("X19 "); count++; }
250 if (b & CSR_MASK_X20) { gcPrintf("X20 "); count++; }
251 if (b & CSR_MASK_X21) { gcPrintf("X21 "); count++; }
252 if (b & CSR_MASK_X22) { gcPrintf("X22 "); count++; }
253 if (b & CSR_MASK_X23) { gcPrintf("X23 "); count++; }
254 if (b & CSR_MASK_X24) { gcPrintf("X24 "); count++; }
255 if (b & CSR_MASK_X25) { gcPrintf("X25 "); count++; }
256 if (b & CSR_MASK_X26) { gcPrintf("X26 "); count++; }
257 if (b & CSR_MASK_X27) { gcPrintf("X27 "); count++; }
258 if (b & CSR_MASK_X28) { gcPrintf("X28 "); count++; }
259 #else // _ARM_
260 if (b & CSR_MASK_RBX) { gcPrintf("RBX "); count++; }
261 if (b & CSR_MASK_RSI) { gcPrintf("RSI "); count++; }
262 if (b & CSR_MASK_RDI) { gcPrintf("RDI "); count++; }
263 if (b & CSR_MASK_RBP) { gcPrintf("RBP "); count++; }
264 if (b & CSR_MASK_R12) { gcPrintf("R12 "); count++; }
265 #endif // _ARM_
266 gcPrintf("\n");
267 }
268 break;
269
270 case 0x40:
271 {
272 // case 3 -- "register"
273 const char* regName = "???";
274 const char* interior = (b & 0x10) ? "+" : "";
275 const char* pinned = (b & 0x08) ? "!" : "";
276
277 switch (b & 0x7)
278 {
279 #ifdef _TARGET_ARM_
280 case CSR_NUM_R4: regName = "R4"; break;
281 case CSR_NUM_R5: regName = "R5"; break;
282 case CSR_NUM_R6: regName = "R6"; break;
283 case CSR_NUM_R7: regName = "R7"; break;
284 case CSR_NUM_R8: regName = "R8"; break;
285 case CSR_NUM_R9: regName = "R9"; break;
286 case CSR_NUM_R10: regName = "R10"; break;
287 case CSR_NUM_R11: regName = "R11"; break;
288 #elif defined(_TARGET_ARM64_)
289 case CSR_NUM_X19: regName = "X19"; break;
290 case CSR_NUM_X20: regName = "X20"; break;
291 case CSR_NUM_X21: regName = "X21"; break;
292 case CSR_NUM_X22: regName = "X22"; break;
293 case CSR_NUM_X23: regName = "X23"; break;
294 case CSR_NUM_X24: regName = "X24"; break;
295 case CSR_NUM_X25: regName = "X25"; break;
296 case CSR_NUM_X26: regName = "X26"; break;
297 case CSR_NUM_X27: regName = "X27"; break;
298 case CSR_NUM_X28: regName = "X28"; break;
299 #else // _ARM_
300 case CSR_NUM_RBX: regName = "RBX"; break;
301 case CSR_NUM_RSI: regName = "RSI"; break;
302 case CSR_NUM_RDI: regName = "RDI"; break;
303 case CSR_NUM_RBP: regName = "RBP"; break;
304 #ifdef _TARGET_AMD64_
305 case CSR_NUM_R12: regName = "R12"; break;
306 case CSR_NUM_R13: regName = "R13"; break;
307 case CSR_NUM_R14: regName = "R14"; break;
308 case CSR_NUM_R15: regName = "R15"; break;
309 #endif // _TARGET_AMD64_
310 #endif // _ARM_
311 }
312 gcPrintf("%02x | 3 %s%s%s \n", b, regName, interior, pinned);
313 count++;
314 }
315 break;
316
317 case 0x80:
318 {
319 if (b & 0x10)
320 {
321 // case 4 -- "local slot set"
322 gcPrintf("%02x | 4 ", b);
323 bool isFirst = true;
324
325 int mask = 0x01;
326 int slotNum = 0;
327 while (mask <= 0x08)
328 {
329 if (b & mask)
330 {
331 if (!isFirst)
332 {
333 if (!first)
334 gcPrintf(" ");
335 gcPrintf(" | ");
336 }
337
338 PrintLocalSlot(slotNum, pHeader);
339
340 isFirst = false;
341 count++;
342 }
343 mask <<= 1;
344 slotNum++;
345 }
346 }
347 else
348 {
349 // case 5 -- "local slot"
350 int slotNum = (int)(b & 0xF) + 4;
351 gcPrintf("%02x | 5 ", b);
352 PrintLocalSlot(slotNum, pHeader);
353
354 count++;
355 }
356 }
357 break;
358 case 0xC0:
359 {
360 gcPrintf("%02x ", b);
361 unsigned mask = 0;
362 PTR_UInt8 pInts = pCursor;
363 unsigned offset = VarInt::ReadUnsigned(pCursor);
364 const char* interior = (b & 0x10) ? "+" : "";
365 const char* pinned = (b & 0x08) ? "!" : "";
366 #ifdef _TARGET_ARM_
367 const char* baseReg = (b & 0x04) ? "R7" : "SP";
368 #else
369 const char* baseReg = (b & 0x04) ? "EBP" : "ESP";
370 #endif
371 const char* sign = (b & 0x02) ? "-" : "+";
372 if (b & 0x01)
373 {
374 mask = VarInt::ReadUnsigned(pCursor);
375 }
376
377 int c = 1;
378 while (pInts != pCursor)
379 {
380 gcPrintf("%02x ", *pInts++);
381 c++;
382 }
383
384 for (; c < 4; c++)
385 {
386 gcPrintf(" ");
387 }
388
389 gcPrintf("| 6 [%s%s%02X]%s%s\n", baseReg, sign, offset, interior, pinned);
390 count++;
391
392 while (mask > 0)
393 {
394 offset += POINTER_SIZE;
395 if (mask & 1)
396 {
397 if (!first)
398 gcPrintf(" ");
399
400 gcPrintf(" | [%s%s%02X]%s%s\n", baseReg, sign, offset, interior, pinned);
401 count++;
402 }
403 mask >>= 1;
404 }
405 }
406 break;
407 }
408 }
409 while (!last);
410
411 //gcPrintf("\n");
412 }
413
414
415
416
DumpGCTable(PTR_UInt8 gcInfo,Tables * pTables,const GCInfoHeader & header)417 size_t FASTCALL GCDump::DumpGCTable (PTR_UInt8 gcInfo,
418 Tables * pTables,
419 const GCInfoHeader& header)
420 {
421 //
422 // Decode the method GC info
423 //
424 // 0ddddccc -- SMALL ENCODING
425 //
426 // -- dddd is an index into the delta shortcut table
427 // -- ccc is an offset into the callsite strings blob
428 //
429 // 1ddddddd { info offset } -- BIG ENCODING
430 //
431 // -- ddddddd is a 7-bit delta
432 // -- { info offset } is a variable-length unsigned encoding of the offset into the callsite
433 // strings blob for this callsite.
434 //
435 // 10000000 { delta } -- FORWARDER
436 //
437 // -- { delta } is a variable-length unsigned encoding of the offset to the next callsite
438 //
439 // 11111111 -- STRING TERMINATOR
440 //
441
442 PTR_UInt8 pCursor = gcInfo;
443 UInt32 curOffset = 0;
444
445 for (;;)
446 {
447 UInt8 b = *pCursor++;
448 unsigned infoOffset;
449
450 if (b & 0x80)
451 {
452 UInt8 lowBits = (b & 0x7F);
453 // FORWARDER
454 if (lowBits == 0)
455 {
456 curOffset += VarInt::ReadUnsigned(pCursor);
457 continue;
458 }
459 else
460 if (lowBits == 0x7F) // STRING TERMINATOR
461 break;
462
463 // BIG ENCODING
464 curOffset += lowBits;
465 infoOffset = VarInt::ReadUnsigned(pCursor);
466 }
467 else
468 {
469 // SMALL ENCODING
470 infoOffset = (b & 0x7);
471 curOffset += pTables->pbDeltaShortcutTable[b >> 3];
472 }
473
474 DumpCallsiteString(curOffset, pTables->pbCallsiteInfoBlob + infoOffset, &header);
475 }
476
477 gcPrintf("-------\n");
478
479 return 0;
480 }
481
482 #endif // _DEBUG || DACCESS_COMPILE
483