Exception Handling in the Profiling API
Exception notifications are the most difficult of all notifications to describe and to understand. This topic describes exception processing and explains how the profiling API handles various types of exceptions.
Exception Notification Flow Chart
Exception processing is inherently complex. The exception notifications described in this topic provide all the information that a sophisticated profiler requires to keep track of the pass (search phase or unwind phase), the frame, the filter, and the finally block that is executed for every thread in the profiled process. Exception notifications do not provide any ThreadIDs, but you can call the ICorProfilerInfo::GetCurrentThreadID method to discover which managed thread has thrown the exception.
The following illustration shows how the code profiler receives various callbacks when it monitors exception events. Each thread starts out in the normal execution state. When the thread is in a state inside the exception system (in the search phase or unwind phase), it is controlled by the exception system. Any non-exception-related callbacks (for example, ICorProfilerCallback::ObjectAllocated) that occur while the thread is in one of these states may be attributed to the exception system itself. When the thread is in a state outside the exception system, it is running arbitrary managed code.
Exception callback sequence
Threads that have crossed into managed code while processing an exception could throw another exception, which would result in a whole new pass of exception handling. (This new pass is indicated by "New exception handling pass" in the previous illustration.) If such a nested exception escapes the filter/finally/catch blocks from the original exception, this can affect the original exception as follows:
If the nested exception occurred within a filter block and escapes the filter block, the filter will be considered to return false and the first pass will continue.
If the nested exception occurred within a finally block and escapes the finally block, the original exception's processing will never resume.
If the nested exception occurred within a catch block and escapes the catch block, the original exception's processing will never resume.
An exception may be handled in unmanaged code. In this case, the profiler will see the unwind phase but will not receive notification of catch handlers. Execution will just resume normally in the unmanaged code. A profiler that is aware of unmanaged code will be able to detect this situation, but a managed-only profiler may see any number of things, including but not limited to the following:
An ICorProfilerCallback::UnmanagedToManagedTransition callback when the unmanaged code calls or returns to managed code.
Thread termination (if the unmanaged code was at the root of the thread).
Application termination (if the unmanaged code terminates the application).
An exception might be handled by the common language runtime (CLR) itself. In this case, the profiler will see the unwind phase but will not receive notification of catch handlers. It may see execution resume normally in managed or unmanaged code.
By default, an unhandled exception will lead to process termination in the .NET Framework version 2.0. You can force adherence to the .NET Framework version 1 exception policy by using an application compatibility flag, as described in Exceptions in Managed Threads.