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 * GCDumpNonX86.cpp
6 */
7
8#include "gcdump.h"
9
10#define LIMITED_METHOD_CONTRACT ((void)0)
11#define WRAPPER_NO_CONTRACT ((void)0)
12
13#define GCINFODECODER_NO_EE
14#include "gcinfodecoder.h"
15#include "gcinfodumper.h"
16
17
18PCSTR GetRegName (UINT32 regnum)
19{
20#ifdef _TARGET_AMD64_
21
22 switch (regnum)
23 {
24 case 0: return "rax";
25 case 1: return "rcx";
26 case 2: return "rdx";
27 case 3: return "rbx";
28 case 4: return "rsp";
29 case 5: return "rbp";
30 case 6: return "rsi";
31 case 7: return "rdi";
32 case 8: return "r8";
33 case 9: return "r9";
34 case 10: return "r10";
35 case 11: return "r11";
36 case 12: return "r12";
37 case 13: return "r13";
38 case 14: return "r14";
39 case 15: return "r15";
40 }
41
42
43 return "???";
44#elif defined(_TARGET_ARM64_)
45
46 static CHAR szRegName[16];
47 if (regnum < 29)
48 {
49 _snprintf_s(szRegName, _countof(szRegName), sizeof(szRegName), "X%u", regnum);
50 return szRegName;
51 }
52 else if(regnum == 29)
53 {
54 return "Fp";
55 }
56 else if(regnum == 30)
57 {
58 return "Lr";
59 }
60 else if(regnum == 31)
61 {
62 return "Sp";
63 }
64
65 return "???";
66#elif defined(_TARGET_ARM_)
67 if (regnum > 128)
68 return "???";
69
70 static CHAR szRegName[16];
71 _snprintf_s(szRegName, _countof(szRegName), sizeof(szRegName), "r%u", regnum);
72 return szRegName;
73
74#endif
75}
76
77
78/*****************************************************************************/
79
80
81GCDump::GCDump(UINT32 gcInfoVer, bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs)
82 : gcInfoVersion(gcInfoVer),
83 fDumpEncBytes (encBytes ),
84 cMaxEncBytes (maxEncBytes ),
85 fDumpCodeOffsets(dumpCodeOffs)
86{
87}
88
89
90/*****************************************************************************/
91
92
93struct GcInfoDumpState
94{
95 UINT32 LastCodeOffset;
96 BOOL fAnythingPrinted;
97 BOOL fSafePoint;
98 UINT32 FrameRegister;
99 GCDump::printfFtn pfnPrintf;
100};
101
102
103BOOL InterruptibleStateChangeCallback (
104 UINT32 CodeOffset,
105 BOOL fInterruptible,
106 PVOID pvData)
107{
108 GcInfoDumpState *pState = (GcInfoDumpState*)pvData;
109
110 if (pState->fAnythingPrinted)
111 {
112 pState->pfnPrintf("\n");
113 pState->fAnythingPrinted = FALSE;
114 pState->fSafePoint = FALSE;
115 }
116
117 pState->pfnPrintf("%08x%s interruptible\n", CodeOffset, fInterruptible ? "" : " not");
118
119 pState->LastCodeOffset = -1;
120
121 return FALSE;
122}
123
124BOOL SafePointCallback (
125 UINT32 CodeOffset,
126 PVOID pvData)
127{
128 GcInfoDumpState *pState = (GcInfoDumpState*)pvData;
129
130 if (pState->fAnythingPrinted)
131 {
132 pState->pfnPrintf("\n");
133 }
134
135 pState->pfnPrintf("%08x is a safepoint: ", CodeOffset);
136
137 pState->LastCodeOffset = CodeOffset;
138 pState->fAnythingPrinted = TRUE;
139 pState->fSafePoint = TRUE;
140
141 return FALSE;
142}
143
144
145VOID PrintFlags (GCDump::printfFtn pfnPrintf, GcSlotFlags Flags)
146{
147 if (Flags & GC_SLOT_PINNED)
148 pfnPrintf("(pinned)");
149
150 if (Flags & GC_SLOT_INTERIOR)
151 pfnPrintf("(interior)");
152
153 if (Flags & GC_SLOT_UNTRACKED)
154 pfnPrintf("(untracked)");
155}
156
157
158BOOL RegisterStateChangeCallback (
159 UINT32 CodeOffset,
160 UINT32 RegisterNumber,
161 GcSlotFlags Flags,
162 GcSlotState NewState,
163 PVOID pvData)
164{
165 GcInfoDumpState *pState = (GcInfoDumpState*)pvData;
166
167 if (pState->fSafePoint && (GC_SLOT_LIVE != NewState))
168 {
169 // Don't print deaths for safepoints
170 return FALSE;
171 }
172
173 if (pState->LastCodeOffset != CodeOffset)
174 {
175 if (pState->fAnythingPrinted)
176 pState->pfnPrintf("\n");
177
178 pState->pfnPrintf("%08x", CodeOffset);
179
180 pState->LastCodeOffset = CodeOffset;
181 }
182
183 char delta = ((GC_SLOT_LIVE == NewState) ? '+' : '-');
184
185 pState->pfnPrintf(" %c%s", delta, GetRegName(RegisterNumber));
186
187 PrintFlags(pState->pfnPrintf, Flags);
188
189 pState->fAnythingPrinted = TRUE;
190
191 return FALSE;
192}
193
194
195BOOL StackSlotStateChangeCallback (
196 UINT32 CodeOffset,
197 GcSlotFlags flags,
198 GcStackSlotBase BaseRegister,
199 SSIZE_T StackOffset,
200 GcSlotState NewState,
201 PVOID pvData)
202{
203 GcInfoDumpState *pState = (GcInfoDumpState*)pvData;
204
205 if (pState->fSafePoint && (GC_SLOT_LIVE != NewState))
206 {
207 // Don't print deaths for safepoints
208 return FALSE;
209 }
210
211 if (pState->LastCodeOffset != CodeOffset)
212 {
213 if (pState->fAnythingPrinted)
214 pState->pfnPrintf("\n");
215
216 if ((CodeOffset == -2) && !pState->fAnythingPrinted)
217 pState->pfnPrintf("Untracked:");
218 else
219 pState->pfnPrintf("%08x", CodeOffset);
220
221 pState->LastCodeOffset = CodeOffset;
222 }
223
224 char delta = ((GC_SLOT_LIVE == NewState) ? '+' : '-');
225
226 CHAR sign = '+';
227
228 // the dumper's call back (in GcInfoDumper.cpp) has to "guess" the base register
229 // for stack slots it usually guesses it wrong ......
230 // We try to filter out at least the non-sensical combinations
231 // - negative offset relative to SP
232 // - positive offset relative to CALLER_SP
233
234 if (StackOffset < 0)
235 {
236 StackOffset = -StackOffset;
237 sign = '-';
238#ifndef GCINFODUMPER_IS_FIXED
239 if (BaseRegister == GC_SP_REL)
240 { // negative offset to SP????
241 BaseRegister = GC_CALLER_SP_REL;
242 }
243#endif // !GCINFODUMPER_IS_FIXED
244 }
245#ifndef GCINFODUMPER_IS_FIXED
246 else if (BaseRegister == GC_CALLER_SP_REL)
247 { // positive offset to Caller_SP????
248 BaseRegister = GC_SP_REL;
249 }
250#endif // !GCINFODUMPER_IS_FIXED
251
252
253
254 PCSTR pszBaseReg;
255
256 switch (BaseRegister)
257 {
258 case GC_CALLER_SP_REL: pszBaseReg = "caller.sp"; break;
259 case GC_SP_REL: pszBaseReg = "sp"; break;
260 case GC_FRAMEREG_REL: pszBaseReg = GetRegName(pState->FrameRegister); break;
261 default: pszBaseReg = "???"; break;
262 }
263
264 pState->pfnPrintf(" %c%s%c%x", delta, pszBaseReg, sign, StackOffset);
265
266 PrintFlags(pState->pfnPrintf, flags);
267
268 pState->fAnythingPrinted = TRUE;
269
270 return FALSE;
271}
272
273
274size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock,
275 unsigned methodSize,
276 bool verifyGCTables)
277{
278 GCInfoToken gcInfoToken = { dac_cast<PTR_VOID>(gcInfoBlock), gcInfoVersion };
279 GcInfoDecoder hdrdecoder(gcInfoToken,
280 (GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT
281 | DECODE_GS_COOKIE
282 | DECODE_CODE_LENGTH
283 | DECODE_PSP_SYM
284 | DECODE_VARARG
285 | DECODE_GENERICS_INST_CONTEXT
286 | DECODE_GC_LIFETIMES
287 | DECODE_PROLOG_LENGTH
288 | DECODE_RETURN_KIND),
289 0);
290
291 if (NO_SECURITY_OBJECT != hdrdecoder.GetSecurityObjectStackSlot() ||
292 NO_GENERICS_INST_CONTEXT != hdrdecoder.GetGenericsInstContextStackSlot() ||
293 NO_GS_COOKIE == hdrdecoder.GetGSCookieStackSlot())
294 {
295 gcPrintf("Prolog size: ");
296 UINT32 prologSize = hdrdecoder.GetPrologSize();
297 gcPrintf("%d\n", prologSize);
298 }
299
300 gcPrintf("Security object: ");
301 if (NO_SECURITY_OBJECT == hdrdecoder.GetSecurityObjectStackSlot())
302 {
303 gcPrintf("<none>\n");
304 }
305 else
306 {
307 INT32 ofs = hdrdecoder.GetSecurityObjectStackSlot();
308 char sign = '+';
309
310 if (ofs < 0)
311 {
312 sign = '-';
313 ofs = -ofs;
314 }
315
316 gcPrintf("caller.sp%c%x\n", sign, ofs);
317 }
318
319 gcPrintf("GS cookie: ");
320 if (NO_GS_COOKIE == hdrdecoder.GetGSCookieStackSlot())
321 {
322 gcPrintf("<none>\n");
323 }
324 else
325 {
326 INT32 ofs = hdrdecoder.GetGSCookieStackSlot();
327 char sign = '+';
328
329 if (ofs < 0)
330 {
331 sign = '-';
332 ofs = -ofs;
333 }
334
335 gcPrintf("caller.sp%c%x\n", sign, ofs);
336
337 UINT32 validRangeStart = hdrdecoder.GetGSCookieValidRangeStart();
338 UINT32 validRangeEnd = hdrdecoder.GetGSCookieValidRangeEnd();
339 gcPrintf("GS cookie valid range: [%x;%x)\n", validRangeStart, validRangeEnd);
340 }
341
342 gcPrintf("PSPSym: ");
343 if (NO_PSP_SYM == hdrdecoder.GetPSPSymStackSlot())
344 {
345 gcPrintf("<none>\n");
346 }
347 else
348 {
349 INT32 ofs = hdrdecoder.GetPSPSymStackSlot();
350 char sign = '+';
351
352 if (ofs < 0)
353 {
354 sign = '-';
355 ofs = -ofs;
356 }
357
358#ifdef _TARGET_AMD64_
359 // The PSPSym is relative to InitialSP on X64 and CallerSP on other platforms.
360 gcPrintf("initial.sp%c%x\n", sign, ofs);
361#else
362 gcPrintf("caller.sp%c%x\n", sign, ofs);
363#endif
364 }
365
366 gcPrintf("Generics inst context: ");
367 if (NO_GENERICS_INST_CONTEXT == hdrdecoder.GetGenericsInstContextStackSlot())
368 {
369 gcPrintf("<none>\n");
370 }
371 else
372 {
373 INT32 ofs = hdrdecoder.GetGenericsInstContextStackSlot();
374 char sign = '+';
375
376 if (ofs < 0)
377 {
378 sign = '-';
379 ofs = -ofs;
380 }
381
382 gcPrintf("caller.sp%c%x\n", sign, ofs);
383 }
384
385 gcPrintf("PSP slot: ");
386 if (NO_PSP_SYM == hdrdecoder.GetPSPSymStackSlot())
387 {
388 gcPrintf("<none>\n");
389 }
390 else
391 {
392 INT32 ofs = hdrdecoder.GetPSPSymStackSlot();
393 char sign = '+';
394
395 if (ofs < 0)
396 {
397 sign = '-';
398 ofs = -ofs;
399 }
400
401 gcPrintf("caller.sp%c%x\n", sign, ofs);
402
403 }
404
405 gcPrintf("GenericInst slot: ");
406 if (NO_GENERICS_INST_CONTEXT == hdrdecoder.GetGenericsInstContextStackSlot())
407 {
408 gcPrintf("<none>\n");
409 }
410 else
411 {
412 INT32 ofs = hdrdecoder.GetGenericsInstContextStackSlot();
413 char sign = '+';
414
415 if (ofs < 0)
416 {
417 sign = '-';
418 ofs = -ofs;
419 }
420
421 gcPrintf("caller.sp%c%x ", sign, ofs);
422
423 if (hdrdecoder.HasMethodDescGenericsInstContext())
424 gcPrintf("(GENERIC_PARAM_CONTEXT_METHODDESC)\n");
425 else if (hdrdecoder.HasMethodTableGenericsInstContext())
426 gcPrintf("(GENERIC_PARAM_CONTEXT_METHODHANDLE)\n");
427 else
428 gcPrintf("(GENERIC_PARAM_CONTEXT_THIS)\n");
429 }
430
431 gcPrintf("Varargs: %u\n", hdrdecoder.GetIsVarArg());
432 gcPrintf("Frame pointer: %s\n", NO_STACK_BASE_REGISTER == hdrdecoder.GetStackBaseRegister()
433 ? "<none>"
434 : GetRegName(hdrdecoder.GetStackBaseRegister()));
435
436#ifdef _TARGET_AMD64_
437 gcPrintf("Wants Report Only Leaf: %u\n", hdrdecoder.WantsReportOnlyLeaf());
438#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
439 gcPrintf("Has tailcalls: %u\n", hdrdecoder.HasTailCalls());
440#endif // _TARGET_AMD64_
441#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
442 gcPrintf("Size of parameter area: %x\n", hdrdecoder.GetSizeOfStackParameterArea());
443#endif
444
445 ReturnKind returnKind = hdrdecoder.GetReturnKind();
446 gcPrintf("Return Kind: %s\n", ReturnKindToString(returnKind));
447
448 UINT32 cbEncodedMethodSize = hdrdecoder.GetCodeLength();
449 gcPrintf("Code size: %x\n", cbEncodedMethodSize);
450
451 GcInfoDumper dumper(gcInfoToken);
452
453 GcInfoDumpState state;
454 state.LastCodeOffset = -1;
455 state.fAnythingPrinted = FALSE;
456 state.fSafePoint = FALSE;
457 state.FrameRegister = hdrdecoder.GetStackBaseRegister();
458 state.pfnPrintf = gcPrintf;
459
460 GcInfoDumper::EnumerateStateChangesResults result = dumper.EnumerateStateChanges(
461 &InterruptibleStateChangeCallback,
462 &RegisterStateChangeCallback,
463 &StackSlotStateChangeCallback,
464 &SafePointCallback,
465 &state);
466
467 if (state.fAnythingPrinted)
468 gcPrintf("\n");
469
470 switch (result)
471 {
472 case GcInfoDumper::SUCCESS:
473 // do nothing
474 break;
475
476 case GcInfoDumper::OUT_OF_MEMORY:
477 gcPrintf("out of memory\n");
478 break;
479
480 case GcInfoDumper::REPORTED_REGISTER_IN_CALLERS_FRAME:
481 gcPrintf("reported register in caller's frame\n");
482 break;
483
484 case GcInfoDumper::REPORTED_FRAME_POINTER:
485 gcPrintf("reported frame register\n");
486 break;
487
488 case GcInfoDumper::REPORTED_INVALID_BASE_REGISTER:
489 gcPrintf("reported pointer relative to wrong base register\n");
490 break;
491
492 case GcInfoDumper::REPORTED_INVALID_POINTER:
493 gcPrintf("reported invalid pointer\n");
494 break;
495
496 case GcInfoDumper::DECODER_FAILED:
497 gcPrintf("decoder failed\n");
498 break;
499
500 default:
501 gcPrintf("invalid GC info\n");
502 break;
503 }
504
505 return (result == GcInfoDumper::SUCCESS) ? dumper.GetGCInfoSize() : 0;
506}
507
508
509/*****************************************************************************/
510
511void GCDump::DumpPtrsInFrame(PTR_CBYTE gcInfoBlock,
512 PTR_CBYTE codeBlock,
513 unsigned offs,
514 bool verifyGCTables)
515{
516 _ASSERTE(!"NYI");
517}
518
519
520#define _common_h_
521
522#ifndef LOG
523#define LOG(x) ((void)0)
524#endif
525
526#define GCINFODECODER_CONTRACT ((void)0)
527#define GET_CALLER_SP(pREGDISPLAY) ((size_t)GetSP(pREGDISPLAY->pCallerContext))
528#define VALIDATE_OBJECTREF(objref, fDeep) ((void)0)
529#define VALIDATE_ROOT(isInterior, hCallBack, pObjRef) ((void)0)
530#include "../vm/gcinfodecoder.cpp"
531#include "../gcinfo/gcinfodumper.cpp"
532