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; }
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; }
VOID EnterLogLock() { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; // We don't care about violating CANNOT_TAKE_LOCK in debug-only builds, and it's // rather hard to care about this, as we LOG all over the place. CONTRACT_VIOLATION(TakesLockViolation); if(LogFileMutex != 0) { DWORD status; status = ClrWaitForMutex(LogFileMutex, INFINITE, FALSE); _ASSERTE(WAIT_OBJECT_0 == status); } }
// Full version handles metadata caching, which Release() needs to coordinate with. // Thus Release() is in a satellite lib. ULONG RegMeta::Release() { // This is called during cleanup. We can not fail this call by probing. // As long as we make sure the cleanup does not use too much space through // BEGIN_CLEANUP_ENTRYPOINT, we are OK. CONTRACT_VIOLATION (SOToleranceViolation); BEGIN_CLEANUP_ENTRYPOINT; #if defined(FEATURE_METADATA_IN_VM) _ASSERTE(!m_bCached || LOADEDMODULES::IsEntryInList(this)); #else _ASSERTE(!m_bCached); #endif // FEATURE_METADATA_IN_VM BOOL bCached = m_bCached; ULONG cRef = InterlockedDecrement(&m_cRef); // NOTE: 'this' may be unsafe after this point, if the module is cached, and // another thread finds the module in the cache, releases it, and deletes it // before we get around to deleting it. (That's why we must make a local copy // of m_bCached.) // If no references left... if (cRef == 0) { if (!bCached) { // If the module is not (was not) cached, no other thread can have // discovered the module, so this thread can now safely delete it. delete this; } #if defined(FEATURE_METADATA_IN_VM) else if (LOADEDMODULES::RemoveModuleFromLoadedList(this)) { // If the module was cached, RemoveModuleFromLoadedList() will try to // safely un-publish the module, and if it succeeds, no other thread // has (or will) discover the module, so this thread can delete it. m_bCached = false; delete this; } #endif // FEATURE_METADATA_IN_VM } END_CLEANUP_ENTRYPOINT return cRef; } // RegMeta::Release
//***************************************************************************** // 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; RaiseExceptionOnAssert(rTestAndRaise); if (DebugBreakOnAssert()) { DebugBreak(); } 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); }
void EEContract::DoChecks(UINT testmask, __in_z const char *szFunction, __in_z char *szFile, int lineNum) { SCAN_IGNORE_THROW; // Tell the static contract analyzer to ignore contract violations SCAN_IGNORE_FAULT; // due to the contract checking logic itself. SCAN_IGNORE_TRIGGER; SCAN_IGNORE_LOCK; SCAN_IGNORE_SO; // Many of the checks below result in calls to GetThread() // that work just fine if GetThread() returns NULL, so temporarily // allow such calls. BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; m_pThread = GetThread(); if (m_pThread != NULL) { m_pClrDebugState = m_pThread->GetClrDebugState(); } // Call our base DoChecks. BaseContract::DoChecks(testmask, szFunction, szFile, lineNum); m_testmask = testmask; m_contractStackRecord.m_testmask = testmask; // GC mode check switch (testmask & MODE_Mask) { case MODE_Coop: if (m_pThread == NULL || !m_pThread->PreemptiveGCDisabled()) { // // Check if this is the debugger helper thread and has the runtime // stoppped. If both of these things are true, then we do not care // whether we are in COOP mode or not. // if ((g_pDebugInterface != NULL) && g_pDebugInterface->ThisIsHelperThread() && g_pDebugInterface->IsStopped()) { break; } // Pretend that the threads doing GC are in cooperative mode so that code with // MODE_COOPERATIVE contract works fine on them. if (IsGCThread()) { break; } if (!( (ModeViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) { if (m_pThread == NULL) { CONTRACT_ASSERT("You must have called SetupThread in order to be in GC Cooperative mode.", Contract::MODE_Preempt, Contract::MODE_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } else { CONTRACT_ASSERT("MODE_COOPERATIVE encountered while thread is in preemptive state.", Contract::MODE_Preempt, Contract::MODE_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } } } break; case MODE_Preempt: // Unmanaged threads are considered permanently preemptive so a NULL thread amounts to a passing case here. if (m_pThread != NULL && m_pThread->PreemptiveGCDisabled()) { if (!( (ModeViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) { CONTRACT_ASSERT("MODE_PREEMPTIVE encountered while thread is in cooperative state.", Contract::MODE_Coop, Contract::MODE_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } } break; case MODE_Disabled: // Nothing break; default: UNREACHABLE(); } // GC Trigger check switch (testmask & GC_Mask) { case GC_Triggers: // We don't want to do a full TRIGGERSGC here as this could corrupt // OBJECTREF-typed arguments to the function. { if (m_pClrDebugState->GetGCNoTriggerCount()) { if (!( (GCViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) { CONTRACT_ASSERT("GC_TRIGGERS encountered in a GC_NOTRIGGER scope", Contract::GC_NoTrigger, Contract::GC_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } } } break; case GC_NoTrigger: m_pClrDebugState->ViolationMaskReset( GCViolation ); // Inlined BeginNoTriggerGC m_pClrDebugState->IncrementGCNoTriggerCount(); if (m_pThread && m_pThread->m_fPreemptiveGCDisabled) { m_pClrDebugState->IncrementGCForbidCount(); } break; case GC_Disabled: // Nothing break; default: UNREACHABLE(); } // Host Triggers check switch (testmask & HOST_Mask) { case HOST_Calls: { if (!m_pClrDebugState->IsHostCaller()) { if (!( (HostViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) { // Avoid infinite recursion by temporarily allowing HOST_CALLS // violations so that we don't get contract asserts in anything // called downstream of CONTRACT_ASSERT. If we unwind out of // here, our dtor will reset our state to what it was on entry. CONTRACT_VIOLATION(HostViolation); CONTRACT_ASSERT("HOST_CALLS encountered in a HOST_NOCALLS scope", Contract::HOST_NoCalls, Contract::HOST_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } } } break; case HOST_NoCalls: // m_pClrDebugState->ViolationMaskReset( HostViolation ); m_pClrDebugState->ResetHostCaller(); break; case HOST_Disabled: // Nothing break; default: UNREACHABLE(); } END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; // EE Thread-required check // NOTE: The following must NOT be inside BEGIN/END_GETTHREAD_ALLOWED, // as the change to m_pClrDebugState->m_allowGetThread below would be // overwritten by END_GETTHREAD_ALLOWED. switch (testmask & EE_THREAD_Mask) { case EE_THREAD_Required: if (!((EEThreadViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) { if (m_pThread == NULL) { CONTRACT_ASSERT("EE_THREAD_REQUIRED encountered with no current EE Thread object in TLS.", Contract::EE_THREAD_Required, Contract::EE_THREAD_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } else if (!m_pClrDebugState->IsGetThreadAllowed()) { // In general, it's unsafe for an EE_THREAD_NOT_REQUIRED function to // call an EE_THREAD_REQUIRED function. In cases where it is safe, // you may wrap the call to the EE_THREAD_REQUIRED function inside a // BEGIN/END_GETTHREAD_ALLOWED block, but you may only do so if the // case where GetThread() == NULL is clearly handled in a way that // prevents entry into the BEGIN/END_GETTHREAD_ALLOWED block. CONTRACT_ASSERT("EE_THREAD_REQUIRED encountered in an EE_THREAD_NOT_REQUIRED scope, without an intervening BEGIN/END_GETTHREAD_ALLOWED block.", Contract::EE_THREAD_Required, Contract::EE_THREAD_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } } m_pClrDebugState->SetGetThreadAllowed(); break; case EE_THREAD_Not_Required: m_pClrDebugState->ResetGetThreadAllowed(); break; case EE_THREAD_Disabled: break; default: UNREACHABLE(); } }