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// ==++==
6//
7
8//
9// ==--==
10
11#include "strike.h"
12#include "gcinfo.h"
13#include "util.h"
14#include <dbghelp.h>
15#include <limits.h>
16
17#include "sos_md.h"
18
19#ifdef SOS_TARGET_X86
20namespace X86GCDump
21{
22#include "gcdump.h"
23#undef assert
24#define assert(a)
25#define CONTRACTL
26#define DAC_ARG(x)
27#define CONTRACTL_END
28#define LIMITED_METHOD_CONTRACT
29#define NOTHROW
30#define GC_NOTRIGGER
31#define SUPPORTS_DAC
32#define LIMITED_METHOD_DAC_CONTRACT
33#include "gcdecoder.cpp"
34#undef CONTRACTL
35#undef CONTRACTL_END
36#undef LIMITED_METHOD_CONTRACT
37#undef NOTHROW
38#undef GC_NOTRIGGER
39#undef _ASSERTE
40#define _ASSERTE(a) do {} while (0)
41
42#include "gcdump.cpp"
43#include "i386/gcdumpx86.cpp"
44}
45#endif // SOS_TARGET_X86
46
47#ifdef SOS_TARGET_AMD64
48#include "gcdump.h"
49#define DAC_ARG(x)
50#define SUPPORTS_DAC
51#define LIMITED_METHOD_DAC_CONTRACT
52#undef LIMITED_METHOD_CONTRACT
53#undef PREGDISPLAY
54 #ifdef LOG
55 #undef LOG
56 #endif
57 #define LOG(x) ((void)0)
58 #ifdef LOG_PIPTR
59 #undef LOG_PIPTR
60 #endif
61 #define LOG_PIPTR(pObjRef, gcFlags, hCallBack) ((void)0)
62#include "gcdumpnonx86.cpp"
63#endif // SOS_TARGET_AMD64
64
65#include "disasm.h"
66
67#ifndef ERANGE
68#define ERANGE 34
69#endif
70
71PVOID
72GenOpenMapping(
73 PCSTR FilePath,
74 PULONG Size
75 )
76{
77#ifndef FEATURE_PAL
78 HANDLE hFile;
79 HANDLE hMappedFile;
80 PVOID MappedFile;
81
82 hFile = CreateFileA(
83 FilePath,
84 GENERIC_READ,
85 FILE_SHARE_READ | FILE_SHARE_WRITE,
86 NULL,
87 OPEN_EXISTING,
88 0,
89 NULL
90 );
91#if 0
92 if ( hFile == NULL || hFile == INVALID_HANDLE_VALUE ) {
93
94 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
95
96 // We're on an OS that doesn't support Unicode
97 // file operations. Convert to ANSI and see if
98 // that helps.
99
100 CHAR FilePathA [ MAX_LONGPATH + 10 ];
101
102 if (WideCharToMultiByte (CP_ACP,
103 0,
104 FilePath,
105 -1,
106 FilePathA,
107 sizeof (FilePathA),
108 0,
109 0
110 ) > 0) {
111
112 hFile = CreateFileA(FilePathA,
113 GENERIC_READ,
114 FILE_SHARE_READ | FILE_SHARE_WRITE,
115 NULL,
116 OPEN_EXISTING,
117 0,
118 NULL
119 );
120 }
121 }
122
123 if ( hFile == NULL || hFile == INVALID_HANDLE_VALUE ) {
124 return NULL;
125 }
126 }
127#endif
128
129 *Size = GetFileSize(hFile, NULL);
130 if (*Size == ULONG_MAX) {
131 CloseHandle( hFile );
132 return NULL;
133 }
134
135 hMappedFile = CreateFileMapping (
136 hFile,
137 NULL,
138 PAGE_READONLY,
139 0,
140 0,
141 NULL
142 );
143
144 if ( !hMappedFile ) {
145 CloseHandle ( hFile );
146 return NULL;
147 }
148
149 MappedFile = MapViewOfFile (
150 hMappedFile,
151 FILE_MAP_READ,
152 0,
153 0,
154 0
155 );
156
157 CloseHandle (hMappedFile);
158 CloseHandle (hFile);
159
160 return MappedFile;
161#else // FEATURE_PAL
162 return NULL;
163#endif // FEATURE_PAL
164}
165
166char* PrintOneLine (__in_z char *begin, __in_z char *limit)
167{
168 if (begin == NULL || begin >= limit) {
169 return NULL;
170 }
171 char line[128];
172 size_t length;
173 char *end;
174 while (1) {
175 if (IsInterrupt())
176 return NULL;
177 length = strlen (begin);
178 end = strstr (begin, "\r\xa");
179 if (end == NULL) {
180 ExtOut ("%s", begin);
181 end = begin+length+1;
182 if (end >= limit) {
183 return NULL;
184 }
185 }
186 else {
187 end += 2;
188 length = end-begin;
189 while (length) {
190 if (IsInterrupt())
191 return NULL;
192 size_t n = length;
193 if (n > 127) {
194 n = 127;
195 }
196 strncpy_s (line,_countof(line), begin, n);
197 line[n] = '\0';
198 ExtOut ("%s", line);
199 begin += n;
200 length -= n;
201 }
202 return end;
203 }
204 }
205}
206
207void UnassemblyUnmanaged(DWORD_PTR IP, BOOL bSuppressLines)
208{
209 char filename[MAX_PATH_FNAME+1];
210 char line[256];
211 int lcount = 10;
212
213 ULONG linenum = 0;
214 ULONG64 Displacement = 0;
215 BOOL fLineAvailable = FALSE;
216 ULONG64 vIP = 0;
217
218 if (!bSuppressLines)
219 {
220 ReloadSymbolWithLineInfo();
221 fLineAvailable = SUCCEEDED (g_ExtSymbols->GetLineByOffset(TO_CDADDR(IP),
222 &linenum,
223 filename,
224 MAX_PATH_FNAME+1,
225 NULL,
226 &Displacement));
227 }
228 ULONG FileLines = 0;
229 ArrayHolder<ULONG64> Buffer = NULL;
230
231 if (fLineAvailable)
232 {
233 g_ExtSymbols->GetSourceFileLineOffsets(filename, NULL, 0, &FileLines);
234 if (FileLines == 0xFFFFFFFF || FileLines == 0)
235 fLineAvailable = FALSE;
236 }
237
238 if (fLineAvailable)
239 {
240 Buffer = new ULONG64[FileLines];
241 if (Buffer == NULL)
242 fLineAvailable = FALSE;
243 }
244
245 if (!fLineAvailable)
246 {
247 vIP = TO_CDADDR(IP);
248 // There is no line info. Just disasm the code.
249 while (lcount-- > 0)
250 {
251 if (IsInterrupt())
252 return;
253 g_ExtControl->Disassemble (vIP, 0, line, 256, NULL, &vIP);
254 ExtOut (line);
255 }
256 return;
257 }
258
259 g_ExtSymbols->GetSourceFileLineOffsets(filename, Buffer, FileLines, NULL);
260
261 int beginLine = 0;
262 int endLine = 0;
263 int lastLine;
264 linenum --;
265 for (lastLine = linenum; lastLine >= 0; lastLine --) {
266 if (IsInterrupt())
267 return;
268 if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
269 g_ExtSymbols->GetNameByOffset(Buffer[lastLine], NULL, 0, NULL, &Displacement);
270 if (Displacement == 0) {
271 beginLine = lastLine;
272 break;
273 }
274 }
275 }
276 if (lastLine < 0) {
277 int n = lcount / 2;
278 lastLine = linenum-1;
279 beginLine = lastLine;
280 while (lastLine >= 0) {
281 if (IsInterrupt())
282 return;
283 if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
284 beginLine = lastLine;
285 n --;
286 if (n == 0) {
287 break;
288 }
289 }
290 lastLine --;
291 }
292 }
293 while (beginLine > 0 && Buffer[beginLine-1] == DEBUG_INVALID_OFFSET) {
294 if (IsInterrupt())
295 return;
296 beginLine --;
297 }
298 int endOfFunc = 0;
299 for (lastLine = linenum+1; (ULONG)lastLine < FileLines; lastLine ++) {
300 if (IsInterrupt())
301 return;
302 if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
303 g_ExtSymbols->GetNameByOffset(Buffer[lastLine], NULL, 0, NULL, &Displacement);
304 if (Displacement == 0) {
305 endLine = lastLine;
306 break;
307 }
308 endOfFunc = lastLine;
309 }
310 }
311 if ((ULONG)lastLine == FileLines) {
312 int n = lcount / 2;
313 lastLine = linenum+1;
314 endLine = lastLine;
315 while ((ULONG)lastLine < FileLines) {
316 if (IsInterrupt())
317 return;
318 if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
319 endLine = lastLine;
320 n --;
321 if (n == 0) {
322 break;
323 }
324 }
325 lastLine ++;
326 }
327 }
328
329 PVOID MappedBase = NULL;
330 ULONG MappedSize = 0;
331
332 class ToUnmap
333 {
334 PVOID *m_Base;
335 public:
336 ToUnmap (PVOID *base)
337 :m_Base(base)
338 {}
339 ~ToUnmap ()
340 {
341 if (*m_Base) {
342 UnmapViewOfFile (*m_Base);
343 *m_Base = NULL;
344 }
345 }
346 };
347 ToUnmap toUnmap(&MappedBase);
348
349#define MAX_SOURCE_PATH 1024
350 char Found[MAX_SOURCE_PATH];
351 char *pFile;
352 if (g_ExtSymbols->FindSourceFile(0,
353 filename,
354 DEBUG_FIND_SOURCE_BEST_MATCH | DEBUG_FIND_SOURCE_FULL_PATH,
355 NULL,
356 Found,
357 sizeof(Found),
358 NULL) != S_OK)
359 {
360 pFile = filename;
361 }
362 else
363 {
364 MappedBase = GenOpenMapping(Found, &MappedSize);
365 pFile = Found;
366 }
367
368 lastLine = beginLine;
369 char *pFileCh = (char*)MappedBase;
370 if (MappedBase) {
371 ExtOut ("%s\n", pFile);
372 int n = beginLine;
373 while (n > 0) {
374 while (!(pFileCh[0] == '\r' && pFileCh[1] == 0xa)) {
375 if (IsInterrupt())
376 return;
377 pFileCh ++;
378 }
379 pFileCh += 2;
380 n --;
381 }
382 }
383
384 char filename1[MAX_PATH_FNAME+1];
385 for (lastLine = beginLine; lastLine < endLine; lastLine ++) {
386 if (IsInterrupt())
387 return;
388 if (MappedBase) {
389 ExtOut("%4d ", lastLine+1);
390 pFileCh = PrintOneLine(pFileCh, (char*)MappedBase+MappedSize);
391 }
392 if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
393 if (MappedBase == 0) {
394 ExtOut (">>> %s:%d\n", pFile, lastLine+1);
395 }
396 vIP = Buffer[lastLine];
397 ULONG64 vNextLineIP;
398 int i;
399 for (i = lastLine + 1; (ULONG)i < FileLines && Buffer[i] == DEBUG_INVALID_OFFSET; i ++) {
400 if (IsInterrupt())
401 return;
402 }
403 if ((ULONG)i == FileLines) {
404 vNextLineIP = 0;
405 }
406 else
407 vNextLineIP = Buffer[i];
408 while (1) {
409 if (IsInterrupt())
410 return;
411 g_ExtControl->Disassemble(vIP, 0, line, 256, NULL, &vIP);
412 ExtOut (line);
413 if (vIP > vNextLineIP || vNextLineIP - vIP > 40) {
414 if (FAILED (g_ExtSymbols->GetLineByOffset(vIP, &linenum,
415 filename1,
416 MAX_PATH_FNAME+1,
417 NULL,
418 &Displacement))) {
419 if (lastLine != endOfFunc) {
420 break;
421 }
422 if (strstr (line, "ret") || strstr (line, "jmp")) {
423 break;
424 }
425 }
426
427 if (linenum != (ULONG)lastLine+1 || strcmp (filename, filename1)) {
428 break;
429 }
430 }
431 else if (vIP == vNextLineIP) {
432 break;
433 }
434 }
435 }
436 }
437}
438
439void DisasmAndClean (DWORD_PTR &IP, __out_ecount_opt(length) char *line, ULONG length)
440{
441 ULONG64 vIP = TO_CDADDR(IP);
442 g_ExtControl->Disassemble (vIP, 0, line, length, NULL, &vIP);
443 IP = (DWORD_PTR)vIP;
444 // remove the ending '\n'
445 char *ptr = strrchr (line, '\n');
446 if (ptr != NULL)
447 ptr[0] = '\0';
448}
449
450// If byref, move to pass the byref prefix
451BOOL IsByRef (__deref_inout_z char *& ptr)
452{
453 BOOL bByRef = FALSE;
454 const char* qindirCh = "qword ptr [";
455 const char* dindirCh = "dword ptr [";
456 const char* qindirDsCh = "qword ptr ds:[";
457 const char* dindirDsCh = "dword ptr ds:[";
458 if (ptr[0] == '[')
459 {
460 bByRef = TRUE;
461 ptr ++;
462 }
463 else if (!IsDbgTargetArm() && !strncmp (ptr, IsDbgTargetWin64() ? qindirCh : dindirCh, 11))
464 {
465 bByRef = TRUE;
466 ptr += 11;
467 }
468 // The new disassembly engine for windbg formats indirect calls
469 // slightly differently:
470 else if (!IsDbgTargetArm() && !strncmp (ptr, IsDbgTargetWin64() ? qindirDsCh : dindirDsCh, 14))
471 {
472 bByRef = TRUE;
473 ptr += 14;
474 }
475 return bByRef;
476}
477
478BOOL IsTermSep (char ch)
479{
480 return (ch == '\0' || isspace (ch) || ch == ',' || ch == '\n');
481}
482
483// Find next term. A term is seperated by space or ,
484void NextTerm (__deref_inout_z char *& ptr)
485{
486 // If we have a byref, skip to ']'
487 if (IsByRef (ptr))
488 {
489 while (ptr[0] != ']' && ptr[0] != '\0')
490 {
491 if (IsInterrupt())
492 return;
493 ptr ++;
494 }
495 if (ptr[0] == ']')
496 ptr ++;
497 }
498
499 while (!IsTermSep (ptr[0]))
500 {
501 if (IsInterrupt())
502 return;
503 ptr ++;
504 }
505
506 while (IsTermSep(ptr[0]) && (*ptr != '\0'))
507 {
508 if (IsInterrupt())
509 return;
510 ptr ++;
511 }
512}
513
514
515// Parses something like 6e24d310, 0x6e24d310, or 6e24d310h.
516// On 64-bit, also parses things like 000006fb`f9b70f50 and
517// 000006fbf9b70f50 (as well as their 0x-prefix, -h suffix variations).
518INT_PTR ParseHexNumber (__in_z char *ptr, ___out char **endptr)
519{
520 char *endptr1;
521 INT_PTR value1 = strtoul(ptr, &endptr1, 16);
522
523#ifdef _TARGET_WIN64_
524 if ('`' == endptr1[0] && isxdigit(endptr1[1]))
525 {
526 char *endptr2;
527 INT_PTR value2 = strtoul(endptr1+1, &endptr2, 16);
528
529 value1 = (value1 << 32) | value2;
530 endptr1 = endptr2;
531 }
532 // if the hex number was specified as 000006fbf9b70f50, an overflow occurred
533 else if (ULONG_MAX == value1 && errno == ERANGE)
534 {
535 if (!strncmp(ptr, "0x", 2))
536 ptr += 2;
537
538 char savedigit = ptr[8];
539 ptr[8] = '\0';
540
541 value1 = strtoul(ptr, &endptr1, 16);
542
543 ptr[8] = savedigit;
544
545 char *endptr2;
546 INT_PTR value2 = strtoul(ptr+8, &endptr2, 16);
547
548 size_t ndigits2 = endptr2 - (ptr+8);
549
550 value1 = (value1 << (ndigits2*4)) | value2;
551 endptr1 = endptr2;
552 }
553#endif // _TARGET_WIN64_
554
555 // account for the possible 'h' suffix
556 if ((*endptr1 == 'h') || (*endptr1 == 'H'))
557 {
558 ++endptr1;
559 }
560
561 *endptr = endptr1;
562 return value1;
563}
564
565
566// only handle pure value, or memory address
567INT_PTR GetValueFromExpr(__in_z char *ptr, INT_PTR &value)
568{
569 BOOL bNegative = FALSE;
570 value = 0;
571 char *myPtr = ptr;
572 BOOL bByRef = IsByRef (myPtr);
573
574 // ARM disassembly contains '#' prefixes for hex constants
575 if (*myPtr == '#')
576 ++myPtr;
577
578 if (myPtr[0] == '-')
579 {
580 myPtr ++;
581 bNegative = TRUE;
582 }
583 if (!strncmp (myPtr, "0x", 2) || isxdigit (myPtr[0]))
584 {
585 char *endptr;
586 value = ParseHexNumber(myPtr, &endptr);
587 if ((!bByRef && IsTermSep(endptr[0])) || (bByRef && endptr[0] == ']'))
588 {
589 if (bNegative)
590 value = -value;
591 ptr = endptr;
592 if (bByRef)
593 {
594 ptr += 1;
595 SafeReadMemory (TO_TADDR(value), &value, 4, NULL);
596 }
597 return ptr - myPtr;
598 }
599 }
600
601 // handle mscorlib+0xed310 (6e24d310)
602 if (!bByRef)
603 {
604 ptr = myPtr;
605 // handle 'offset ' before the expression:
606 if (strncmp(ptr, "offset ", 7) == 0)
607 {
608 ptr += 7;
609 }
610 while (ptr[0] != ' ' && ptr[0] != '+' && ptr[0] != '\0')
611 {
612 if (IsInterrupt())
613 return 0;
614 ptr ++;
615 }
616 if (ptr[0] == '+')
617 {
618 NextTerm (ptr);
619 if (ptr[0] == '(')
620 {
621 ptr ++;
622 char *endptr;
623 value = ParseHexNumber(ptr, &endptr);
624 if (endptr[0] == ')')
625 {
626 ptr ++;
627 return ptr - myPtr;
628 }
629 }
630 }
631 }
632 if (bByRef)
633 {
634 // handle dword [mscorlib+0x2bd788 (02ead788)]
635 ptr = myPtr;
636 // handle 'offset ' before the expression:
637 if (strncmp(ptr, "offset ", 7) == 0)
638 {
639 ptr += 7;
640 }
641 while (ptr[0] != '(' && ptr[0] != '\0')
642 {
643 if (IsInterrupt())
644 return 0;
645 ptr ++;
646 }
647 if (ptr[0] == '(')
648 {
649 ptr ++;
650 char *endptr;
651 value = ParseHexNumber(ptr, &endptr);
652 if (endptr[0] == ')' && endptr[1] == ']')
653 {
654 ptr = endptr + 2;
655 SafeReadMemory (TO_TADDR(value), &value, 4, NULL);
656 return ptr - myPtr;
657 }
658 }
659 }
660
661#ifdef _TARGET_WIN64_
662 // handle CLRStub@7fffc8601cc (000007fffc8601cc)
663 if (!bByRef && !strncmp(myPtr, "CLRStub[", 8))
664 {
665 ptr = myPtr;
666 while (ptr[0] != '(' && ptr[0] != '\0')
667 {
668 if (IsInterrupt())
669 return 0;
670 ptr ++;
671 }
672 if (ptr[0] == '(')
673 {
674 ptr ++;
675 char *endptr;
676 value = ParseHexNumber(ptr, &endptr);
677 if (endptr[0] == ')')
678 {
679 ptr ++;
680 return ptr - myPtr;
681 }
682 }
683 }
684#endif // _TARGET_WIN64_
685
686 return 0;
687}
688
689
690const char * HelperFuncName (size_t IP)
691{
692 static char s_szHelperName[100];
693 if (S_OK == g_sos->GetJitHelperFunctionName(IP, sizeof(s_szHelperName), &s_szHelperName[0], NULL))
694 return &s_szHelperName[0];
695 else
696 return NULL;
697}
698
699
700// Returns:
701// NULL if the EHInfo passed in does not refer to a Typed clause
702// "..." if pEHInfo->isCatchAllHandler is TRUE
703// "TypeName" if pEHInfo is a DACEHInfo*.
704// Note:
705// The return is a pointer to a global buffer, therefore this value must
706// be consumed as soon as possible after a call to this function.
707LPCWSTR EHTypedClauseTypeName(___in const DACEHInfo* pEHInfo)
708{
709 _ASSERTE(pEHInfo != NULL);
710 if ((pEHInfo->clauseType == EHTyped) && pEHInfo->isCatchAllHandler)
711 {
712 return W("...");
713 }
714
715 // is there a method table or a token to look at?
716 if (pEHInfo->clauseType == EHTyped)
717 {
718 TADDR mt;
719 if (pEHInfo->moduleAddr == 0)
720 {
721 mt = TO_TADDR(pEHInfo->mtCatch);
722 NameForMT_s(mt, g_mdName, mdNameLen);
723 } else {
724 PrettyPrintClassFromToken(TO_TADDR(pEHInfo->moduleAddr), pEHInfo->tokCatch, g_mdName, mdNameLen, FormatCSharp);
725 }
726 return g_mdName;
727 }
728
729 return NULL;
730}
731
732BOOL IsClonedFinally(DACEHInfo *pEHInfo)
733{
734 // This maybe should be determined in the VM and passed in the DACEHInfo struct.
735#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
736 return ((pEHInfo->tryStartOffset == pEHInfo->tryEndOffset) &&
737 (pEHInfo->tryStartOffset == pEHInfo->handlerStartOffset) &&
738 (pEHInfo->clauseType == EHFinally) &&
739 pEHInfo->isDuplicateClause);
740#else
741 return FALSE;
742#endif
743}
744
745
746void SOSEHInfo::FormatForDisassembly(CLRDATA_ADDRESS offSet)
747{
748 LPCWSTR typeName = NULL;
749 // the order of printing and iterating will matter on the boundaries
750
751 // Print END tags in forward order (most nested to least nested). However, cloned
752 // finally clauses are always at the end, but they should be considered most nested,
753 // so have a separate loop to output them first.
754 for (UINT i=0; i < EHCount; i++)
755 {
756 DACEHInfo *pCur = &m_pInfos[i];
757
758 if (IsClonedFinally(pCur) &&
759 (offSet == pCur->handlerEndOffset))
760 {
761 ExtOut ("EHHandler %d: CLONED FINALLY END\n", i);
762 }
763 }
764
765 for (UINT i=0; i < EHCount; i++)
766 {
767 DACEHInfo *pCur = &m_pInfos[i];
768
769 if (pCur->isDuplicateClause)
770 {
771 // Don't print anything for duplicate clauses
772 continue;
773 }
774
775 if (offSet == pCur->tryEndOffset)
776 {
777 ExtOut ("EHHandler %d: %s CLAUSE END\n", i, EHTypeName(pCur->clauseType));
778 }
779
780 if (offSet == pCur->handlerEndOffset)
781 {
782 ExtOut ("EHHandler %d: %s HANDLER END\n", i, EHTypeName(pCur->clauseType));
783 }
784 }
785
786 // Print BEGIN tags in reverse order (least nested to most nested).
787 for (UINT i=EHCount-1; i != (UINT)-1; --i)
788 {
789 DACEHInfo *pCur = &m_pInfos[i];
790
791 // Must do this before the isDuplicatedClause check, since these are marked as duplicated clauses.
792 if (IsClonedFinally(pCur) &&
793 (offSet == pCur->handlerStartOffset))
794 {
795 ExtOut ("EHHandler %d: CLONED FINALLY BEGIN\n", i);
796 }
797
798 if (pCur->isDuplicateClause)
799 {
800 // Don't print anything for duplicate clauses
801 continue;
802 }
803
804 if (offSet == pCur->tryStartOffset)
805 {
806 ExtOut ("EHHandler %d: %s CLAUSE BEGIN", i, EHTypeName(pCur->clauseType));
807 typeName = EHTypedClauseTypeName(pCur);
808 if (typeName != NULL)
809 {
810 ExtOut(" catch(%S) ", typeName);
811 }
812 ExtOut ("\n");
813 }
814
815 if (offSet == pCur->handlerStartOffset)
816 {
817 ExtOut ("EHHandler %d: %s HANDLER BEGIN", i, EHTypeName(pCur->clauseType));
818 typeName = EHTypedClauseTypeName(pCur);
819 if (typeName != NULL)
820 {
821 ExtOut(" catch(%S) ", typeName);
822 }
823 ExtOut ("\n");
824 }
825
826 if ((pCur->clauseType == EHFilter) &&
827 (offSet == pCur->filterOffset))
828 {
829 ExtOut ("EHHandler %d: %s FILTER BEGIN\n",i, EHTypeName(pCur->clauseType));
830 }
831 }
832}
833
834
835//
836// Implementation shared by X86, ARM, and X64
837// Any cross platform code should resolve through g_targetMachine or should
838// use the IS_DBG_TARGET_XYZ macro.
839//
840
841void PrintNativeStack(DWORD_PTR ip, BOOL bSuppressLines)
842{
843 char filename[MAX_PATH_FNAME + 1];
844 char symbol[1024];
845 ULONG64 displacement;
846
847 HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(ip), symbol, _countof(symbol), NULL, &displacement);
848 if (SUCCEEDED(hr) && symbol[0] != '\0')
849 {
850 ExtOut("%s", symbol);
851
852 if (displacement)
853 {
854 ExtOut(" + %#x", displacement);
855 }
856
857 if (!bSuppressLines)
858 {
859 ULONG line;
860 hr = g_ExtSymbols->GetLineByOffset(TO_CDADDR(ip), &line, filename, _countof(filename), NULL, NULL);
861 if (SUCCEEDED(hr))
862 {
863 ExtOut(" [%s:%d]", filename, line);
864 }
865 }
866 }
867 else
868 {
869 DMLOut(DMLIP(ip));
870 }
871}
872
873// Return TRUE if we have printed something.
874BOOL PrintCallInfo(DWORD_PTR vEBP, DWORD_PTR IP, DumpStackFlag& DSFlag, BOOL bSymbolOnly)
875{
876 ULONG64 Displacement;
877 BOOL bOutput = FALSE;
878
879 // degrade gracefully for debuggees that don't have a runtime loaded, or a DAC available
880 DWORD_PTR methodDesc = 0;
881 if (!g_bDacBroken)
882 {
883 methodDesc = FunctionType (IP);
884 }
885
886 if (methodDesc > 1)
887 {
888 bOutput = TRUE;
889 if (!bSymbolOnly)
890 DMLOut("%p %s ", SOS_PTR(vEBP), DMLIP(IP));
891 DMLOut("(MethodDesc %s ", DMLMethodDesc(methodDesc));
892
893 // TODO: Microsoft, more checks to make sure method is not eeimpl, etc. Add this field to MethodDesc
894
895 DacpCodeHeaderData codeHeaderData;
896 if (codeHeaderData.Request(g_sos, TO_CDADDR(IP)) == S_OK)
897 {
898 DWORD_PTR IPBegin = (DWORD_PTR) codeHeaderData.MethodStart;
899 methodDesc = (DWORD_PTR) codeHeaderData.MethodDescPtr;
900 Displacement = IP - IPBegin;
901 if (IP >= IPBegin && Displacement <= codeHeaderData.MethodSize)
902 ExtOut ("+ %#x ", Displacement);
903 }
904 if (NameForMD_s(methodDesc, g_mdName, mdNameLen))
905 {
906 ExtOut("%S)", g_mdName);
907 }
908 else
909 {
910 ExtOut("%s)", DMLIP(IP));
911 }
912 }
913 else
914 {
915 if (!DSFlag.fEEonly)
916 {
917 bOutput = TRUE;
918 const char *name;
919 if (!bSymbolOnly)
920 DMLOut("%p %s ", SOS_PTR(vEBP), DMLIP(IP));
921
922 // if AMD64 ever becomes a cross platform target this must be resolved through
923 // virtual dispatch rather than conditional compilation
924#if defined(_TARGET_AMD64_) || defined(_TARGET_X86_)
925 // degrade gracefully for debuggees that don't have a runtime loaded, or a DAC available
926 eTargetType ett = ettUnk;
927 if (!g_bDacBroken)
928 {
929 DWORD_PTR finalMDorIP = 0;
930 ett = GetFinalTarget(IP, &finalMDorIP);
931 if (ett == ettNative || ett==ettJitHelp)
932 {
933 methodDesc = 0;
934 IP = finalMDorIP;
935 }
936 else
937 {
938 methodDesc = finalMDorIP;
939 }
940 }
941#endif // _TARGET_AMD64_ || _TARGET_X86_
942 if (methodDesc == 0)
943 {
944 PrintNativeStack(IP, DSFlag.fSuppressSrcInfo);
945 }
946 else if (g_bDacBroken)
947 {
948 // degrade gracefully for debuggees that don't have a runtime loaded, or a DAC available
949 DMLOut(DMLIP(IP));
950 }
951 else if (IsMethodDesc (IP))
952 {
953 NameForMD_s(IP, g_mdName, mdNameLen);
954 ExtOut(" (stub for %S)", g_mdName);
955 }
956 else if (IsMethodDesc(IP+5)) {
957 NameForMD_s((DWORD_PTR)(IP+5), g_mdName, mdNameLen);
958 DMLOut("%s (MethodDesc %s %S)", DMLIP(IP), DMLMethodDesc(IP+5), g_mdName);
959 }
960 else if ((name = HelperFuncName(IP)) != NULL) {
961 ExtOut(" (JitHelp: %s)", name);
962 }
963#if defined(_TARGET_AMD64_) || defined(_TARGET_X86_)
964 else if (ett == ettMD || ett == ettStub)
965 {
966 NameForMD_s(methodDesc, g_mdName,mdNameLen);
967 DMLOut("%s (stub for %S)", DMLIP(IP), g_mdName);
968 // fallthrough to return
969 }
970#endif // _TARGET_AMD64_ || _TARGET_X86_
971 else
972 {
973 DMLOut(DMLIP(IP));
974 }
975 }
976 }
977 return bOutput;
978}
979
980void DumpStackWorker (DumpStackFlag &DSFlag)
981{
982 DWORD_PTR eip;
983 ULONG64 Offset;
984 g_ExtRegisters->GetInstructionOffset(&Offset);
985 eip = (DWORD_PTR)Offset;
986
987 ExtOut("Current frame: ");
988 PrintCallInfo (0, eip, DSFlag, TRUE);
989 ExtOut ("\n");
990
991 // make certain dword/qword aligned
992 DWORD_PTR ptr = DSFlag.top & (~ALIGNCONST);
993
994 ExtOut (g_targetMachine->GetDumpStackHeading());
995 while (ptr < DSFlag.end)
996 {
997 if (IsInterrupt())
998 return;
999 DWORD_PTR retAddr;
1000 DWORD_PTR whereCalled;
1001 move_xp(retAddr, ptr);
1002 g_targetMachine->IsReturnAddress(retAddr, &whereCalled);
1003 if (whereCalled)
1004 {
1005 BOOL bOutput = PrintCallInfo(ptr-sizeof(TADDR), retAddr, DSFlag, FALSE);
1006 if (!DSFlag.fEEonly)
1007 {
1008 if (whereCalled != 0xFFFFFFFF)
1009 {
1010 ExtOut (", calling ");
1011 PrintCallInfo (0, whereCalled, DSFlag, TRUE);
1012 }
1013 }
1014 if (bOutput)
1015 ExtOut ("\n");
1016
1017 DWORD_PTR cxrAddr;
1018 CROSS_PLATFORM_CONTEXT cxr;
1019 DWORD_PTR exrAddr;
1020 EXCEPTION_RECORD exr;
1021
1022 if (g_targetMachine->GetExceptionContext(ptr,retAddr,&cxrAddr,&cxr,&exrAddr,&exr))
1023 {
1024 TADDR sp = g_targetMachine->GetSP(cxr);
1025 TADDR ip = g_targetMachine->GetIP(cxr);
1026 bOutput = PrintCallInfo(sp, ip, DSFlag, FALSE);
1027 if (bOutput)
1028 {
1029 ExtOut(" ====> Exception ");
1030 if (exrAddr)
1031 ExtOut("Code %x ", exr.ExceptionCode);
1032 ExtOut ("cxr@%p", SOS_PTR(cxrAddr));
1033 if (exrAddr)
1034 ExtOut(" exr@%p", SOS_PTR(exrAddr));
1035 ExtOut("\n");
1036 }
1037 }
1038 }
1039 ptr += sizeof (DWORD_PTR);
1040 }
1041}
1042
1043#ifdef SOS_TARGET_X86
1044///
1045/// X86Machine implementation
1046///
1047LPCSTR X86Machine::s_DumpStackHeading = "ChildEBP RetAddr Caller, Callee\n";
1048LPCSTR X86Machine::s_DSOHeading = "ESP/REG Object Name\n";
1049LPCSTR X86Machine::s_GCRegs[7] = {"eax", "ebx", "ecx", "edx", "esi", "edi", "ebp"};
1050LPCSTR X86Machine::s_SPName = "ESP";
1051
1052void PrintNothing (const char *fmt, ...)
1053{
1054 // Do nothing.
1055}
1056
1057///
1058/// Dump X86 GCInfo header and table
1059///
1060void X86Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
1061{
1062 X86GCDump::InfoHdr header;
1063 X86GCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
1064 BYTE* pTable = dac_cast<PTR_BYTE>(gcInfoToken.Info);
1065 if (bPrintHeader)
1066 {
1067 gcDump.gcPrintf = gcPrintf;
1068 gcPrintf("Method info block:\n");
1069 }
1070 else
1071 {
1072 gcDump.gcPrintf = PrintNothing;
1073 }
1074 pTable += gcDump.DumpInfoHdr(pTable, &header, &methodSize, 0);
1075 if (bPrintHeader)
1076 {
1077 gcPrintf("\n");
1078 gcPrintf("Pointer table:\n");
1079 }
1080 gcDump.gcPrintf = gcPrintf;
1081 gcDump.DumpGCTable(pTable, header, methodSize, 0);
1082}
1083#endif // SOS_TARGET_X86
1084
1085#ifdef SOS_TARGET_ARM
1086///
1087/// ARMMachine implementation
1088///
1089LPCSTR ARMMachine::s_DumpStackHeading = "ChildFP RetAddr Caller, Callee\n";
1090LPCSTR ARMMachine::s_DSOHeading = "SP/REG Object Name\n";
1091LPCSTR ARMMachine::s_GCRegs[14] = {"r0", "r1", "r2", "r3", "r4", "r5", "r6",
1092 "r7", "r8", "r9", "r10", "r11", "r12", "lr"};
1093LPCSTR ARMMachine::s_SPName = "sp";
1094
1095#endif // SOS_TARGET_ARM
1096
1097#ifdef SOS_TARGET_AMD64
1098///
1099/// AMD64Machine implementation
1100///
1101LPCSTR AMD64Machine::s_DumpStackHeading = "Child-SP RetAddr Caller, Callee\n";
1102LPCSTR AMD64Machine::s_DSOHeading = "RSP/REG Object Name\n";
1103LPCSTR AMD64Machine::s_GCRegs[15] = {"rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp",
1104 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"};
1105LPCSTR AMD64Machine::s_SPName = "RSP";
1106
1107///
1108/// Dump AMD64 GCInfo table
1109///
1110void AMD64Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
1111{
1112 if (bPrintHeader)
1113 {
1114 ExtOut("Pointer table:\n");
1115 }
1116
1117 GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
1118 gcDump.gcPrintf = gcPrintf;
1119
1120 gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
1121}
1122
1123#endif // SOS_TARGET_AMD64
1124
1125#ifdef SOS_TARGET_ARM64
1126///
1127/// ARM64Machine implementation
1128///
1129LPCSTR ARM64Machine::s_DumpStackHeading = "ChildFP RetAddr Caller, Callee\n";
1130LPCSTR ARM64Machine::s_DSOHeading = "SP/REG Object Name\n";
1131// excluding x18, fp & lr as these will not contain object references
1132LPCSTR ARM64Machine::s_GCRegs[28] = {"x0", "x1", "x2", "x3", "x4", "x5", "x6",
1133 "x7", "x8", "x9", "x10", "x11", "x12", "x13",
1134 "x14", "x15", "x16", "x17", "x19", "x20","x21",
1135 "x22", "x23", "x24", "x25", "x26", "x27", "x28"};
1136LPCSTR ARM64Machine::s_SPName = "sp";
1137
1138#endif // SOS_TARGET_ARM64
1139
1140
1141