Fix stack walking on Unix in case of finally
authorJan Vorlicek <janvorli@microsoft.com>
Mon, 7 Mar 2016 22:17:10 +0000 (23:17 +0100)
committerJan Vorlicek <janvorli@microsoft.com>
Tue, 8 Mar 2016 21:02:08 +0000 (22:02 +0100)
The issue is that the code in the StackFrameIterator::Filter that handles cases
when a funclet frame that was already removed from the stack due to native frames
unwinding works for catch funclets only and not for finally ones.
The ExceptionTracker::GetCallerOfActualHandlingFrame is set for catch funclets
only. To make it work for the finally funclets as well, we need to use information
from the ExceptionTracker::m_EnclosingClauseInfoForGCReporting instead.

There was also another problem in the Filter method that caused the function to
spin in an infinite loop when a parent of a funclet was also a funclet. In
such case, the code in the method rechecks the current frame but the special
functionality to check the exception trackers data needs to be skipped for
the recheck.

And finally, when we find that the current frame was a parent of an unwound
funclet from the evidence in the exception trackers, we also need to set
the fSkippingFunclet.

Commit migrated from https://github.com/dotnet/coreclr/commit/76c2dfc6321bff28c90ac48b0f2ca5abb02abe72

src/coreclr/src/vm/exceptionhandling.h
src/coreclr/src/vm/stackwalk.cpp

index d5dd480..2a8181b 100644 (file)
@@ -558,7 +558,14 @@ public:
         return m_sfCallerOfActualHandlerFrame;
     }
 
-    StackFrame GetCallerOfCollapsedActualHandlingFrame()
+    StackFrame GetCallerOfEnclosingClause()
+    {
+        LIMITED_METHOD_CONTRACT;
+
+        return m_EnclosingClauseInfoForGCReporting.GetEnclosingClauseCallerSP();
+    }
+
+    StackFrame GetCallerOfCollapsedEnclosingClause()
     {
         LIMITED_METHOD_CONTRACT;
 
index ec9acfb..a2eb289 100644 (file)
@@ -1689,8 +1689,6 @@ StackWalkAction StackFrameIterator::Filter(void)
 ProcessFuncletsForGCReporting:
                 do
                 {
-                    fRecheckCurrentFrame = false;
-                    
                     // When enumerating GC references for "liveness" reporting, depending upon the architecture,
                     // the responsibility of who reports what varies:
                     //
@@ -1751,7 +1749,7 @@ ProcessFuncletsForGCReporting:
                         // only source of evidence about it.
                         // This is different from Windows where the full stack is preserved until an exception is fully handled
                         // and so we can detect it just from walking the stack.
-                        if (!fSkippingFunclet && (pTracker != NULL))
+                        if (!fRecheckCurrentFrame && !fSkippingFunclet && (pTracker != NULL))
                         {
                             // The stack walker is not skipping frames now, which means it didn't find a funclet frame that
                             // would require skipping the current frame. If we find a tracker with caller of actual handling
@@ -1764,14 +1762,14 @@ ProcessFuncletsForGCReporting:
                             {
                                 if (hasFuncletStarted)
                                 {
-                                    sfFuncletParent = pCurrTracker->GetCallerOfActualHandlingFrame();
+                                    sfFuncletParent = pCurrTracker->GetCallerOfEnclosingClause();
                                     if (!sfFuncletParent.IsNull() && ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, sfFuncletParent))
                                     {
                                         break;
                                     }
                                 }
 
-                                sfFuncletParent = pCurrTracker->GetCallerOfCollapsedActualHandlingFrame();
+                                sfFuncletParent = pCurrTracker->GetCallerOfCollapsedEnclosingClause();
                                 if (!sfFuncletParent.IsNull() && ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, sfFuncletParent))
                                 {
                                     break;
@@ -1793,9 +1791,12 @@ ProcessFuncletsForGCReporting:
                                 m_sfFuncletParent = sfFuncletParent;
                                 m_fProcessNonFilterFunclet = true;
                                 m_fDidFuncletReportGCReferences = false;
+                                fSkippingFunclet = true;
                             }
                         }
 #endif // FEATURE_PAL
+
+                        fRecheckCurrentFrame = false;
                         // Do we already have a reference to a funclet parent?
                         if (!m_sfFuncletParent.IsNull())
                         {
@@ -2000,6 +2001,7 @@ ProcessFuncletsForGCReporting:
                                         // Since we are in GC reference reporting mode,
                                         // then avoid code duplication and go to
                                         // funclet processing.
+                                        fRecheckCurrentFrame = true;
                                         goto ProcessFuncletsForGCReporting;
                                     }
                                 }
@@ -2105,6 +2107,7 @@ ProcessFuncletsForGCReporting:
                                 // If we are in GC reference reporting mode,
                                 // then avoid code duplication and go to
                                 // funclet processing.
+                                fRecheckCurrentFrame = true;
                                 goto ProcessFuncletsForGCReporting;
                             }
                             else