Repository: dotnet/runtime
File: src/coreclr/vm/exceptionhandling.cpp (C++)

Code before:
                // If the frame is ProtectValueClassFrame, move to the next one as we want to report the FuncEvalFrame
                if (pFrame->GetFrameIdentifier() == FrameIdentifier::ProtectValueClassFrame)
                {
                    pFrame = pFrame->PtrNextFrame();
                    _ASSERTE(pFrame != FRAME_TOP);
                }
                if ((pFrame->GetFrameIdentifier() == FrameIdentifier::FuncEvalFrame) || IsTopmostDebuggerU2MCatchHandlerFrame(pFrame))
                {
                    EEToDebuggerExceptionInterfaceWrapper::NotifyOfCHFFilter((EXCEPTION_POINTERS *)&pExInfo->m_ptrs, pFrame);
                }
            }
        }
    }
}

NOINLINE static void NotifyFunctionEnterHelper(StackFrameIterator *pThis, Thread *pThread, ExInfo *pExInfo)
{
    MethodDesc *pMD = pThis->m_crawl.GetFunction();

    if (pThis->m_crawl.IsFunclet() && (pMD == pExInfo->m_pMDToReportFunctionLeave))
    {
        // In case of a funclet, the pMD represents the parent of the funclet. We only want to report entering and leaving
        // the method once. So we ignore transitions from the funclet to the parent method or the funclet into another funclet
        // of the same parent method.
        return;
    }

    if (pExInfo->m_passNumber == 1)
    {
        if (pExInfo->m_pMDToReportFunctionLeave != NULL)
        {
            EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionLeave(pExInfo->m_pMDToReportFunctionLeave);
        }
        EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionEnter(pMD);
    }
    else
    {
        if (pExInfo->m_pMDToReportFunctionLeave != NULL)
        {
            EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFunctionLeave(pExInfo->m_pMDToReportFunctionLeave);
        }
        EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFunctionEnter(pMD);
    }

    pExInfo->m_pMDToReportFunctionLeave = pMD;
}

static void NotifyFunctionEnter(StackFrameIterator *pThis, Thread *pThread, ExInfo *pExInfo)
{
    BEGIN_PROFILER_CALLBACK(CORProfilerTrackExceptions());
    // We don't need to do any notifications for the profiler if we are not tracking exceptions.

Review comment (suggestion):
Consider adding a test case that validates this fix by checking that ExceptionSearchFunctionEnter/ExceptionSearchFunctionLeave and ExceptionUnwindFunctionEnter/ExceptionUnwindFunctionLeave are called exactly once per method when an exception is thrown from within a try-finally block. This would prevent regression of issue #123351. 

A test could be structured similar to the scenario in the issue: a method with a try-finally block that throws from the finally, with a profiler that tracks and validates the count of Enter/Leave pairs for each FunctionID.

Code after:
                // If the frame is ProtectValueClassFrame, move to the next one as we want to report the FuncEvalFrame
                if (pFrame->GetFrameIdentifier() == FrameIdentifier::ProtectValueClassFrame)
                {
                    pFrame = pFrame->PtrNextFrame();
                    _ASSERTE(pFrame != FRAME_TOP);
                }
                if ((pFrame->GetFrameIdentifier() == FrameIdentifier::FuncEvalFrame) || IsTopmostDebuggerU2MCatchHandlerFrame(pFrame))
                {
                    EEToDebuggerExceptionInterfaceWrapper::NotifyOfCHFFilter((EXCEPTION_POINTERS *)&pExInfo->m_ptrs, pFrame);
                }
            }
        }
    }
}

NOINLINE static void NotifyFunctionEnterHelper(StackFrameIterator *pThis, Thread *pThread, ExInfo *pExInfo)
{
    MethodDesc *pMD = pThis->m_crawl.GetFunction();

    if (pExInfo->m_reportedFunctionEnterWasForFunclet && (pMD == pExInfo->m_pMDToReportFunctionLeave))
    {
        // In case of a funclet, the pMD represents the parent of the funclet. We only want to report entering and leaving
        // the method once. So we ignore transitions from the funclet to the parent method or the funclet into another funclet
        // of the same parent method.
        return;
    }

    if (pExInfo->m_passNumber == 1)
    {
        if (pExInfo->m_pMDToReportFunctionLeave != NULL)
        {
            EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionLeave(pExInfo->m_pMDToReportFunctionLeave);
        }
        EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionEnter(pMD);
    }
    else
    {
        if (pExInfo->m_pMDToReportFunctionLeave != NULL)
        {
            EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFunctionLeave(pExInfo->m_pMDToReportFunctionLeave);
        }
        EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFunctionEnter(pMD);
    }

    pExInfo->m_pMDToReportFunctionLeave = pMD;
    pExInfo->m_reportedFunctionEnterWasForFunclet = pThis->m_crawl.IsFunclet();
}

static void NotifyFunctionEnter(StackFrameIterator *pThis, Thread *pThread, ExInfo *pExInfo)
{
    BEGIN_PROFILER_CALLBACK(CORProfilerTrackExceptions());