diff options
Diffstat (limited to 'src/vm/debughelp.cpp')
-rw-r--r-- | src/vm/debughelp.cpp | 1246 |
1 files changed, 1246 insertions, 0 deletions
diff --git a/src/vm/debughelp.cpp b/src/vm/debughelp.cpp new file mode 100644 index 0000000000..df769455aa --- /dev/null +++ b/src/vm/debughelp.cpp @@ -0,0 +1,1246 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +#include "common.h" + +/*******************************************************************/ +/* The folowing routines used to exist in all builds so they could called from the + * debugger before we had strike. + * Now most of them are only inclued in debug builds for diagnostics purposes. +*/ +/*******************************************************************/ + +#include "stdlib.h" + +BOOL isMemoryReadable(const TADDR start, unsigned len) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + // + // To accomplish this in a no-throw way, we have to touch each and every page + // and see if it is in memory or not. + // + + // + // Touch the first and last bytes. + // + char buff; + +#ifdef DACCESS_COMPILE + if (DacReadAll(start, &buff, 1, false) != S_OK) + { + return 0; + } +#else + if (ReadProcessMemory(GetCurrentProcess(), (PVOID)start, &buff, 1, 0) == 0) + { + return 0; + } +#endif + + TADDR location; + + location = start + (len - 1); + +#ifdef DACCESS_COMPILE + if (DacReadAll(location, &buff, 1, false) != S_OK) + { + return 0; + } +#else + if (ReadProcessMemory(GetCurrentProcess(), (PVOID)location, + &buff, 1, 0) == 0) + { + return 0; + } +#endif + + // + // Now we have to loop thru each and every page in between and touch them. + // + location = start; + while (len > PAGE_SIZE) + { + location += PAGE_SIZE; + len -= PAGE_SIZE; + +#ifdef DACCESS_COMPILE + if (DacReadAll(location, &buff, 1, false) != S_OK) + { + return 0; + } +#else + if (ReadProcessMemory(GetCurrentProcess(), (PVOID)location, + &buff, 1, 0) == 0) + { + return 0; + } +#endif + } + + return 1; +} + + +/*******************************************************************/ +/* check to see if 'retAddr' is a valid return address (it points to + someplace that has a 'call' right before it), If possible it is + it returns the address that was called in whereCalled */ + +bool isRetAddr(TADDR retAddr, TADDR* whereCalled) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_NOT_MAINLINE; + } + CONTRACTL_END; + + // don't waste time values clearly out of range + if (retAddr < (TADDR)BOT_MEMORY || retAddr > (TADDR)TOP_MEMORY) + { + return false; + } + + PTR_BYTE spot = PTR_BYTE(retAddr); + if (!isMemoryReadable(dac_cast<TADDR>(spot) - 7, 7)) + { + return(false); + } + + // Note this is possible to be spoofed, but pretty unlikely + *whereCalled = 0; + // call XXXXXXXX + if (spot[-5] == 0xE8) + { + *whereCalled = *(PTR_DWORD(retAddr - 4)) + retAddr; + return(true); + } + + // call [XXXXXXXX] + if (spot[-6] == 0xFF && (spot[-5] == 025)) + { + if (isMemoryReadable(*(PTR_TADDR(retAddr - 4)), 4)) + { + *whereCalled = *(PTR_TADDR(*(PTR_TADDR(retAddr - 4)))); + return(true); + } + } + + // call [REG+XX] + if (spot[-3] == 0xFF && (spot[-2] & ~7) == 0120 && (spot[-2] & 7) != 4) + { + return(true); + } + + if (spot[-4] == 0xFF && spot[-3] == 0124) // call [ESP+XX] + { + return(true); + } + + // call [REG+XXXX] + if (spot[-6] == 0xFF && (spot[-5] & ~7) == 0220 && (spot[-5] & 7) != 4) + { + return(true); + } + + if (spot[-7] == 0xFF && spot[-6] == 0224) // call [ESP+XXXX] + { + return(true); + } + + // call [REG] + if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0020 && (spot[-1] & 7) != 4 && (spot[-1] & 7) != 5) + { + return(true); + } + + // call REG + if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0320 && (spot[-1] & 7) != 4) + { + return(true); + } + + // There are other cases, but I don't believe they are used. + return(false); +} + +/* + * The remaining methods are included in debug builds only + */ +#ifdef _DEBUG + +#ifndef DACCESS_COMPILE +void *DumpEnvironmentBlock(void) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + LPTSTR lpszVariable; + lpszVariable = (LPTSTR)WszGetEnvironmentStrings(); + + while (*lpszVariable) + { + fprintf(stderr, "%c", *lpszVariable++); + } + + fprintf(stderr, "\n"); + + return WszGetEnvironmentStrings(); +} + +#if defined(_TARGET_X86_) +/*******************************************************************/ +// Dump the SEH chain to stderr +void PrintSEHChain(void) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + EXCEPTION_REGISTRATION_RECORD* pEHR = GetCurrentSEHRecord(); + + while (pEHR != NULL && pEHR != EXCEPTION_CHAIN_END) + { + fprintf(stderr, "pEHR:0x%x Handler:0x%x\n", (size_t)pEHR, (size_t)pEHR->Handler); + pEHR = pEHR->Next; + } +} +#endif // _TARGET_X86_ + +/*******************************************************************/ +MethodDesc* IP2MD(ULONG_PTR IP) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return ExecutionManager::GetCodeMethodDesc((PCODE)IP); +} + +/*******************************************************************/ +/* if addr is a valid method table, return a poitner to it */ +MethodTable* AsMethodTable(size_t addr) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + DEBUG_ONLY; + } + CONTRACTL_END; + + MethodTable* pValidMT = NULL; + + EX_TRY + { + MethodTable* pMT = (MethodTable*) addr; + + if (isMemoryReadable((TADDR)pMT, sizeof(MethodTable))) + { + EEClass* cls = pMT->GetClass_NoLogging(); + + if (isMemoryReadable((TADDR)cls, sizeof(EEClass)) && + (cls->GetMethodTable() == pMT)) + { + pValidMT = pMT; + } + } + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions) + + return(pValidMT); +} + +/*******************************************************************/ +/* if addr is a valid method table, return a pointer to it */ +MethodDesc* AsMethodDesc(size_t addr) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + DEBUG_ONLY; + } + CONTRACTL_END; + + if (!IS_ALIGNED(addr, sizeof(void*))) + return(0); + + MethodDesc* pValidMD = NULL; + + // We try to avoid the most AVs by explicitit calls to isMemoryReadable below, but rare cases can still get through + // if we are unlucky. + AVInRuntimeImplOkayHolder AVOkay; + + EX_TRY + { + MethodDesc* pMD = (MethodDesc*) addr; + + if (isMemoryReadable((TADDR)pMD, sizeof(MethodDesc))) + { + MethodDescChunk *chunk = pMD->GetMethodDescChunk(); + + if (isMemoryReadable((TADDR)chunk, sizeof(MethodDescChunk))) + { + RelativeFixupPointer<PTR_MethodTable> * ppMT = chunk->GetMethodTablePtr(); + + // The MethodTable is stored as a RelativeFixupPointer which does an + // extra indirection if the address is tagged (the low bit is set). + // That could AV if we don't check it first. + + if (!ppMT->IsTagged((TADDR)ppMT) || isMemoryReadable((TADDR)ppMT->GetValuePtr((TADDR)ppMT), sizeof(MethodTable*))) + { + if (AsMethodTable((size_t)RelativeFixupPointer<PTR_MethodTable>::GetValueAtPtr((TADDR)ppMT)) != 0) + { + pValidMD = pMD; + } + } + } + } + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions) + + + return(pValidMD); +} + + +// This function will return NULL if the buffer is not large enough. +/*******************************************************************/ + +wchar_t* formatMethodTable(MethodTable* pMT, + __out_z __inout_ecount(bufSize) wchar_t* buff, + DWORD bufSize) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + if(bufSize == 0) + { + return NULL; + } + + buff[ bufSize - 1] = W('\0'); + + DefineFullyQualifiedNameForClass(); + + LPCUTF8 clsName = GetFullyQualifiedNameForClass(pMT); + + if (clsName != 0) + { + if(_snwprintf_s(buff, bufSize - 1, _TRUNCATE, W("%S"), clsName) < 0) + { + return NULL; + } + + buff[ bufSize - 1] = W('\0'); + + } + return(buff); +} + +/*******************************************************************/ +// This function will return NULL if the buffer is not large enough, otherwise it will +// return the buffer position for next write. +/*******************************************************************/ + +wchar_t* formatMethodDesc(MethodDesc* pMD, + __out_z __inout_ecount(bufSize) wchar_t* buff, + DWORD bufSize) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + if(bufSize == 0) + { + return NULL; + } + + buff = formatMethodTable(pMD->GetMethodTable(), buff, bufSize); + if(buff == NULL) + { + return NULL; + } + + buff[bufSize - 1] = W('\0'); // this will guarantee the buffer is also NULL-terminated + if(_snwprintf_s( &buff[lstrlenW(buff)] , bufSize -lstrlenW(buff) - 1, _TRUNCATE, W("::%S"), pMD->GetName()) < 0) + { + return NULL; + } + +#ifdef _DEBUG + if (pMD->m_pszDebugMethodSignature) + { + if(_snwprintf_s(&buff[lstrlenW(buff)], + bufSize -lstrlenW(buff) - 1, + _TRUNCATE, + W(" %S"), + pMD->m_pszDebugMethodSignature) < 0) + { + return NULL; + } + + } +#endif + + if(_snwprintf_s(&buff[lstrlenW(buff)], bufSize -lstrlenW(buff) - 1, _TRUNCATE, W("(%x)"), (size_t)pMD) < 0) + { + return NULL; + } + + return(buff); +} + + + + +/*******************************************************************/ +/* dump the stack, pretty printing IL methods if possible. This + routine is very robust. It will never cause an access violation + and it always find return addresses if they are on the stack + (it may find some spurious ones however). */ + +int dumpStack(BYTE* topOfStack, unsigned len) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + size_t* top = (size_t*) topOfStack; + size_t* end = (size_t*) &topOfStack[len]; + + size_t* ptr = (size_t*) (((size_t) top) & ~3); // make certain dword aligned. + TADDR whereCalled; + + WszOutputDebugString(W("***************************************************\n")); + + CQuickBytes qb; + + int nLen = MAX_CLASSNAME_LENGTH * 4 + 400; // this should be enough + + wchar_t *buff = (wchar_t *) qb.AllocThrows(nLen * sizeof(wchar_t)); + + while (ptr < end) + { + buff[nLen - 1] = W('\0'); + + wchar_t* buffPtr = buff; + + // stop if we hit unmapped pages + if (!isMemoryReadable((TADDR)ptr, sizeof(TADDR))) + { + break; + } + + if (isRetAddr((TADDR)*ptr, &whereCalled)) + { + if (_snwprintf_s(buffPtr, buff+NumItems(buff)-buffPtr-1, _TRUNCATE, W("STK[%08X] = %08X "), (size_t)ptr, *ptr) <0) + { + return(0); + } + + buffPtr += lstrlenW(buffPtr); + + const wchar_t* kind = W("RETADDR "); + + // Is this a stub (is the return address a MethodDesc? + MethodDesc* ftn = AsMethodDesc(*ptr); + + if (ftn != 0) + { + + kind = W(" MD PARAM"); + + // If another true return address is not directly before it, it is just + // a methodDesc param. + TADDR prevRetAddr = ptr[1]; + + if (isRetAddr(prevRetAddr, &whereCalled) && AsMethodDesc(prevRetAddr) == 0) + { + kind = W("STUBCALL"); + } + else + { + // Is it the magic sequence used by CallDescr? + if (isMemoryReadable(prevRetAddr - sizeof(short), + sizeof(short)) && + ((short*) prevRetAddr)[-1] == 0x5A59) // Pop ECX POP EDX + { + kind = W("STUBCALL"); + } + + } + + } + else // Is it some other code the EE knows about? + { + ftn = ExecutionManager::GetCodeMethodDesc((PCODE)(*ptr)); + } + + if(_snwprintf_s(buffPtr, buff+ nLen -buffPtr-1, _TRUNCATE, W("%s "), kind) < 0) + { + return(0); + } + + buffPtr += lstrlenW(buffPtr); + + if (ftn != 0) + { + // buffer is not large enough + if( formatMethodDesc(ftn, buffPtr, static_cast<DWORD>(buff+ nLen -buffPtr-1)) == NULL) + { + return(0); + } + + buffPtr += lstrlenW(buffPtr); + } + else + { + wcsncpy_s(buffPtr, nLen - (buffPtr - buff), W("<UNKNOWN FTN>"), _TRUNCATE); + buffPtr += lstrlenW(buffPtr); + } + + if (whereCalled != 0) + { + if(_snwprintf_s(buffPtr, buff+ nLen -buffPtr-1, _TRUNCATE, W(" Caller called Entry %X"), whereCalled) <0) + { + return(0); + } + + buffPtr += lstrlenW(buffPtr); + } + + wcsncpy_s(buffPtr, nLen - (buffPtr - buff), W("\n"), _TRUNCATE); + buffPtr += lstrlenW(buffPtr); + WszOutputDebugString(buff); + } + + MethodTable* pMT = AsMethodTable(*ptr); + if (pMT != 0) + { + buffPtr = buff; + if( _snwprintf_s(buffPtr, buff+ nLen -buffPtr-1, _TRUNCATE, W("STK[%08X] = %08X MT PARAM "), (size_t)ptr, *ptr ) <0) + { + return(0); + } + + buffPtr += lstrlenW(buffPtr); + + if( formatMethodTable(pMT, buffPtr, static_cast<DWORD>(buff+ nLen -buffPtr-1)) == NULL) + { + return(0); + } + + buffPtr += lstrlenW(buffPtr); + + wcsncpy_s(buffPtr, nLen - (buffPtr - buff), W("\n"), _TRUNCATE); + WszOutputDebugString(buff); + + } + + ptr++; + + } // while + + return(0); +} + +/*******************************************************************/ +/* dump the stack from the current ESP. Stop when we reach a 64K + boundary */ +int DumpCurrentStack() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + +#ifdef _TARGET_X86_ + BYTE* top = (BYTE *)GetCurrentSP(); + + // go back at most 64K, it will stop if we go off the + // top to unmapped memory + return(dumpStack(top, 0xFFFF)); +#else + _ASSERTE(!"@NYI - DumpCurrentStack(DebugHelp.cpp)"); + return 0; +#endif // _TARGET_X86_ +} + +/*******************************************************************/ +WCHAR* StringVal(STRINGREF objref) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return(objref->GetBuffer()); +} + +LPCUTF8 NameForMethodTable(UINT_PTR pMT) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + DefineFullyQualifiedNameForClass(); + LPCUTF8 clsName = GetFullyQualifiedNameForClass(((MethodTable*)pMT)); + // Note we're returning local stack space - this should be OK for using in the debugger though + return clsName; +} + +LPCUTF8 ClassNameForObject(UINT_PTR obj) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return(NameForMethodTable((UINT_PTR)(((Object*)obj)->GetMethodTable()))); +} + +LPCUTF8 ClassNameForOBJECTREF(OBJECTREF obj) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return(ClassNameForObject((UINT_PTR)(OBJECTREFToObject(obj)))); +} + +LPCUTF8 NameForMethodDesc(UINT_PTR pMD) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return(((MethodDesc*)pMD)->GetName()); +} + +LPCUTF8 ClassNameForMethodDesc(UINT_PTR pMD) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + DefineFullyQualifiedNameForClass (); + return GetFullyQualifiedNameForClass(((MethodDesc*)pMD)->GetMethodTable()); +} + +PCCOR_SIGNATURE RawSigForMethodDesc(MethodDesc* pMD) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return(pMD->GetSig()); +} + +Thread * CurrentThreadInfo () +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return GetThread (); +} + +AppDomain *GetAppDomainForObject(UINT_PTR obj) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return ((Object*)obj)->GetAppDomain(); +} + +ADIndex GetAppDomainIndexForObject(UINT_PTR obj) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return ((Object*)obj)->GetHeader()->GetAppDomainIndex(); +} + +AppDomain *GetAppDomainForObjectHeader(UINT_PTR hdr) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + ADIndex indx = ((ObjHeader*)hdr)->GetAppDomainIndex(); + if (!indx.m_dwIndex) + { + return NULL; + } + + return SystemDomain::GetAppDomainAtIndex(indx); +} + +ADIndex GetAppDomainIndexForObjectHeader(UINT_PTR hdr) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return ((ObjHeader*)hdr)->GetAppDomainIndex(); +} + +SyncBlock *GetSyncBlockForObject(UINT_PTR obj) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return ((Object*)obj)->GetHeader()->PassiveGetSyncBlock(); +} + +/*******************************************************************/ +void PrintMethodTable(UINT_PTR pMT) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + } + CONTRACTL_END; + + MethodTable * p = (MethodTable *)pMT; + + DefineFullyQualifiedNameForClass(); + LPCUTF8 name = GetFullyQualifiedNameForClass(p); + p->DebugDumpVtable(name, true); + p->DebugDumpFieldLayout(name, true); + p->DebugDumpGCDesc(name, true); +} + +void PrintTableForMethodDesc(UINT_PTR pMD) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + } + CONTRACTL_END; + + PrintMethodTable((UINT_PTR) ((MethodDesc *)pMD)->GetMethodTable() ); +} + +void PrintException(OBJECTREF pObjectRef) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + + if(pObjectRef == NULL) + { + return; + } + + GCPROTECT_BEGIN(pObjectRef); + + if (!IsException(pObjectRef->GetMethodTable())) + { + printf("Specified object is not an exception object.\n"); + } + else + { + MethodDescCallSite internalToString(METHOD__EXCEPTION__INTERNAL_TO_STRING, &pObjectRef); + + ARG_SLOT arg[1] = { + ObjToArgSlot(pObjectRef) + }; + + STRINGREF str = internalToString.Call_RetSTRINGREF(arg); + + if(str->GetBuffer() != NULL) + { + WszOutputDebugString(str->GetBuffer()); + } + } + + GCPROTECT_END(); +} + +void PrintException(UINT_PTR pObject) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + OBJECTREF pObjectRef = NULL; + GCPROTECT_BEGIN(pObjectRef); + GCPROTECT_END(); +} + +/*******************************************************************/ +/* sends a current stack trace to the debug window */ + +const char* FormatSig(MethodDesc* pMD, AppDomain *pDomain, AllocMemTracker *pamTracker); + +struct PrintCallbackData { + BOOL toStdout; + BOOL withAppDomain; +#ifdef _DEBUG + BOOL toLOG; +#endif +}; + +StackWalkAction PrintStackTraceCallback(CrawlFrame* pCF, VOID* pData) +{ + CONTRACTL + { + DISABLED(NOTHROW); + DISABLED(GC_TRIGGERS); + } + CONTRACTL_END; + + CONTRACT_VIOLATION(ThrowsViolation); + + MethodDesc* pMD = pCF->GetFunction(); + const int nLen = 2048 - 1; // keep one character for "\n" + wchar_t *buff = (wchar_t*)alloca((nLen + 1) * sizeof(wchar_t)); + buff[0] = 0; + buff[nLen-1] = W('\0'); // make sure the buffer is always NULL-terminated + + PrintCallbackData *pCBD = (PrintCallbackData *)pData; + + if (pMD != 0) + { + MethodTable * pMT = pMD->GetMethodTable(); + + if (pCBD->withAppDomain) + { + if(_snwprintf_s(&buff[lstrlenW(buff)], + nLen -lstrlenW(buff) - 1, + _TRUNCATE, + W("{[%3.3x] %s} "), + pCF->GetAppDomain()->GetId().m_dwId, + pCF->GetAppDomain()->GetFriendlyName(FALSE)) < 0) + { + return SWA_CONTINUE; + } + } + + DefineFullyQualifiedNameForClass(); + + LPCUTF8 clsName = GetFullyQualifiedNameForClass(pMT); + + if (clsName != 0) + { + if(_snwprintf_s(&buff[lstrlenW(buff)], nLen -lstrlenW(buff) - 1, _TRUNCATE, W("%S::"), clsName) < 0) + { + return SWA_CONTINUE; + } + } + + // This prematurely suppressrelease'd AmTracker will leak any memory allocated by FormatSig. + // But this routine is diagnostic aid, not customer-reachable so we won't bother to plug. + AllocMemTracker dummyAmTracker; + + int buffLen = _snwprintf_s(&buff[lstrlenW(buff)], + nLen -lstrlenW(buff) - 1, + _TRUNCATE, + W("%S %S "), + pMD->GetName(), + FormatSig(pMD, pCF->GetAppDomain(), &dummyAmTracker)); + + dummyAmTracker.SuppressRelease(); + if (buffLen < 0 ) + { + return SWA_CONTINUE; + } + + + if (pCF->IsFrameless() && pCF->GetJitManager() != 0) { + + PREGDISPLAY regs = pCF->GetRegisterSet(); + + DWORD offset = pCF->GetRelOffset(); + + TADDR start = pCF->GetCodeInfo()->GetStartAddress(); + + if(_snwprintf_s(&buff[lstrlenW(buff)], + nLen -lstrlenW(buff) - 1, + _TRUNCATE, + W("JIT ESP:%X MethStart:%X EIP:%X(rel %X)"), + (size_t)GetRegdisplaySP(regs), + (size_t)start, + (size_t)GetControlPC(regs), + offset) < 0) + { + return SWA_CONTINUE; + } + + } + else + { + + if(_snwprintf_s(&buff[lstrlenW(buff)], nLen -lstrlenW(buff) - 1, _TRUNCATE, W("EE implemented")) < 0) + { + return SWA_CONTINUE; + } + } + + } + else + { + Frame* frame = pCF->GetFrame(); + + if(_snwprintf_s(&buff[lstrlenW(buff)], + nLen -lstrlenW(buff) - 1, + _TRUNCATE, + W("EE Frame is") LFMT_ADDR, + (size_t)DBG_ADDR(frame)) < 0) + { + return SWA_CONTINUE; + } + } + + if (pCBD->toStdout) + { + wcscat_s(buff, nLen + 1, W("\n")); + PrintToStdOutW(buff); + } +#ifdef _DEBUG + else if (pCBD->toLOG) + { + MAKE_ANSIPTR_FROMWIDE(sbuff, buff); + // For LogSpewAlways to work rightr the "\n" (newline) + // must be in the fmt string not part of the args + LogSpewAlways(" %s\n", sbuff); + } +#endif + else + { + wcscat_s(buff, nLen + 1, W("\n")); + WszOutputDebugString(buff); + } + + return SWA_CONTINUE; +} + +void PrintStackTrace() +{ + CONTRACTL + { + DISABLED(NOTHROW); + DISABLED(GC_TRIGGERS); + } + CONTRACTL_END; + + WszOutputDebugString(W("***************************************************\n")); + PrintCallbackData cbd = {0, 0}; + GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); +} + +void PrintStackTraceToStdout() +{ + CONTRACTL + { + DISABLED(NOTHROW); + DISABLED(GC_TRIGGERS); + } + CONTRACTL_END; + + PrintCallbackData cbd = {1, 0}; + GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); +} + +#ifdef _DEBUG +void PrintStackTraceToLog() +{ + CONTRACTL + { + DISABLED(NOTHROW); + DISABLED(GC_TRIGGERS); + } + CONTRACTL_END; + + PrintCallbackData cbd = {0, 0, 1}; + GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); +} +#endif + +void PrintStackTraceWithAD() +{ + CONTRACTL + { + DISABLED(NOTHROW); + DISABLED(GC_TRIGGERS); + } + CONTRACTL_END; + + WszOutputDebugString(W("***************************************************\n")); + PrintCallbackData cbd = {0, 1}; + GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); +} + +void PrintStackTraceWithADToStdout() +{ + CONTRACTL + { + DISABLED(NOTHROW); + DISABLED(GC_TRIGGERS); + } + CONTRACTL_END; + + PrintCallbackData cbd = {1, 1}; + GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); +} + +#ifdef _DEBUG +void PrintStackTraceWithADToLog() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + PrintCallbackData cbd = {0, 1, 1}; + GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); +} + +void PrintStackTraceWithADToLog(Thread *pThread) +{ + CONTRACTL + { + DISABLED(NOTHROW); + DISABLED(GC_TRIGGERS); + } + CONTRACTL_END; + + PrintCallbackData cbd = {0, 1, 1}; + pThread->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); +} +#endif + +/*******************************************************************/ +// Get the system or current domain from the thread. +BaseDomain* GetSystemDomain() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return SystemDomain::System(); +} + +AppDomain* GetCurrentDomain() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return SystemDomain::GetCurrentDomain(); +} + +void PrintDomainName(size_t ob) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + AppDomain* dm = (AppDomain*) ob; + LPCWSTR st = dm->GetFriendlyName(FALSE); + + if(st != NULL) + { + WszOutputDebugString(st); + } + else + { + WszOutputDebugString(W("<Domain with no Name>")); + } +} + +#if defined(_TARGET_X86_) + +#include "gcdump.h" + +#include "../gcdump/i386/gcdumpx86.cpp" + +#include "../gcdump/gcdump.cpp" + +/*********************************************************************/ +void printfToDbgOut(const char* fmt, ...) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + va_list args; + va_start(args, fmt); + + char buffer[4096]; + _vsnprintf_s(buffer, COUNTOF(buffer), _TRUNCATE, fmt, args); + + va_end(args); + OutputDebugStringA( buffer ); +} + +void DumpGCInfo(MethodDesc* method) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + PCODE methodStart = method->GetNativeCode(); + + if (methodStart == 0) + { + return; + } + + EECodeInfo codeInfo(methodStart); + _ASSERTE(codeInfo.GetRelOffset() == 0); + + ICodeManager* codeMan = codeInfo.GetCodeManager(); + GCInfoToken table = codeInfo.GetGCInfoToken(); + + unsigned methodSize = (unsigned)codeMan->GetFunctionSize(table); + + GCDump gcDump(table.Version); + PTR_CBYTE gcInfo = PTR_CBYTE(table.Info); + + gcDump.gcPrintf = printfToDbgOut; + + InfoHdr header; + + printfToDbgOut ("Method info block:\n"); + gcInfo += gcDump.DumpInfoHdr(gcInfo, &header, &methodSize, 0); + + printfToDbgOut ("\n"); + printfToDbgOut ("Pointer table:\n"); + + gcInfo += gcDump.DumpGCTable(gcInfo, header, methodSize, 0); +} + +void DumpGCInfoMD(size_t method) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + DumpGCInfo((MethodDesc*) method); +} +#endif + + +#ifdef LOGGING +void LogStackTrace() +{ + WRAPPER_NO_CONTRACT; + + PrintCallbackData cbd = {0, 0, 1}; + GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd,ALLOW_ASYNC_STACK_WALK, 0); +} +#endif + +#endif // #ifndef DACCESS_COMPILE +#endif //_DEBUG |