Fix GCStress 0xC on Unix (dotnet/coreclr#5276)
authorJan Vorlicek <janvorli@microsoft.com>
Fri, 27 May 2016 19:19:31 +0000 (21:19 +0200)
committerJan Kotas <jkotas@microsoft.com>
Fri, 27 May 2016 19:19:31 +0000 (12:19 -0700)
This change fixes GCStress 0xC on Unix that was not working due to the way that
we check that a specific IP is in managed code. To make that check safe,
a precondition checking that the thread is in cooperative GC mode was added
some time ago. But that causes a problem with the GCStress 0xC and PInvoke
stubs. The PInvoke stub switches to preemptive mode at some point and when
the code beyond that point is instrumented and the corresponding hardware
exception occurs, the exception address is not evaluated as being in managed
code and so the exception terminates the application.
This fix excludes the safety check of GC mode when we are running with GC stress
mode 8 or 4 (or both -> 0xc).

Commit migrated from https://github.com/dotnet/coreclr/commit/20f7d04e477d2d2901c7e76a78e060b8d34ed1c1

src/coreclr/src/vm/exceptionhandling.cpp

index 58a796a..64725f6 100644 (file)
@@ -5048,19 +5048,29 @@ bool IsDivByZeroAnIntegerOverflow(PCONTEXT pContext)
 }
 #endif //_AMD64_
 
-BOOL PALAPI IsSafeToHandleHardwareException(PCONTEXT contextRecord, PEXCEPTION_RECORD exceptionRecord)
+BOOL IsSafeToCallExecutionManager()
 {
     Thread *pThread = GetThread();
-    PCODE controlPc = GetIP(contextRecord);
+
     // It is safe to call the ExecutionManager::IsManagedCode only if the current thread is in
     // the cooperative mode. Otherwise ExecutionManager::IsManagedCode could deadlock if 
     // the exception happened when the thread was holding the ExecutionManager's writer lock.
     // When the thread is in preemptive mode, we know for sure that it is not executing managed code.
-    BOOL isManagedCode = (pThread != NULL && pThread->PreemptiveGCDisabled() && ExecutionManager::IsManagedCode(controlPc));
+    // Unfortunately, when running GC stress mode that invokes GC after every jitted or NGENed
+    // instruction, we need to relax that to enable instrumentation of PInvoke stubs that switch to
+    // preemptive GC mode at some point.
+    return ((pThread != NULL) && pThread->PreemptiveGCDisabled()) || 
+           GCStress<cfg_instr_jit>::IsEnabled() || 
+           GCStress<cfg_instr_ngen>::IsEnabled();
+}
+
+BOOL PALAPI IsSafeToHandleHardwareException(PCONTEXT contextRecord, PEXCEPTION_RECORD exceptionRecord)
+{
+    PCODE controlPc = GetIP(contextRecord);
     return g_fEEStarted && (
         exceptionRecord->ExceptionCode == STATUS_BREAKPOINT || 
         exceptionRecord->ExceptionCode == STATUS_SINGLE_STEP ||
-        isManagedCode ||
+        (IsSafeToCallExecutionManager() && ExecutionManager::IsManagedCode(controlPc)) ||
         IsIPInMarkedJitHelper(controlPc));
 }
 
@@ -5072,10 +5082,8 @@ VOID PALAPI HandleHardwareException(PAL_SEHException* ex)
     {
         // A hardware exception is handled only if it happened in a jitted code or 
         // in one of the JIT helper functions (JIT_MemSet, ...)
-        Thread *pThread = GetThread();
         PCODE controlPc = GetIP(&ex->ContextRecord);
-        BOOL isManagedCode = (pThread != NULL && pThread->PreemptiveGCDisabled() && ExecutionManager::IsManagedCode(controlPc));
-        if (isManagedCode && IsGcMarker(ex->ExceptionRecord.ExceptionCode, &ex->ContextRecord))
+        if (ExecutionManager::IsManagedCode(controlPc) && IsGcMarker(ex->ExceptionRecord.ExceptionCode, &ex->ContextRecord))
         {
             RtlRestoreContext(&ex->ContextRecord, &ex->ExceptionRecord);
             UNREACHABLE();