分析 API 中的异常处理

异常通知是所有通知中最难描述和理解的通知。 本主题描述异常处理,并说明分析 API 如何处理各种类型的异常。

异常通知流程图

异常处理在本质上非常复杂。 本主题中描述的异常通知提供了所有信息,复杂探查器需要这些信息来跟踪处理过程(搜索阶段或展开阶段)、帧、filter 以及针对分析进程中的每个线程执行的 finally 块。 异常通知未提供任何 ThreadID,但您可以调用 ICorProfilerInfo::GetCurrentThreadID 方法来找出哪个托管线程引发了异常。

下面的插图演示代码探查器在监视异常事件时将如何收到各种回调。 每个线程开始时都处于正常执行状态。 如果线程(在搜索阶段或展开阶段中)处于异常系统内的某个状态,该线程就受到异常系统的控制。 在线程处于其中一种状态时发生的任何非异常相关回调(例如,ICorProfilerCallback::ObjectAllocated)可能都是异常系统本身的结果。 如果线程处于异常系统外的某个状态,则它正在运行任意托管代码。

异常回调顺序

异常回调序列异常回调序列

嵌套的异常

在处理异常时穿入托管代码的线程可能会引发另一个异常,而这将导致一轮新的异常处理过程。 (这个新的处理过程由上图中的“新异常处理过程”指示。)如果此类嵌套异常从原始异常中退出 filter/finally/catch 块,这可能会对原始异常产生如下影响:

  • 如果嵌套异常出现在 filter 块内并退出 filter 块,则认为 filter 将返回 false,并且第一个处理过程将继续。

  • 如果嵌套异常出现在 finally 块内并退出 finally 块,则原始异常的处理将永远不会继续。

  • 如果嵌套异常出现在 catch 块内并退出 catch 块,则原始异常的处理将永远不会继续。

非托管处理程序

可能会在非托管代码中处理异常。 在这种情况下,探查器将看到展开阶段,但不会收到 catch 处理程序的通知。 执行将只是以正常方式在非托管代码中继续。 可识别非托管代码的探查器将能够检测到此情况,但仅托管的探查器则可能会看到很多情况,包括但不限于以下情况:

  • ICorProfilerCallback::UnmanagedToManagedTransition 回调(当非托管代码调用或返回到托管代码时)。

  • 线程终止(如果非托管代码位于线程的根处)。

  • 应用程序终止(如果非托管代码终止了应用程序)。

CLR 处理程序

异常可能由公共语言运行时 (CLR) 本身处理。 在这种情况下,探查器将看到展开阶段,但不会收到 catch 处理程序的通知。 它可能会看到执行以正常方式在托管或非托管代码中继续。

未经处理的异常

默认情况下,未经处理的异常在 .NET Framework 2.0 版中将导致进程终止。 通过使用应用程序兼容性标志(如托管线程中的异常中所述),可以强制遵循 .NET Framework 版本 1 的异常策略。

请参见

概念

分析概述