Fix stack walker on Unix
authorJan Vorlicek <janvorli@microsoft.com>
Wed, 24 Feb 2016 10:51:43 +0000 (11:51 +0100)
committerJan Vorlicek <janvorli@microsoft.com>
Fri, 26 Feb 2016 01:10:13 +0000 (02:10 +0100)
This change fixes the stack walker on Unix to properly account for the cases
when funclet frames were reclaimed due to native frames unwinding and so
they are not on the stack anymore. The stack walker needs to know that to
properly skip reporting GC references for the parent frame of the funclet.
While there was already code attempting to do that, it was incorrectly
skipping some exception trackers and not skipping the current tracker
in case the catch handler was not called yet.
This problem was discovered while running stress tests with GCStress 3.

Commit migrated from https://github.com/dotnet/coreclr/commit/12f1fcdbec8cdc862953f90b4b420d3e4e74657b

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

index 9acebc2..d5dd480 100644 (file)
@@ -565,7 +565,7 @@ public:
         return m_EnclosingClauseInfoOfCollapsedTracker.GetEnclosingClauseCallerSP();
     }
 
-#ifndef FEATURE_PAL          
+#ifndef FEATURE_PAL
 private:
     EHWatsonBucketTracker m_WatsonBucketTracker;
 public:
index ca0ac61..bde71db 100644 (file)
@@ -538,6 +538,8 @@ BOOL DebuggerExState::SetDebuggerInterceptInfo(IJitManager *pJitManager,
 }
 #endif // DEBUGGING_SUPPORTED
 
+#endif // DACCESS_COMPILE
+
 EHClauseInfo* ThreadExceptionState::GetCurrentEHClauseInfo()
 {
 #ifdef WIN64EXCEPTIONS
@@ -565,8 +567,6 @@ EHClauseInfo* ThreadExceptionState::GetCurrentEHClauseInfo()
 #endif // WIN64EXCEPTIONS
 }
 
-#endif // DACCESS_COMPILE
-
 void ThreadExceptionState::SetThreadExceptionFlag(ThreadExceptionFlag flag)
 {
     LIMITED_METHOD_CONTRACT;
index 7c0fbb1..ec9acfb 100644 (file)
@@ -1753,36 +1753,40 @@ ProcessFuncletsForGCReporting:
                         // and so we can detect it just from walking the stack.
                         if (!fSkippingFunclet && (pTracker != NULL))
                         {
-                            bool fFoundFuncletParent = false;
-
-                            // First check if the current frame is a caller of a funclet of a collapsed exception tracker
-                            StackFrame sfFuncletParent = pTracker->GetCallerOfCollapsedActualHandlingFrame();
-                            if (!sfFuncletParent.IsNull() && ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, sfFuncletParent))
+                            // 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
+                            // frame matching the current frame, it means that the funclet stack frame was reclaimed.
+                            StackFrame sfFuncletParent;
+                            ExceptionTracker* pCurrTracker = pTracker;
+                            bool hasFuncletStarted = m_crawl.pThread->GetExceptionState()->GetCurrentEHClauseInfo()->IsManagedCodeEntered();
+
+                            while (pCurrTracker != NULL)
                             {
-                                fFoundFuncletParent = true;
-                            }
-                            else
-                            {
-                                ExceptionTracker* pCurrTracker = pTracker;
-
-                                // Scan all previous trackers and see if the current frame is a caller of any of
-                                // the handling frames. 
-                                while ((pCurrTracker = pCurrTracker->GetPreviousExceptionTracker()) != NULL)
+                                if (hasFuncletStarted)
                                 {
                                     sfFuncletParent = pCurrTracker->GetCallerOfActualHandlingFrame();
-                                    if (ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, sfFuncletParent))
+                                    if (!sfFuncletParent.IsNull() && ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, sfFuncletParent))
                                     {
-                                        pTracker = pCurrTracker;
-                                        fFoundFuncletParent = true;
-
                                         break;
                                     }
                                 }
+
+                                sfFuncletParent = pCurrTracker->GetCallerOfCollapsedActualHandlingFrame();
+                                if (!sfFuncletParent.IsNull() && ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, sfFuncletParent))
+                                {
+                                    break;
+                                }
+
+                                // Funclets handling exception for trackers older than the current one were always started,
+                                // since the current tracker was created due to an exception in the funclet belonging to 
+                                // the previous tracker.
+                                hasFuncletStarted = true;
+                                pCurrTracker = pCurrTracker->GetPreviousExceptionTracker();
                             }
 
-                            if (fFoundFuncletParent)
+                            if (pCurrTracker != NULL)
                             {
-                                // We have found that the current frame is a caller of a handling frame.
+                                // The current frame is a parent of a funclet that was already unwound and removed from the stack
                                 // Set the members the same way we would set them on Windows when we
                                 // would detect this just from stack walking.
                                 m_sfParent = sfFuncletParent;