diff options
author | Jan Vorlicek <janvorli@microsoft.com> | 2015-03-30 12:06:12 +0200 |
---|---|---|
committer | Jan Vorlicek <janvorli@microsoft.com> | 2015-03-30 12:06:12 +0200 |
commit | 73a8ac6fdbf12eff1102adcac62750c209789589 (patch) | |
tree | 0307c8cf9edf6fee6677f6661755d380987556f6 | |
parent | 898cdcf05ae334252b354a1f5bae9e26be1912ec (diff) | |
download | coreclr-73a8ac6fdbf12eff1102adcac62750c209789589.tar.gz coreclr-73a8ac6fdbf12eff1102adcac62750c209789589.tar.bz2 coreclr-73a8ac6fdbf12eff1102adcac62750c209789589.zip |
Fix GC exception related issue
This change fixes a problem that has occured when GC was called from
an exception handler during split exception handling. The problem
manifestated itself as a crash in the HasFrameBeenUnwoundByAnyActiveException
function due to the pInitialExplicitFrame being NULL.
The cause of this issue was caused by a combination of two issues.
First, the m_ScannedStackRange was not reset in the partial 2nd pass to
the state at the beginning of the corresponding partial 1st pass.
And second, the 'unwind has started' flag was not reset after
restoring exception flags in the new exception tracker after switching
from the 2nd back to the 1st pass during the split exception handling.
There was one additional issue, several additional members of the exception
tracker needed to be propagated to the new tracker. These members are
needed by the HasFrameBeenUnwoundByAnyActiveException function.
Since the number of state members that are propagated from the old to the
new exception tracker has grown, I've refactored the code to keep those
in a separate class.
-rw-r--r-- | src/vm/exceptionhandling.cpp | 61 | ||||
-rw-r--r-- | src/vm/exceptionhandling.h | 33 |
2 files changed, 72 insertions, 22 deletions
diff --git a/src/vm/exceptionhandling.cpp b/src/vm/exceptionhandling.cpp index 7e7dc57a5f..0d89fbb281 100644 --- a/src/vm/exceptionhandling.cpp +++ b/src/vm/exceptionhandling.cpp @@ -3518,6 +3518,36 @@ void ExceptionTracker::PopTrackers( } } +// Save the state of the source exception tracker +void ExceptionTracker::PartialTrackerState::Save(const ExceptionTracker* pSourceTracker) +{ + m_sfResumeStackFrame = pSourceTracker->m_sfResumeStackFrame; + m_ScannedStackRange = pSourceTracker->m_ScannedStackRange; + m_uCatchToCallPC = pSourceTracker->m_uCatchToCallPC; + m_pClauseForCatchToken = pSourceTracker->m_pClauseForCatchToken; + m_ClauseForCatch = pSourceTracker->m_ClauseForCatch; + m_ExceptionFlags = pSourceTracker->m_ExceptionFlags; + m_sfLastUnwoundEstablisherFrame = pSourceTracker->m_sfLastUnwoundEstablisherFrame; + m_EHClauseInfo = pSourceTracker->m_EHClauseInfo; + m_EnclosingClauseInfo = pSourceTracker->m_EnclosingClauseInfo; + m_EnclosingClauseInfoForGCReporting = pSourceTracker->m_EnclosingClauseInfoForGCReporting; +} + +// Restore the state into the target exception tracker +void ExceptionTracker::PartialTrackerState::Restore(ExceptionTracker* pTargetTracker) +{ + pTargetTracker->m_sfResumeStackFrame = m_sfResumeStackFrame; + pTargetTracker->m_ScannedStackRange = m_ScannedStackRange; + pTargetTracker->m_uCatchToCallPC = m_uCatchToCallPC; + pTargetTracker->m_pClauseForCatchToken = m_pClauseForCatchToken; + pTargetTracker->m_ClauseForCatch = m_ClauseForCatch; + pTargetTracker->m_ExceptionFlags = m_ExceptionFlags; + pTargetTracker->m_sfLastUnwoundEstablisherFrame = m_sfLastUnwoundEstablisherFrame; + pTargetTracker->m_EHClauseInfo = m_EHClauseInfo; + pTargetTracker->m_EnclosingClauseInfo = m_EnclosingClauseInfo; + pTargetTracker->m_EnclosingClauseInfoForGCReporting = m_EnclosingClauseInfoForGCReporting; +} + // // static ExceptionTracker* ExceptionTracker::GetOrCreateTracker( @@ -3550,12 +3580,7 @@ ExceptionTracker* ExceptionTracker::GetOrCreateTracker( bool fIsInterleavedHandling = false; bool fTransitionFromSecondToFirstPass = false; - StackFrame previousTrackerResumeStackFrame; - StackRange previousTrackerScannedStackRange; - UINT_PTR previousTrackerCatchToCallPC; - PTR_EXCEPTION_CLAUSE_TOKEN previousTrackerClauseForCatchToken; - EE_ILEXCEPTION_CLAUSE previousTrackerClauseForCatch; - ExceptionFlags previousTrackerExceptionFlags; + PartialTrackerState previousTrackerPartialState; // Initialize the out parameter. *pStackTraceState = STS_Append; @@ -3594,12 +3619,7 @@ ExceptionTracker* ExceptionTracker::GetOrCreateTracker( { // Remember part of the current state that needs to be transferred to // the newly created tracker. - previousTrackerResumeStackFrame = pTracker->m_sfResumeStackFrame; - previousTrackerScannedStackRange = pTracker->m_ScannedStackRange; - previousTrackerCatchToCallPC = pTracker->m_uCatchToCallPC; - previousTrackerClauseForCatchToken = pTracker->m_pClauseForCatchToken; - previousTrackerClauseForCatch = pTracker->m_ClauseForCatch; - previousTrackerExceptionFlags = pTracker->m_ExceptionFlags; + previousTrackerPartialState.Save(pTracker); // We just transitioned from 2nd pass to 1st pass when we handle the exception in an interleaved manner EH_LOG((LL_INFO100, ">>continued processing of PREVIOUS exception (interleaved handling)\n")); @@ -3695,12 +3715,12 @@ ExceptionTracker* ExceptionTracker::GetOrCreateTracker( // series of first and second passes. When we create a new tracker as a result // of switching from the 2nd pass back to the 1st pass, we need to carry over // several members that were set when processing one of the previous frames. - pNewTracker->m_sfResumeStackFrame = previousTrackerResumeStackFrame; - pNewTracker->m_uCatchToCallPC = previousTrackerCatchToCallPC; - pNewTracker->m_pClauseForCatchToken = previousTrackerClauseForCatchToken; - pNewTracker->m_ClauseForCatch = previousTrackerClauseForCatch; - pNewTracker->m_ExceptionFlags = previousTrackerExceptionFlags; - pNewTracker->m_ScannedStackRange = previousTrackerScannedStackRange; + previousTrackerPartialState.Restore(pNewTracker); + // Reset the 'unwind has started' flag to indicate we are in the first pass again + pNewTracker->m_ExceptionFlags.ResetUnwindHasStarted(); + // Remember the current scanned stack range so that we can restore it after + // switching to the 2nd pass. + pNewTracker->m_secondPassInitialScannedStackRange = pNewTracker->m_ScannedStackRange; } CONSISTENCY_CHECK(pNewTracker->IsValid()); @@ -3853,10 +3873,7 @@ ExceptionTracker* ExceptionTracker::GetOrCreateTracker( // We have to detect this transition because otherwise we break when unmanaged code // catches our exceptions. EH_LOG((LL_INFO100, ">>tracker transitioned to second pass\n")); - if (!fIsInterleavedHandling) - { - pTracker->m_ScannedStackRange.Reset(); - } + pTracker->m_ScannedStackRange = pTracker->m_secondPassInitialScannedStackRange; pTracker->m_ExceptionFlags.SetUnwindHasStarted(); if (pTracker->m_ExceptionFlags.UnwindingToFindResumeFrame()) diff --git a/src/vm/exceptionhandling.h b/src/vm/exceptionhandling.h index 5b2568aadc..9362fa4a65 100644 --- a/src/vm/exceptionhandling.h +++ b/src/vm/exceptionhandling.h @@ -660,11 +660,44 @@ private: ; bool m_fEnclosingClauseIsFunclet; }; + // This class saves partial state of an exception tracker that needs to + // be preserved when transitioning from the 2nd to 1st pass during the + // interleaved exception handling before processing next segment of + // managed stack frames. + // The exception tracker is recreated during such transition and + // only a subset of the exception tracker state defined by this class + // is propagated to the new tracker instance. + class PartialTrackerState + { + StackFrame m_sfResumeStackFrame; + StackRange m_ScannedStackRange; + UINT_PTR m_uCatchToCallPC; + PTR_EXCEPTION_CLAUSE_TOKEN m_pClauseForCatchToken; + EE_ILEXCEPTION_CLAUSE m_ClauseForCatch; + ExceptionFlags m_ExceptionFlags; + StackFrame m_sfLastUnwoundEstablisherFrame; + EHClauseInfo m_EHClauseInfo; + EnclosingClauseInfo m_EnclosingClauseInfo; + EnclosingClauseInfo m_EnclosingClauseInfoForGCReporting; + + public: + // Save the state of the source exception tracker + void Save(const ExceptionTracker* pSourceTracker); + // Restore the state into the target exception tracker + void Restore(ExceptionTracker* pTargetTracker); + }; + PTR_ExceptionTracker m_pPrevNestedInfo; Thread* m_pThread; // this is used as an IsValid/IsFree field -- if it's NULL, the allocator can // reuse its memory, if it's non-NULL, it better be a valid thread pointer StackRange m_ScannedStackRange; + // Range used to initialize the m_ScannedStackRange at start of the 2nd pass. + // This is: + // 1) null range for non-interleaved exception handling and the very first + // 2nd pass of the interleaved handling and + // 2) non-null for the other 2nd passes of the interleaved handling. + StackRange m_secondPassInitialScannedStackRange; DAC_EXCEPTION_POINTERS m_ptrs; OBJECTHANDLE m_hThrowable; StackTraceInfo m_StackTraceInfo; |