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#include "common.h"
7
8/*******************************************************************/
9/* The following routines used to exist in all builds so they could called from the
10 * debugger before we had strike.
11 * Now most of them are only included in debug builds for diagnostics purposes.
12*/
13/*******************************************************************/
14
15#include "stdlib.h"
16
17BOOL isMemoryReadable(const TADDR start, unsigned len)
18{
19 CONTRACTL
20 {
21 NOTHROW;
22 GC_NOTRIGGER;
23 SO_TOLERANT;
24 }
25 CONTRACTL_END;
26
27#if !defined(DACCESS_COMPILE) && defined(FEATURE_PAL)
28
29 return PAL_ProbeMemory((PVOID)start, len, FALSE);
30
31#else // !DACCESS_COMPILE && FEATURE_PAL
32
33 //
34 // To accomplish this in a no-throw way, we have to touch each and every page
35 // and see if it is in memory or not.
36 //
37
38 //
39 // Touch the first and last bytes.
40 //
41 char buff;
42
43#ifdef DACCESS_COMPILE
44 if (DacReadAll(start, &buff, 1, false) != S_OK)
45 {
46 return 0;
47 }
48#else
49 if (ReadProcessMemory(GetCurrentProcess(), (PVOID)start, &buff, 1, 0) == 0)
50 {
51 return 0;
52 }
53#endif
54
55 TADDR location;
56
57 location = start + (len - 1);
58
59#ifdef DACCESS_COMPILE
60 if (DacReadAll(location, &buff, 1, false) != S_OK)
61 {
62 return 0;
63 }
64#else
65 if (ReadProcessMemory(GetCurrentProcess(), (PVOID)location,
66 &buff, 1, 0) == 0)
67 {
68 return 0;
69 }
70#endif
71
72 //
73 // Now we have to loop thru each and every page in between and touch them.
74 //
75 location = start;
76 while (len > GetOsPageSize())
77 {
78 location += GetOsPageSize();
79 len -= GetOsPageSize();
80
81#ifdef DACCESS_COMPILE
82 if (DacReadAll(location, &buff, 1, false) != S_OK)
83 {
84 return 0;
85 }
86#else
87 if (ReadProcessMemory(GetCurrentProcess(), (PVOID)location,
88 &buff, 1, 0) == 0)
89 {
90 return 0;
91 }
92#endif
93 }
94
95 return 1;
96#endif // !DACCESS_COMPILE && FEATURE_PAL
97}
98
99
100/*******************************************************************/
101/* check to see if 'retAddr' is a valid return address (it points to
102 someplace that has a 'call' right before it), If possible it is
103 it returns the address that was called in whereCalled */
104
105bool isRetAddr(TADDR retAddr, TADDR* whereCalled)
106{
107 CONTRACTL
108 {
109 NOTHROW;
110 GC_NOTRIGGER;
111 SO_NOT_MAINLINE;
112 }
113 CONTRACTL_END;
114
115 // don't waste time values clearly out of range
116 if (retAddr < (TADDR)BOT_MEMORY || retAddr > (TADDR)TOP_MEMORY)
117 {
118 return false;
119 }
120
121 PTR_BYTE spot = PTR_BYTE(retAddr);
122 if (!isMemoryReadable(dac_cast<TADDR>(spot) - 7, 7))
123 {
124 return(false);
125 }
126
127 // Note this is possible to be spoofed, but pretty unlikely
128 *whereCalled = 0;
129 // call XXXXXXXX
130 if (spot[-5] == 0xE8)
131 {
132 *whereCalled = *(PTR_DWORD(retAddr - 4)) + retAddr;
133 return(true);
134 }
135
136 // call [XXXXXXXX]
137 if (spot[-6] == 0xFF && (spot[-5] == 025))
138 {
139 if (isMemoryReadable(*(PTR_TADDR(retAddr - 4)), 4))
140 {
141 *whereCalled = *(PTR_TADDR(*(PTR_TADDR(retAddr - 4))));
142 return(true);
143 }
144 }
145
146 // call [REG+XX]
147 if (spot[-3] == 0xFF && (spot[-2] & ~7) == 0120 && (spot[-2] & 7) != 4)
148 {
149 return(true);
150 }
151
152 if (spot[-4] == 0xFF && spot[-3] == 0124) // call [ESP+XX]
153 {
154 return(true);
155 }
156
157 // call [REG+XXXX]
158 if (spot[-6] == 0xFF && (spot[-5] & ~7) == 0220 && (spot[-5] & 7) != 4)
159 {
160 return(true);
161 }
162
163 if (spot[-7] == 0xFF && spot[-6] == 0224) // call [ESP+XXXX]
164 {
165 return(true);
166 }
167
168 // call [REG]
169 if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0020 && (spot[-1] & 7) != 4 && (spot[-1] & 7) != 5)
170 {
171 return(true);
172 }
173
174 // call REG
175 if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0320 && (spot[-1] & 7) != 4)
176 {
177 return(true);
178 }
179
180 // There are other cases, but I don't believe they are used.
181 return(false);
182}
183
184/*
185 * The remaining methods are included in debug builds only
186 */
187#ifdef _DEBUG
188
189#ifndef DACCESS_COMPILE
190void *DumpEnvironmentBlock(void)
191{
192 CONTRACTL
193 {
194 NOTHROW;
195 GC_NOTRIGGER;
196 }
197 CONTRACTL_END;
198
199 LPTSTR lpszVariable;
200 lpszVariable = (LPTSTR)WszGetEnvironmentStrings();
201
202 while (*lpszVariable)
203 {
204 fprintf(stderr, "%c", *lpszVariable++);
205 }
206
207 fprintf(stderr, "\n");
208
209 return WszGetEnvironmentStrings();
210}
211
212#if defined(_TARGET_X86_) && !defined(FEATURE_PAL)
213/*******************************************************************/
214// Dump the SEH chain to stderr
215void PrintSEHChain(void)
216{
217 CONTRACTL
218 {
219 NOTHROW;
220 GC_NOTRIGGER;
221 }
222 CONTRACTL_END;
223
224 EXCEPTION_REGISTRATION_RECORD* pEHR = GetCurrentSEHRecord();
225
226 while (pEHR != NULL && pEHR != EXCEPTION_CHAIN_END)
227 {
228 fprintf(stderr, "pEHR:0x%x Handler:0x%x\n", (size_t)pEHR, (size_t)pEHR->Handler);
229 pEHR = pEHR->Next;
230 }
231}
232#endif // _TARGET_X86_
233
234/*******************************************************************/
235MethodDesc* IP2MD(ULONG_PTR IP)
236{
237 CONTRACTL
238 {
239 NOTHROW;
240 GC_NOTRIGGER;
241 }
242 CONTRACTL_END;
243
244 return ExecutionManager::GetCodeMethodDesc((PCODE)IP);
245}
246
247/*******************************************************************/
248/* if addr is a valid method table, return a pointer to it */
249MethodTable* AsMethodTable(size_t addr)
250{
251 CONTRACTL
252 {
253 NOTHROW;
254 GC_NOTRIGGER;
255 DEBUG_ONLY;
256 }
257 CONTRACTL_END;
258
259 MethodTable* pValidMT = NULL;
260
261 EX_TRY
262 {
263 MethodTable* pMT = (MethodTable*) addr;
264
265 if (isMemoryReadable((TADDR)pMT, sizeof(MethodTable)))
266 {
267 EEClass* cls = pMT->GetClass_NoLogging();
268
269 if (isMemoryReadable((TADDR)cls, sizeof(EEClass)) &&
270 (cls->GetMethodTable() == pMT))
271 {
272 pValidMT = pMT;
273 }
274 }
275 }
276 EX_CATCH
277 {
278 }
279 EX_END_CATCH(SwallowAllExceptions)
280
281 return(pValidMT);
282}
283
284/*******************************************************************/
285/* if addr is a valid method table, return a pointer to it */
286MethodDesc* AsMethodDesc(size_t addr)
287{
288 CONTRACTL
289 {
290 NOTHROW;
291 GC_NOTRIGGER;
292 DEBUG_ONLY;
293 }
294 CONTRACTL_END;
295
296 if (!IS_ALIGNED(addr, sizeof(void*)))
297 return(0);
298
299 MethodDesc* pValidMD = NULL;
300
301 // We try to avoid the most AVs by explicitit calls to isMemoryReadable below, but rare cases can still get through
302 // if we are unlucky.
303 AVInRuntimeImplOkayHolder AVOkay;
304
305 EX_TRY
306 {
307 MethodDesc* pMD = (MethodDesc*) addr;
308
309 if (isMemoryReadable((TADDR)pMD, sizeof(MethodDesc)))
310 {
311 MethodDescChunk *chunk = pMD->GetMethodDescChunk();
312
313 if (isMemoryReadable((TADDR)chunk, sizeof(MethodDescChunk)))
314 {
315 RelativeFixupPointer<PTR_MethodTable> * ppMT = chunk->GetMethodTablePtr();
316
317 // The MethodTable is stored as a RelativeFixupPointer which does an
318 // extra indirection if the address is tagged (the low bit is set).
319 // That could AV if we don't check it first.
320
321 if (!ppMT->IsTagged((TADDR)ppMT) || isMemoryReadable((TADDR)ppMT->GetValuePtr(), sizeof(MethodTable*)))
322 {
323 if (AsMethodTable((size_t)RelativeFixupPointer<PTR_MethodTable>::GetValueAtPtr((TADDR)ppMT)) != 0)
324 {
325 pValidMD = pMD;
326 }
327 }
328 }
329 }
330 }
331 EX_CATCH
332 {
333 }
334 EX_END_CATCH(SwallowAllExceptions)
335
336
337 return(pValidMD);
338}
339
340
341// This function will return NULL if the buffer is not large enough.
342/*******************************************************************/
343
344wchar_t* formatMethodTable(MethodTable* pMT,
345 __out_z __inout_ecount(bufSize) wchar_t* buff,
346 DWORD bufSize)
347{
348 CONTRACTL
349 {
350 NOTHROW;
351 GC_NOTRIGGER;
352 }
353 CONTRACTL_END;
354
355 if(bufSize == 0)
356 {
357 return NULL;
358 }
359
360 buff[ bufSize - 1] = W('\0');
361
362 DefineFullyQualifiedNameForClass();
363
364 LPCUTF8 clsName = GetFullyQualifiedNameForClass(pMT);
365
366 if (clsName != 0)
367 {
368 if(_snwprintf_s(buff, bufSize - 1, _TRUNCATE, W("%S"), clsName) < 0)
369 {
370 return NULL;
371 }
372
373 buff[ bufSize - 1] = W('\0');
374
375 }
376 return(buff);
377}
378
379/*******************************************************************/
380// This function will return NULL if the buffer is not large enough, otherwise it will
381// return the buffer position for next write.
382/*******************************************************************/
383
384wchar_t* formatMethodDesc(MethodDesc* pMD,
385 __out_z __inout_ecount(bufSize) wchar_t* buff,
386 DWORD bufSize)
387{
388 CONTRACTL
389 {
390 NOTHROW;
391 GC_NOTRIGGER;
392 }
393 CONTRACTL_END;
394
395 if(bufSize == 0)
396 {
397 return NULL;
398 }
399
400 buff = formatMethodTable(pMD->GetMethodTable(), buff, bufSize);
401 if(buff == NULL)
402 {
403 return NULL;
404 }
405
406 buff[bufSize - 1] = W('\0'); // this will guarantee the buffer is also NULL-terminated
407 if(_snwprintf_s( &buff[wcslen(buff)] , bufSize - wcslen(buff) - 1, _TRUNCATE, W("::%S"), pMD->GetName()) < 0)
408 {
409 return NULL;
410 }
411
412#ifdef _DEBUG
413 if (pMD->m_pszDebugMethodSignature)
414 {
415 if(_snwprintf_s(&buff[wcslen(buff)],
416 bufSize - wcslen(buff) - 1,
417 _TRUNCATE,
418 W(" %S"),
419 pMD->m_pszDebugMethodSignature) < 0)
420 {
421 return NULL;
422 }
423
424 }
425#endif
426
427 if(_snwprintf_s(&buff[wcslen(buff)], bufSize - wcslen(buff) - 1, _TRUNCATE, W("(%x)"), (size_t)pMD) < 0)
428 {
429 return NULL;
430 }
431
432 return(buff);
433}
434
435
436
437
438/*******************************************************************/
439/* dump the stack, pretty printing IL methods if possible. This
440 routine is very robust. It will never cause an access violation
441 and it always find return addresses if they are on the stack
442 (it may find some spurious ones however). */
443
444int dumpStack(BYTE* topOfStack, unsigned len)
445{
446 CONTRACTL
447 {
448 THROWS;
449 GC_NOTRIGGER;
450 }
451 CONTRACTL_END;
452
453 size_t* top = (size_t*) topOfStack;
454 size_t* end = (size_t*) &topOfStack[len];
455
456 size_t* ptr = (size_t*) (((size_t) top) & ~3); // make certain dword aligned.
457 TADDR whereCalled;
458
459 WszOutputDebugString(W("***************************************************\n"));
460
461 CQuickBytes qb;
462
463 int nLen = MAX_CLASSNAME_LENGTH * 4 + 400; // this should be enough
464
465 wchar_t *buff = (wchar_t *) qb.AllocThrows(nLen * sizeof(wchar_t));
466
467 while (ptr < end)
468 {
469 buff[nLen - 1] = W('\0');
470
471 wchar_t* buffPtr = buff;
472
473 // stop if we hit unmapped pages
474 if (!isMemoryReadable((TADDR)ptr, sizeof(TADDR)))
475 {
476 break;
477 }
478
479 if (isRetAddr((TADDR)*ptr, &whereCalled))
480 {
481 if (_snwprintf_s(buffPtr, buff+NumItems(buff)-buffPtr-1, _TRUNCATE, W("STK[%08X] = %08X "), (size_t)ptr, *ptr) <0)
482 {
483 return(0);
484 }
485
486 buffPtr += wcslen(buffPtr);
487
488 const wchar_t* kind = W("RETADDR ");
489
490 // Is this a stub (is the return address a MethodDesc?
491 MethodDesc* ftn = AsMethodDesc(*ptr);
492
493 if (ftn != 0)
494 {
495
496 kind = W(" MD PARAM");
497
498 // If another true return address is not directly before it, it is just
499 // a methodDesc param.
500 TADDR prevRetAddr = ptr[1];
501
502 if (isRetAddr(prevRetAddr, &whereCalled) && AsMethodDesc(prevRetAddr) == 0)
503 {
504 kind = W("STUBCALL");
505 }
506 else
507 {
508 // Is it the magic sequence used by CallDescr?
509 if (isMemoryReadable(prevRetAddr - sizeof(short),
510 sizeof(short)) &&
511 ((short*) prevRetAddr)[-1] == 0x5A59) // Pop ECX POP EDX
512 {
513 kind = W("STUBCALL");
514 }
515
516 }
517
518 }
519 else // Is it some other code the EE knows about?
520 {
521 ftn = ExecutionManager::GetCodeMethodDesc((PCODE)(*ptr));
522 }
523
524 if(_snwprintf_s(buffPtr, buff+ nLen -buffPtr-1, _TRUNCATE, W("%s "), kind) < 0)
525 {
526 return(0);
527 }
528
529 buffPtr += wcslen(buffPtr);
530
531 if (ftn != 0)
532 {
533 // buffer is not large enough
534 if( formatMethodDesc(ftn, buffPtr, static_cast<DWORD>(buff+ nLen -buffPtr-1)) == NULL)
535 {
536 return(0);
537 }
538
539 buffPtr += wcslen(buffPtr);
540 }
541 else
542 {
543 wcsncpy_s(buffPtr, nLen - (buffPtr - buff), W("<UNKNOWN FTN>"), _TRUNCATE);
544 buffPtr += wcslen(buffPtr);
545 }
546
547 if (whereCalled != 0)
548 {
549 if(_snwprintf_s(buffPtr, buff+ nLen -buffPtr-1, _TRUNCATE, W(" Caller called Entry %X"), whereCalled) <0)
550 {
551 return(0);
552 }
553
554 buffPtr += wcslen(buffPtr);
555 }
556
557 wcsncpy_s(buffPtr, nLen - (buffPtr - buff), W("\n"), _TRUNCATE);
558 buffPtr += wcslen(buffPtr);
559 WszOutputDebugString(buff);
560 }
561
562 MethodTable* pMT = AsMethodTable(*ptr);
563 if (pMT != 0)
564 {
565 buffPtr = buff;
566 if( _snwprintf_s(buffPtr, buff+ nLen -buffPtr-1, _TRUNCATE, W("STK[%08X] = %08X MT PARAM "), (size_t)ptr, *ptr ) <0)
567 {
568 return(0);
569 }
570
571 buffPtr += wcslen(buffPtr);
572
573 if( formatMethodTable(pMT, buffPtr, static_cast<DWORD>(buff+ nLen -buffPtr-1)) == NULL)
574 {
575 return(0);
576 }
577
578 buffPtr += wcslen(buffPtr);
579
580 wcsncpy_s(buffPtr, nLen - (buffPtr - buff), W("\n"), _TRUNCATE);
581 WszOutputDebugString(buff);
582
583 }
584
585 ptr++;
586
587 } // while
588
589 return(0);
590}
591
592/*******************************************************************/
593/* dump the stack from the current ESP. Stop when we reach a 64K
594 boundary */
595int DumpCurrentStack()
596{
597 CONTRACTL
598 {
599 THROWS;
600 GC_NOTRIGGER;
601 }
602 CONTRACTL_END;
603
604#ifdef _TARGET_X86_
605 BYTE* top = (BYTE *)GetCurrentSP();
606
607 // go back at most 64K, it will stop if we go off the
608 // top to unmapped memory
609 return(dumpStack(top, 0xFFFF));
610#else
611 _ASSERTE(!"@NYI - DumpCurrentStack(DebugHelp.cpp)");
612 return 0;
613#endif // _TARGET_X86_
614}
615
616/*******************************************************************/
617WCHAR* StringVal(STRINGREF objref)
618{
619 CONTRACTL
620 {
621 THROWS;
622 GC_NOTRIGGER;
623 }
624 CONTRACTL_END;
625
626 return(objref->GetBuffer());
627}
628
629LPCUTF8 NameForMethodTable(UINT_PTR pMT)
630{
631 CONTRACTL
632 {
633 NOTHROW;
634 GC_NOTRIGGER;
635 }
636 CONTRACTL_END;
637
638 DefineFullyQualifiedNameForClass();
639 LPCUTF8 clsName = GetFullyQualifiedNameForClass(((MethodTable*)pMT));
640 // Note we're returning local stack space - this should be OK for using in the debugger though
641 return clsName;
642}
643
644LPCUTF8 ClassNameForObject(UINT_PTR obj)
645{
646 CONTRACTL
647 {
648 NOTHROW;
649 GC_NOTRIGGER;
650 }
651 CONTRACTL_END;
652
653 return(NameForMethodTable((UINT_PTR)(((Object*)obj)->GetMethodTable())));
654}
655
656LPCUTF8 ClassNameForOBJECTREF(OBJECTREF obj)
657{
658 CONTRACTL
659 {
660 NOTHROW;
661 GC_NOTRIGGER;
662 }
663 CONTRACTL_END;
664
665 return(ClassNameForObject((UINT_PTR)(OBJECTREFToObject(obj))));
666}
667
668LPCUTF8 NameForMethodDesc(UINT_PTR pMD)
669{
670 CONTRACTL
671 {
672 NOTHROW;
673 GC_NOTRIGGER;
674 }
675 CONTRACTL_END;
676
677 return(((MethodDesc*)pMD)->GetName());
678}
679
680LPCUTF8 ClassNameForMethodDesc(UINT_PTR pMD)
681{
682 CONTRACTL
683 {
684 NOTHROW;
685 GC_NOTRIGGER;
686 }
687 CONTRACTL_END;
688
689 DefineFullyQualifiedNameForClass ();
690 return GetFullyQualifiedNameForClass(((MethodDesc*)pMD)->GetMethodTable());
691}
692
693PCCOR_SIGNATURE RawSigForMethodDesc(MethodDesc* pMD)
694{
695 CONTRACTL
696 {
697 NOTHROW;
698 GC_NOTRIGGER;
699 }
700 CONTRACTL_END;
701
702 return(pMD->GetSig());
703}
704
705Thread * CurrentThreadInfo ()
706{
707 CONTRACTL
708 {
709 NOTHROW;
710 GC_NOTRIGGER;
711 }
712 CONTRACTL_END;
713
714 return GetThread ();
715}
716
717AppDomain *GetAppDomainForObject(UINT_PTR obj)
718{
719 CONTRACTL
720 {
721 NOTHROW;
722 GC_NOTRIGGER;
723 }
724 CONTRACTL_END;
725
726 return ((Object*)obj)->GetAppDomain();
727}
728
729ADIndex GetAppDomainIndexForObject(UINT_PTR obj)
730{
731 CONTRACTL
732 {
733 NOTHROW;
734 GC_NOTRIGGER;
735 }
736 CONTRACTL_END;
737
738 return ((Object*)obj)->GetHeader()->GetAppDomainIndex();
739}
740
741AppDomain *GetAppDomainForObjectHeader(UINT_PTR hdr)
742{
743 CONTRACTL
744 {
745 NOTHROW;
746 GC_NOTRIGGER;
747 }
748 CONTRACTL_END;
749
750 ADIndex indx = ((ObjHeader*)hdr)->GetAppDomainIndex();
751 if (!indx.m_dwIndex)
752 {
753 return NULL;
754 }
755
756 return SystemDomain::GetAppDomainAtIndex(indx);
757}
758
759ADIndex GetAppDomainIndexForObjectHeader(UINT_PTR hdr)
760{
761 CONTRACTL
762 {
763 NOTHROW;
764 GC_NOTRIGGER;
765 }
766 CONTRACTL_END;
767
768 return ((ObjHeader*)hdr)->GetAppDomainIndex();
769}
770
771SyncBlock *GetSyncBlockForObject(UINT_PTR obj)
772{
773 CONTRACTL
774 {
775 NOTHROW;
776 GC_NOTRIGGER;
777 }
778 CONTRACTL_END;
779
780 return ((Object*)obj)->GetHeader()->PassiveGetSyncBlock();
781}
782
783/*******************************************************************/
784void PrintMethodTable(UINT_PTR pMT)
785{
786 CONTRACTL
787 {
788 NOTHROW;
789 GC_TRIGGERS;
790 }
791 CONTRACTL_END;
792
793 MethodTable * p = (MethodTable *)pMT;
794
795 DefineFullyQualifiedNameForClass();
796 LPCUTF8 name = GetFullyQualifiedNameForClass(p);
797 p->DebugDumpVtable(name, true);
798 p->DebugDumpFieldLayout(name, true);
799 p->DebugDumpGCDesc(name, true);
800}
801
802void PrintTableForMethodDesc(UINT_PTR pMD)
803{
804 CONTRACTL
805 {
806 NOTHROW;
807 GC_TRIGGERS;
808 }
809 CONTRACTL_END;
810
811 PrintMethodTable((UINT_PTR) ((MethodDesc *)pMD)->GetMethodTable() );
812}
813
814void PrintException(OBJECTREF pObjectRef)
815{
816 CONTRACTL
817 {
818 THROWS;
819 GC_NOTRIGGER;
820 }
821 CONTRACTL_END;
822
823
824 if(pObjectRef == NULL)
825 {
826 return;
827 }
828
829 GCPROTECT_BEGIN(pObjectRef);
830
831 if (!IsException(pObjectRef->GetMethodTable()))
832 {
833 printf("Specified object is not an exception object.\n");
834 }
835 else
836 {
837 MethodDescCallSite internalToString(METHOD__EXCEPTION__INTERNAL_TO_STRING, &pObjectRef);
838
839 ARG_SLOT arg[1] = {
840 ObjToArgSlot(pObjectRef)
841 };
842
843 STRINGREF str = internalToString.Call_RetSTRINGREF(arg);
844
845 if(str->GetBuffer() != NULL)
846 {
847 WszOutputDebugString(str->GetBuffer());
848 }
849 }
850
851 GCPROTECT_END();
852}
853
854void PrintException(UINT_PTR pObject)
855{
856 CONTRACTL
857 {
858 NOTHROW;
859 GC_NOTRIGGER;
860 }
861 CONTRACTL_END;
862
863 OBJECTREF pObjectRef = NULL;
864 GCPROTECT_BEGIN(pObjectRef);
865 GCPROTECT_END();
866}
867
868/*******************************************************************/
869/* sends a current stack trace to the debug window */
870
871const char* FormatSig(MethodDesc* pMD, AppDomain *pDomain, AllocMemTracker *pamTracker);
872
873struct PrintCallbackData {
874 BOOL toStdout;
875 BOOL withAppDomain;
876#ifdef _DEBUG
877 BOOL toLOG;
878#endif
879};
880
881StackWalkAction PrintStackTraceCallback(CrawlFrame* pCF, VOID* pData)
882{
883 CONTRACTL
884 {
885 DISABLED(NOTHROW);
886 DISABLED(GC_TRIGGERS);
887 }
888 CONTRACTL_END;
889
890 CONTRACT_VIOLATION(ThrowsViolation);
891
892 MethodDesc* pMD = pCF->GetFunction();
893 const int nLen = 2048 - 1; // keep one character for "\n"
894 wchar_t *buff = (wchar_t*)alloca((nLen + 1) * sizeof(wchar_t));
895 buff[0] = 0;
896 buff[nLen-1] = W('\0'); // make sure the buffer is always NULL-terminated
897
898 PrintCallbackData *pCBD = (PrintCallbackData *)pData;
899
900 if (pMD != 0)
901 {
902 MethodTable * pMT = pMD->GetMethodTable();
903
904 if (pCBD->withAppDomain)
905 {
906 if(_snwprintf_s(&buff[wcslen(buff)],
907 nLen - wcslen(buff) - 1,
908 _TRUNCATE,
909 W("{[%3.3x] %s} "),
910 pCF->GetAppDomain()->GetId().m_dwId,
911 pCF->GetAppDomain()->GetFriendlyName(FALSE)) < 0)
912 {
913 return SWA_CONTINUE;
914 }
915 }
916
917 DefineFullyQualifiedNameForClass();
918
919 LPCUTF8 clsName = GetFullyQualifiedNameForClass(pMT);
920
921 if (clsName != 0)
922 {
923 if(_snwprintf_s(&buff[wcslen(buff)], nLen - wcslen(buff) - 1, _TRUNCATE, W("%S::"), clsName) < 0)
924 {
925 return SWA_CONTINUE;
926 }
927 }
928
929 // This prematurely suppressrelease'd AmTracker will leak any memory allocated by FormatSig.
930 // But this routine is diagnostic aid, not customer-reachable so we won't bother to plug.
931 AllocMemTracker dummyAmTracker;
932
933 int buffLen = _snwprintf_s(&buff[wcslen(buff)],
934 nLen - wcslen(buff) - 1,
935 _TRUNCATE,
936 W("%S %S "),
937 pMD->GetName(),
938 FormatSig(pMD, pCF->GetAppDomain(), &dummyAmTracker));
939
940 dummyAmTracker.SuppressRelease();
941 if (buffLen < 0 )
942 {
943 return SWA_CONTINUE;
944 }
945
946
947 if (pCF->IsFrameless() && pCF->GetJitManager() != 0) {
948
949 PREGDISPLAY regs = pCF->GetRegisterSet();
950
951 DWORD offset = pCF->GetRelOffset();
952
953 TADDR start = pCF->GetCodeInfo()->GetStartAddress();
954
955 if(_snwprintf_s(&buff[wcslen(buff)],
956 nLen - wcslen(buff) - 1,
957 _TRUNCATE,
958 W("JIT ESP:%X MethStart:%X EIP:%X(rel %X)"),
959 (size_t)GetRegdisplaySP(regs),
960 (size_t)start,
961 (size_t)GetControlPC(regs),
962 offset) < 0)
963 {
964 return SWA_CONTINUE;
965 }
966
967 }
968 else
969 {
970
971 if(_snwprintf_s(&buff[wcslen(buff)], nLen - wcslen(buff) - 1, _TRUNCATE, W("EE implemented")) < 0)
972 {
973 return SWA_CONTINUE;
974 }
975 }
976
977 }
978 else
979 {
980 Frame* frame = pCF->GetFrame();
981
982 if(_snwprintf_s(&buff[wcslen(buff)],
983 nLen - wcslen(buff) - 1,
984 _TRUNCATE,
985 W("EE Frame is") LFMT_ADDR,
986 (size_t)DBG_ADDR(frame)) < 0)
987 {
988 return SWA_CONTINUE;
989 }
990 }
991
992 if (pCBD->toStdout)
993 {
994 wcscat_s(buff, nLen + 1, W("\n"));
995 PrintToStdOutW(buff);
996 }
997#ifdef _DEBUG
998 else if (pCBD->toLOG)
999 {
1000 MAKE_ANSIPTR_FROMWIDE(sbuff, buff);
1001 // For LogSpewAlways to work rightr the "\n" (newline)
1002 // must be in the fmt string not part of the args
1003 LogSpewAlways(" %s\n", sbuff);
1004 }
1005#endif
1006 else
1007 {
1008 wcscat_s(buff, nLen + 1, W("\n"));
1009 WszOutputDebugString(buff);
1010 }
1011
1012 return SWA_CONTINUE;
1013}
1014
1015void PrintStackTrace()
1016{
1017 CONTRACTL
1018 {
1019 DISABLED(NOTHROW);
1020 DISABLED(GC_TRIGGERS);
1021 }
1022 CONTRACTL_END;
1023
1024 WszOutputDebugString(W("***************************************************\n"));
1025 PrintCallbackData cbd = {0, 0};
1026 GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0);
1027}
1028
1029void PrintStackTraceToStdout()
1030{
1031 CONTRACTL
1032 {
1033 DISABLED(NOTHROW);
1034 DISABLED(GC_TRIGGERS);
1035 }
1036 CONTRACTL_END;
1037
1038 PrintCallbackData cbd = {1, 0};
1039 GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0);
1040}
1041
1042#ifdef _DEBUG
1043void PrintStackTraceToLog()
1044{
1045 CONTRACTL
1046 {
1047 DISABLED(NOTHROW);
1048 DISABLED(GC_TRIGGERS);
1049 }
1050 CONTRACTL_END;
1051
1052 PrintCallbackData cbd = {0, 0, 1};
1053 GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0);
1054}
1055#endif
1056
1057void PrintStackTraceWithAD()
1058{
1059 CONTRACTL
1060 {
1061 DISABLED(NOTHROW);
1062 DISABLED(GC_TRIGGERS);
1063 }
1064 CONTRACTL_END;
1065
1066 WszOutputDebugString(W("***************************************************\n"));
1067 PrintCallbackData cbd = {0, 1};
1068 GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0);
1069}
1070
1071void PrintStackTraceWithADToStdout()
1072{
1073 CONTRACTL
1074 {
1075 DISABLED(NOTHROW);
1076 DISABLED(GC_TRIGGERS);
1077 }
1078 CONTRACTL_END;
1079
1080 PrintCallbackData cbd = {1, 1};
1081 GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0);
1082}
1083
1084#ifdef _DEBUG
1085void PrintStackTraceWithADToLog()
1086{
1087 CONTRACTL
1088 {
1089 NOTHROW;
1090 GC_NOTRIGGER;
1091 }
1092 CONTRACTL_END;
1093
1094 PrintCallbackData cbd = {0, 1, 1};
1095 GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0);
1096}
1097
1098void PrintStackTraceWithADToLog(Thread *pThread)
1099{
1100 CONTRACTL
1101 {
1102 DISABLED(NOTHROW);
1103 DISABLED(GC_TRIGGERS);
1104 }
1105 CONTRACTL_END;
1106
1107 PrintCallbackData cbd = {0, 1, 1};
1108 pThread->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0);
1109}
1110#endif
1111
1112/*******************************************************************/
1113// Get the system or current domain from the thread.
1114BaseDomain* GetSystemDomain()
1115{
1116 CONTRACTL
1117 {
1118 NOTHROW;
1119 GC_NOTRIGGER;
1120 }
1121 CONTRACTL_END;
1122
1123 return SystemDomain::System();
1124}
1125
1126AppDomain* GetCurrentDomain()
1127{
1128 CONTRACTL
1129 {
1130 NOTHROW;
1131 GC_NOTRIGGER;
1132 }
1133 CONTRACTL_END;
1134
1135 return SystemDomain::GetCurrentDomain();
1136}
1137
1138void PrintDomainName(size_t ob)
1139{
1140 CONTRACTL
1141 {
1142 THROWS;
1143 GC_NOTRIGGER;
1144 }
1145 CONTRACTL_END;
1146
1147 AppDomain* dm = (AppDomain*) ob;
1148 LPCWSTR st = dm->GetFriendlyName(FALSE);
1149
1150 if(st != NULL)
1151 {
1152 WszOutputDebugString(st);
1153 }
1154 else
1155 {
1156 WszOutputDebugString(W("<Domain with no Name>"));
1157 }
1158}
1159
1160#if defined(_TARGET_X86_)
1161
1162#include "gcdump.h"
1163
1164#include "../gcdump/i386/gcdumpx86.cpp"
1165
1166#include "../gcdump/gcdump.cpp"
1167
1168/*********************************************************************/
1169void printfToDbgOut(const char* fmt, ...)
1170{
1171 CONTRACTL
1172 {
1173 NOTHROW;
1174 GC_NOTRIGGER;
1175 }
1176 CONTRACTL_END;
1177
1178 va_list args;
1179 va_start(args, fmt);
1180
1181 char buffer[4096];
1182 _vsnprintf_s(buffer, COUNTOF(buffer), _TRUNCATE, fmt, args);
1183
1184 va_end(args);
1185 OutputDebugStringA( buffer );
1186}
1187
1188void DumpGCInfo(MethodDesc* method)
1189{
1190 CONTRACTL
1191 {
1192 NOTHROW;
1193 GC_NOTRIGGER;
1194 }
1195 CONTRACTL_END;
1196
1197 PCODE methodStart = method->GetNativeCode();
1198
1199 if (methodStart == 0)
1200 {
1201 return;
1202 }
1203
1204 EECodeInfo codeInfo(methodStart);
1205 _ASSERTE(codeInfo.GetRelOffset() == 0);
1206
1207 ICodeManager* codeMan = codeInfo.GetCodeManager();
1208 GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken();
1209
1210 unsigned methodSize = (unsigned)codeMan->GetFunctionSize(gcInfoToken);
1211
1212 GCDump gcDump(gcInfoToken.Version);
1213 PTR_CBYTE gcInfo = PTR_CBYTE(gcInfoToken.Info);
1214
1215 gcDump.gcPrintf = printfToDbgOut;
1216
1217 InfoHdr header;
1218
1219 printfToDbgOut ("Method info block:\n");
1220 gcInfo += gcDump.DumpInfoHdr(gcInfo, &header, &methodSize, 0);
1221
1222 printfToDbgOut ("\n");
1223 printfToDbgOut ("Pointer table:\n");
1224
1225 gcInfo += gcDump.DumpGCTable(gcInfo, header, methodSize, 0);
1226}
1227
1228void DumpGCInfoMD(size_t method)
1229{
1230 CONTRACTL
1231 {
1232 NOTHROW;
1233 GC_NOTRIGGER;
1234 }
1235 CONTRACTL_END;
1236
1237 DumpGCInfo((MethodDesc*) method);
1238}
1239#endif
1240
1241
1242#ifdef LOGGING
1243void LogStackTrace()
1244{
1245 WRAPPER_NO_CONTRACT;
1246
1247 PrintCallbackData cbd = {0, 0, 1};
1248 GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd,ALLOW_ASYNC_STACK_WALK, 0);
1249}
1250#endif
1251
1252#endif // #ifndef DACCESS_COMPILE
1253#endif //_DEBUG
1254