diff options
author | Jiyoung Yun <jy910.yun@samsung.com> | 2016-11-23 19:09:09 +0900 |
---|---|---|
committer | Jiyoung Yun <jy910.yun@samsung.com> | 2016-11-23 19:09:09 +0900 |
commit | 4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch) | |
tree | 98110734c91668dfdbb126fcc0e15ddbd93738ca /src/utilcode/debug.cpp | |
parent | fa45f57ed55137c75ac870356a1b8f76c84b229c (diff) | |
download | coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2 coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip |
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/utilcode/debug.cpp')
-rw-r--r-- | src/utilcode/debug.cpp | 896 |
1 files changed, 896 insertions, 0 deletions
diff --git a/src/utilcode/debug.cpp b/src/utilcode/debug.cpp new file mode 100644 index 0000000000..a96aca148a --- /dev/null +++ b/src/utilcode/debug.cpp @@ -0,0 +1,896 @@ +// 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. +//***************************************************************************** +// Debug.cpp +// +// Helper code for debugging. +//***************************************************************************** +// + + +#include "stdafx.h" +#include "utilcode.h" +#include "ex.h" +#include "corexcep.h" + +#ifdef _DEBUG +#define LOGGING +#endif + + +#include "log.h" + +extern "C" _CRTIMP int __cdecl _flushall(void); + +// Global state counter to implement SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE. +Volatile<LONG> g_DbgSuppressAllocationAsserts = 0; + + +#ifdef _DEBUG + +int LowResourceMessageBoxHelperAnsi( + LPCSTR szText, // Text message + LPCSTR szTitle, // Title + UINT uType); // Style of MessageBox + +//***************************************************************************** +// This struct tracks the asserts we want to ignore in the rest of this +// run of the application. +//***************************************************************************** +struct _DBGIGNOREDATA +{ + char rcFile[_MAX_PATH]; + int iLine; + bool bIgnore; +}; + +typedef CDynArray<_DBGIGNOREDATA> DBGIGNORE; +static BYTE grIgnoreMemory[sizeof(DBGIGNORE)]; +inline DBGIGNORE* GetDBGIGNORE() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + static bool fInit; // = false; + if (!fInit) + { + SCAN_IGNORE_THROW; // Doesn't really throw here. + new (grIgnoreMemory) CDynArray<_DBGIGNOREDATA>(); + fInit = true; + } + + return (DBGIGNORE*)grIgnoreMemory; +} + +// Continue the app on an assert. Still output the assert, but +// Don't throw up a GUI. This is useful for testing fatal error +// paths (like FEEE) where the runtime asserts. +BOOL ContinueOnAssert() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + static ConfigDWORD fNoGui; + return fNoGui.val(CLRConfig::INTERNAL_ContinueOnAssert); +} + +BOOL NoGuiOnAssert() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + static ConfigDWORD fNoGui; + return fNoGui.val(CLRConfig::INTERNAL_NoGuiOnAssert); +} + +void DoRaiseExceptionOnAssert(DWORD chance) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_SUPPORTS_DAC; + +#if !defined(DACCESS_COMPILE) + if (chance) + { +#ifndef FEATURE_PAL + PAL_TRY_NAKED + { + RaiseException(EXCEPTION_INTERNAL_ASSERT, 0, 0, NULL); + } + PAL_EXCEPT_NAKED((chance == 1) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + } + PAL_ENDTRY_NAKED +#else // FEATURE_PAL + // For PAL always raise the exception. + RaiseException(EXCEPTION_INTERNAL_ASSERT, 0, 0, NULL); +#endif // FEATURE_PAL + } +#endif // !DACCESS_COMPILE +} + +enum RaiseOnAssertOptions { rTestAndRaise, rTestOnly }; + +BOOL RaiseExceptionOnAssert(RaiseOnAssertOptions option = rTestAndRaise) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_SUPPORTS_DAC; + + // ok for debug-only code to take locks + CONTRACT_VIOLATION(TakesLockViolation); + + DWORD fRet = 0; + +#if !defined(DACCESS_COMPILE) + static ConfigDWORD fRaiseExceptionOnAssert; + // + // we don't want this config key to affect mscordacwks as well! + // + EX_TRY + { + fRet = fRaiseExceptionOnAssert.val(CLRConfig::INTERNAL_RaiseExceptionOnAssert); + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); + + if (option == rTestAndRaise && fRet != 0) + { + DoRaiseExceptionOnAssert(fRet); + } +#endif // !DACCESS_COMPILE + + return fRet != 0; +} + +BOOL DebugBreakOnAssert() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_SUPPORTS_DAC; + + // ok for debug-only code to take locks + CONTRACT_VIOLATION(TakesLockViolation); + + BOOL fRet = FALSE; + +#ifndef DACCESS_COMPILE + static ConfigDWORD fDebugBreak; + // + // we don't want this config key to affect mscordacwks as well! + // + EX_TRY + { + fRet = fDebugBreak.val(CLRConfig::INTERNAL_DebugBreakOnAssert); + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); +#endif // DACCESS_COMPILE + + return fRet; +} + +VOID TerminateOnAssert() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + ShutdownLogging(); + TerminateProcess(GetCurrentProcess(), 123456789); +} + +// Whether this thread is already displaying an assert dialog. +BOOL IsDisplayingAssertDlg() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + size_t flag = 0; + if (ClrFlsCheckValue(TlsIdx_AssertDlgStatus, (LPVOID *)&flag)) + { + return (flag != 0); + } + return FALSE; +} + +void SetDisplayingAssertDlg(BOOL value) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + ClrFlsSetValue(TlsIdx_AssertDlgStatus, (LPVOID)(size_t)value); +} + +VOID LogAssert( + LPCSTR szFile, + int iLine, + LPCSTR szExpr +) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_SO_TOLERANT; + STATIC_CONTRACT_DEBUG_ONLY; + + // Log asserts to the stress log. Note that we can't include the szExpr b/c that + // may not be a string literal (particularly for formatt-able asserts). + STRESS_LOG2(LF_ASSERT, LL_ALWAYS, "ASSERT:%s, line:%d\n", szFile, iLine); + + SYSTEMTIME st; +#ifndef FEATURE_PAL + GetLocalTime(&st); +#else + GetSystemTime(&st); +#endif + + PathString exename; + WszGetModuleFileName(NULL, exename); + + LOG((LF_ASSERT, + LL_FATALERROR, + "FAILED ASSERT(PID %d [0x%08x], Thread: %d [0x%x]) (%lu/%lu/%lu: %02lu:%02lu:%02lu %s): File: %s, Line %d : %s\n", + GetCurrentProcessId(), + GetCurrentProcessId(), + GetCurrentThreadId(), + GetCurrentThreadId(), + (ULONG)st.wMonth, + (ULONG)st.wDay, + (ULONG)st.wYear, + 1 + (( (ULONG)st.wHour + 11 ) % 12), + (ULONG)st.wMinute, + (ULONG)st.wSecond, + (st.wHour < 12) ? "am" : "pm", + szFile, + iLine, + szExpr)); + LOG((LF_ASSERT, LL_FATALERROR, "RUNNING EXE: %ws\n", exename.GetUnicode())); +} + +//***************************************************************************** + +BOOL LaunchJITDebugger() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + BOOL fSuccess = FALSE; +#ifndef FEATURE_PAL + EX_TRY + { + SString debugger; + GetDebuggerSettingInfo(debugger, NULL); + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + // We can leave this event as it is since it is inherited by a child process. + // We will block one scheduler, but the process is asking a user if they want to attach debugger. + HandleHolder eventHandle = WszCreateEvent(&sa, TRUE, FALSE, NULL); + if (eventHandle == NULL) + ThrowOutOfMemory(); + + SString cmdLine; + cmdLine.Printf(debugger, GetCurrentProcessId(), eventHandle.GetValue()); + + STARTUPINFO StartupInfo; + memset(&StartupInfo, 0, sizeof(StartupInfo)); + StartupInfo.cb = sizeof(StartupInfo); + StartupInfo.lpDesktop = W("Winsta0\\Default"); + + PROCESS_INFORMATION ProcessInformation; + if (WszCreateProcess(NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInformation)) + { + WaitForSingleObject(eventHandle.GetValue(), INFINITE); + } + + fSuccess = TRUE; + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); +#endif // !FEATURE_PAL + return fSuccess; +} + + +//***************************************************************************** +// This function is called in order to ultimately return an out of memory +// failed hresult. But this guy will check what environment you are running +// in and give an assert for running in a debug build environment. Usually +// out of memory on a dev machine is a bogus alloction, and this allows you +// to catch such errors. But when run in a stress envrionment where you are +// trying to get out of memory, assert behavior stops the tests. +//***************************************************************************** +HRESULT _OutOfMemory(LPCSTR szFile, int iLine) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + DbgWriteEx(W("WARNING: Out of memory condition being issued from: %hs, line %d\n"), + szFile, iLine); + return (E_OUTOFMEMORY); +} + +int _DbgBreakCount = 0; +static const char * szLowMemoryAssertMessage = "Assert failure (unable to format)"; + +//***************************************************************************** +// This function will handle ignore codes and tell the user what is happening. +//***************************************************************************** +bool _DbgBreakCheck( + LPCSTR szFile, + int iLine, + LPCSTR szExpr, + BOOL fConstrained) +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_DEBUG_ONLY; + + DBGIGNORE* pDBGIFNORE = GetDBGIGNORE(); + _DBGIGNOREDATA *psData; + int i; + + // Check for ignore all. + for (i = 0, psData = pDBGIFNORE->Ptr(); i < pDBGIFNORE->Count(); i++, psData++) + { + if (psData->iLine == iLine && SString::_stricmp(psData->rcFile, szFile) == 0 && psData->bIgnore == true) + { + return false; + } + } + + CONTRACT_VIOLATION(FaultNotFatal | GCViolation | TakesLockViolation); + + SString debugOutput; + SString dialogOutput; + SString modulePath; + SString dialogTitle; + SString dialogIgnoreMessage; + BOOL formattedMessages = FALSE; + + // If we are low on memory we cannot even format a message. If this happens we want to + // contain the exception here but display as much information as we can about the exception. + if (!fConstrained) + { + EX_TRY + { + ClrGetModuleFileName(0, modulePath); + debugOutput.Printf( + W("\nAssert failure(PID %d [0x%08x], Thread: %d [0x%04x]): %hs\n") + W(" File: %hs Line: %d\n") + W(" Image: "), + GetCurrentProcessId(), GetCurrentProcessId(), + GetCurrentThreadId(), GetCurrentThreadId(), + szExpr, szFile, iLine); + debugOutput.Append(modulePath); + debugOutput.Append(W("\n\n")); + + // Change format for message box. The extra spaces in the title + // are there to get around format truncation. + dialogOutput.Printf( + W("%hs\n\n%hs, Line: %d\n\nAbort - Kill program\nRetry - Debug\nIgnore - Keep running\n") + W("\n\nImage:\n"), szExpr, szFile, iLine); + dialogOutput.Append(modulePath); + dialogOutput.Append(W("\n")); + dialogTitle.Printf(W("Assert Failure (PID %d, Thread %d/0x%04x)"), + GetCurrentProcessId(), GetCurrentThreadId(), GetCurrentThreadId()); + + dialogIgnoreMessage.Printf(W("Ignore the assert for the rest of this run?\nYes - Assert will never fire again.\nNo - Assert will continue to fire.\n\n%hs\nLine: %d\n"), + szFile, iLine); + + formattedMessages = TRUE; + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); + } + + // Emit assert in debug output and console for easy access. + if (formattedMessages) + { + WszOutputDebugString(debugOutput); + fwprintf(stderr, W("%s"), (const WCHAR*)debugOutput); + } + else + { + // Note: we cannot convert to unicode or concatenate in this situation. + OutputDebugStringA(szLowMemoryAssertMessage); + OutputDebugStringA("\n"); + OutputDebugStringA(szFile); + OutputDebugStringA("\n"); + OutputDebugStringA(szExpr); + OutputDebugStringA("\n"); + printf(szLowMemoryAssertMessage); + printf("\n"); + printf(szFile); + printf("\n"); + printf("%s", szExpr); + printf("\n"); + } + + LogAssert(szFile, iLine, szExpr); + FlushLogging(); // make certain we get the last part of the log + _flushall(); + + if (ContinueOnAssert()) + { + return false; // don't stop debugger. No gui. + } + + if (NoGuiOnAssert()) + { + TerminateOnAssert(); + } + + if (DebugBreakOnAssert()) + { + return true; // like a retry + } + + if (IsDisplayingAssertDlg()) + { + // We are already displaying an assert dialog box on this thread. The reason why we came here is + // the message loop run by the API we call to display the UI. A message was dispatched and execution + // ended up in the runtime where it fired another assertion. If this happens before the dialog had + // a chance to fully initialize the original assert may not be visible which is misleading for the + // user. So we just continue. + return false; + } + + SetDisplayingAssertDlg(TRUE); + + // Tell user there was an error. + _DbgBreakCount++; + int ret; + if (formattedMessages) + { + ret = UtilMessageBoxCatastrophicNonLocalized( + W("%s"), dialogTitle, MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION, TRUE, (const WCHAR*)dialogOutput); + } + else + { + ret = LowResourceMessageBoxHelperAnsi( + szExpr, szLowMemoryAssertMessage, MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION); + } + --_DbgBreakCount; + + SetDisplayingAssertDlg(FALSE); + + switch(ret) + { + case 0: +#if 0 + // The message box was not displayed. Tell caller to break. + return true; +#endif + // For abort, just quit the app. + case IDABORT: + TerminateProcess(GetCurrentProcess(), 1); + break; + + // Tell caller to break at the correct loction. + case IDRETRY: + if (IsDebuggerPresent()) + { + SetErrorMode(0); + } + else + { + LaunchJITDebugger(); + } + return true; + + // If we want to ignore the assert, find out if this is forever. + case IDIGNORE: + if (formattedMessages) + { + if (UtilMessageBoxCatastrophicNonLocalized( + dialogIgnoreMessage, + W("Ignore Assert Forever?"), + MB_ICONQUESTION | MB_YESNO, + TRUE) != IDYES) + { + break; + } + } + else + { + if (LowResourceMessageBoxHelperAnsi( + "Ignore the assert for the rest of this run?\nYes - Assert will never fire again.\nNo - Assert will continue to fire.\n", + "Ignore Assert Forever?", + MB_ICONQUESTION | MB_YESNO) != IDYES) + { + break; + } + } + if ((psData = pDBGIFNORE->Append()) == 0) + { + return false; + } + psData->bIgnore = true; + psData->iLine = iLine; + strcpy(psData->rcFile, szFile); + break; + } + + return false; +} + +bool _DbgBreakCheckNoThrow( + LPCSTR szFile, + int iLine, + LPCSTR szExpr, + BOOL fConstrained) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_DEBUG_ONLY; + + bool failed = false; + bool result = false; + EX_TRY + { + result = _DbgBreakCheck(szFile, iLine, szExpr, fConstrained); + } + EX_CATCH + { + failed = true; + } + EX_END_CATCH(SwallowAllExceptions); + + if (failed) + { + return true; + } + return result; +} + +#ifndef FEATURE_PAL +// Get the timestamp from the PE file header. This is useful +unsigned DbgGetEXETimeStamp() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + static ULONG cache = 0; + if (cache == 0) { + // Use GetModuleHandleA to avoid contracts - this results in a recursive loop initializing the + // debug allocator. + BYTE* imageBase = (BYTE*) GetModuleHandleA(NULL); + if (imageBase == 0) + return(0); + IMAGE_DOS_HEADER *pDOS = (IMAGE_DOS_HEADER*) imageBase; + if ((pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE)) || (pDOS->e_lfanew == 0)) + return(0); + + IMAGE_NT_HEADERS *pNT = (IMAGE_NT_HEADERS*) (VAL32(pDOS->e_lfanew) + imageBase); + cache = VAL32(pNT->FileHeader.TimeDateStamp); + } + + return cache; +} +#endif // FEATURE_PAL + +// Called from within the IfFail...() macros. Set a breakpoint here to break on +// errors. +VOID DebBreak() +{ + STATIC_CONTRACT_LEAF; + static int i = 0; // add some code here so that we'll be able to set a BP + i++; +} + +VOID DebBreakHr(HRESULT hr) +{ + STATIC_CONTRACT_LEAF; + static int i = 0; // add some code here so that we'll be able to set a BP + _ASSERTE(hr != (HRESULT) 0xcccccccc); + i++; + + // @CONSIDER: We maybe want this on retail builds. + #ifdef _DEBUG + static DWORD dwBreakHR = 99; + + if (dwBreakHR == 99) + dwBreakHR = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnHR); + if (dwBreakHR == (DWORD)hr) + { + _DbgBreak(); + } + #endif +} + +#ifndef FEATURE_PAL +CHAR g_szExprWithStack2[10480]; +#endif +void *dbgForceToMemory; // dummy pointer that pessimises enregistration + +#ifdef MDA_SUPPORTED +#ifdef _DEBUG +BOOL g_bMdaDisableAsserts = FALSE; +#endif +#endif + +int g_BufferLock = -1; + +VOID DbgAssertDialog(const char *szFile, int iLine, const char *szExpr) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; + + DEBUG_ONLY_FUNCTION; + +#ifdef MDA_SUPPORTED +#ifdef _DEBUG + if (g_bMdaDisableAsserts) + return; +#endif +#endif + +#ifdef DACCESS_COMPILE + // In the DAC case, asserts can mean one of two things. + // Either there is a bug in the DAC infrastructure itself (a real assert), or just + // that the target is corrupt or being accessed at an inconsistent state (a "target + // consistency failure"). For target consistency failures, we need a mechanism to disable them + // (without affecting other asserts) so that we can test corrupt / inconsistent targets. + + // @dbgtodo DAC: For now we're treating all asserts as if they are target consistency checks. + // In the future we should differentiate the two so that real asserts continue to fire, even when + // we expect the target to be inconsistent. See DevDiv Bugs 31674. + if( !DacTargetConsistencyAssertsEnabled() ) + { + return; + } +#endif // #ifndef DACCESS_COMPILE + + // We increment this every time we use the SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE + // macro below. If it is a big number it means either a lot of threads are asserting + // or we have a recursion in the Assert logic (usually the latter). At least with this + // code in place, we don't get stack overflow (and the process torn down). + // the correct fix is to avoid calling asserting when allocating memory with an assert. + if (g_DbgSuppressAllocationAsserts > 16) + DebugBreak(); + + SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; + + // Raising the assert dialog can cause us to re-enter the host when allocating + // memory for the string. Since this is debug-only code, we can safely skip + // violation asserts here, particularly since they can also cause infinite + // recursion. + PERMANENT_CONTRACT_VIOLATION(HostViolation, ReasonDebugOnly); + + dbgForceToMemory = &szFile; //make certain these args are available in the debugger + dbgForceToMemory = &iLine; + dbgForceToMemory = &szExpr; + + RaiseExceptionOnAssert(rTestAndRaise); + + BOOL fConstrained = FALSE; + + DWORD dwAssertStacktrace = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertStacktrace); + +#if !defined(DACCESS_COMPILE) && defined(FEATURE_STACK_PROBE) + //global g_fpCheckNStackPagesAvailable is not present when SO infrastructure code is not present + // Trying to get a stack trace if there is little stack available can cause a silent process + // teardown, so only try to do this there is plenty of stack. + if ((dwAssertStacktrace) != 0 && (g_fpCheckNStackPagesAvailable != NULL)) + { + if (!g_fpCheckNStackPagesAvailable(12)) + { + fConstrained = TRUE; + } + } +#endif + + LONG lAlreadyOwned = InterlockedExchange((LPLONG)&g_BufferLock, 1); + if (fConstrained || dwAssertStacktrace == 0 || lAlreadyOwned == 1) + { + if (_DbgBreakCheckNoThrow(szFile, iLine, szExpr, fConstrained)) + { + _DbgBreak(); + } + } + else + { + char *szExprToDisplay = (char*)szExpr; +#ifdef FEATURE_PAL + BOOL fGotStackTrace = TRUE; +#else + BOOL fGotStackTrace = FALSE; +#ifndef DACCESS_COMPILE + EX_TRY + { + FAULT_NOT_FATAL(); + szExprToDisplay = &g_szExprWithStack2[0]; + strcpy(szExprToDisplay, szExpr); + strcat_s(szExprToDisplay, _countof(g_szExprWithStack2), "\n\n"); + GetStringFromStackLevels(1, 10, szExprToDisplay + strlen(szExprToDisplay)); + fGotStackTrace = TRUE; + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); +#endif // DACCESS_COMPILE +#endif // FEATURE_PAL + + if (_DbgBreakCheckNoThrow(szFile, iLine, szExprToDisplay, !fGotStackTrace)) + { + _DbgBreak(); + } + + g_BufferLock = 0; + } +} // DbgAssertDialog + +#if !defined(DACCESS_COMPILE) +//----------------------------------------------------------------------------- +// Returns an a stacktrace for a given context. +// Very useful inside exception filters. +// Returns true if successful, false on failure (such as OOM). +// This never throws. +//----------------------------------------------------------------------------- +bool GetStackTraceAtContext(SString & s, CONTEXT * pContext) +{ + SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; + STATIC_CONTRACT_DEBUG_ONLY; + + // NULL means use the current context. + bool fSuccess = false; + + FAULT_NOT_FATAL(); + +#ifndef FEATURE_PAL + EX_TRY + { + const int cTotal = cfrMaxAssertStackLevels - 1; + // If we have a supplied context, then don't skip any frames. Else we'll + // be using the current context, so skip this frame. + const int cSkip = (pContext == NULL) ? 1 : 0; + char * szString = s.OpenANSIBuffer(cchMaxAssertStackLevelStringLen * cTotal); + GetStringFromStackLevels(cSkip, cTotal, szString, pContext); + s.CloseBuffer((COUNT_T) strlen(szString)); + + // If we made it this far w/o throwing, we succeeded. + fSuccess = true; + } + EX_CATCH + { + // Nothing to do here. + } + EX_END_CATCH(SwallowAllExceptions); +#endif // FEATURE_PAL + + return fSuccess; +} // GetStackTraceAtContext +#endif // !defined(DACCESS_COMPILE) +#endif // _DEBUG + +// This helper will throw up a message box without allocating or using stack if possible, and is +// appropriate for either low memory or low stack situations. +int LowResourceMessageBoxHelperAnsi( + LPCSTR szText, // Text message + LPCSTR szTitle, // Title + UINT uType) // Style of MessageBox +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return IDCANCEL;); + } + CONTRACTL_END; + + // In low memory or stack constrained code we cannot format or convert strings, so use the + // ANSI version. + int result = MessageBoxA(NULL, szText, szTitle, uType); + return result; +} + + +/**************************************************************************** + The following two functions are defined to allow Free builds to call + DebugBreak or to Assert with a stack trace for unexpected fatal errors. + Typically these paths are enabled via a registry key in a Free Build +*****************************************************************************/ + +VOID __FreeBuildDebugBreak() +{ + WRAPPER_NO_CONTRACT; // If we're calling this, we're well past caring about contract consistency! + + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnRetailAssert)) + { + DebugBreak(); + } +} + +void *freForceToMemory; // dummy pointer that pessimises enregistration + +void DECLSPEC_NORETURN __FreeBuildAssertFail(const char *szFile, int iLine, const char *szExpr) +{ + WRAPPER_NO_CONTRACT; // If we're calling this, we're well past caring about contract consistency! + + freForceToMemory = &szFile; //make certain these args are available in the debugger + freForceToMemory = &iLine; + freForceToMemory = &szExpr; + + __FreeBuildDebugBreak(); + + SString buffer; + SString modulePath; + + // Give assert in output for easy access. + ClrGetModuleFileName(0, modulePath); +#ifndef FEATURE_PAL + buffer.Printf(W("CLR: Assert failure(PID %d [0x%08x], Thread: %d [0x%x]): %hs\n") + W(" File: %hs, Line: %d Image:\n"), + GetCurrentProcessId(), GetCurrentProcessId(), + GetCurrentThreadId(), GetCurrentThreadId(), + szExpr, szFile, iLine); + buffer.Append(modulePath); + buffer.Append(W("\n")); + WszOutputDebugString(buffer); + // Write out the error to the console + _putws(buffer); +#else // FEATURE_PAL + // UNIXTODO: Do this for Unix. +#endif // FEATURE_PAL + // Log to the stress log. Note that we can't include the szExpr b/c that + // may not be a string literal (particularly for formatt-able asserts). + STRESS_LOG2(LF_ASSERT, LL_ALWAYS, "ASSERT:%s, line:%d\n", szFile, iLine); + + FlushLogging(); // make certain we get the last part of the log + + _flushall(); + + // TerminateOnAssert(); + ShutdownLogging(); + + // Failing here implies an error in the runtime - hence we use + // COR_E_EXECUTIONENGINE + TerminateProcess(GetCurrentProcess(), COR_E_EXECUTIONENGINE); + + UNREACHABLE(); +} + +//=================================================================================== +// Used by the ex.h macro: EX_CATCH_HRESULT_AND_NGEN_CLEAN(_hr) +// which is used by ngen and mscorsvc to catch unexpected HRESULT +// from one of the RPC calls. +//=================================================================================== +void RetailAssertIfExpectedClean() +{ + static ConfigDWORD g_NGenClean; + if (g_NGenClean.val(CLRConfig::EXTERNAL_NGenClean) == 1) + { + _ASSERTE_ALL_BUILDS("clr/src/Utilcode/Debug.cpp", !"Error during NGen: expected no exceptions to be thrown"); + } +} + |