如何调试仅我代码?

有时开发人员只想调试他们编写的代码,而不想调试应用程序中的第三方代码(如框架和库)。当用户和非用户代码在彼此之间来回调用时,这一点尤其有用。clr调试服务有许多新特性来支持这一点,我们称之为“Just My Code”(JMC)调试。

允许调试器将每个函数标记为用户代码或非用户代码。由调试器决定哪些是用户代码,哪些不是用户代码。visualstudio将使用来自项目系统的提示,如果缺少符号,还将假定给定模块是否为非用户代码。您也可以使用System.Diagnostics.DebuggerNonUserCodeAttribute属性告诉VS将特定方法标记为非用户代码。

-允许步进操作神奇地跳过所有非用户代码。

-提供其他异常通知。

调试器还可以执行其他操作,例如从调用堆栈中过滤非用户代码。

一个非常简单的JMC步进示例

using System;

class Program

{

    static void Main()

    {

        NonUserLibraryCode(); // <-- step in here (F11 in Visual Studio)

    }

    // This attribute tells the debugger to mark this function as non-user code.

    [System.Diagnostics.DebuggerNonUserCode]

    static void NonUserLibraryCode()

    {

        Console.WriteLine("Before");

        UserCode();

        Console.WriteLine("After");

    }

   

    static void UserCode()

    {

        Console.WriteLine("User1"); // <-- step completes here, skipping the non-user code

    }

}

VS还可以从调用堆栈中过滤出非用户代码。因此,当在UserCode()中停止时,调用堆栈看起来像:

>          ConsoleApplication2.exe!Program.UserCode() Line 21            C#

            [External Code]           

            ConsoleApplication2.exe!Program.Main() Line 7 + 0x6 bytes   C#

            [External Code]      

在VS中,JMC可以从Tools | Options | Debugging、“只启用我的代码”复选框中禁用(必须启用它才能使这些示例正常工作)。调用堆栈过滤可以通过右键单击调用堆栈来切换“显示外部代码”。

为什么JMC在技术层面很酷。

jmc-stepping在这方面有一些很好的技术成就。它是…

1)不限于静态分析(如上所示)。JMC可以处理任何东西,包括任意深度的调用堆栈、多播委托、多态/虚拟函数和事件+回调。这里没有烟雾和镜子——你可以任意地构造复杂的例子。
2)非常出色。步骤操作可以跳过大量非用户代码,而不会导致性能损失。相比之下,这是试图伪造JMC,只使用传统的单步操作跳过所有非用户代码(这将非常慢)。
3)线程安全。您可以在多线程程序中使用JMC单步执行而没有任何问题。(其他一些解决方案将在这里分解)
4)非常可配置。您可以在每个函数级别上设置JMC状态,并且可以在运行时切换该状态。

JMC-stepping在ICorDebug级别:
ICorDebug通过ICorDebugStepper对象实现托管单步执行。关于托管步进实际工作方式的所有底层细节都是从调试器中抽象出来的。调试器通过调用ICorDebugStepper2::SetJMC(true)来创建JMC步进程序。jmcstepper将神奇地跳过所有非用户代码。所有非JMC步进器(我称之为“传统步进器”)都忽略JMC状态。因此调试器必须显式地请求JMC,否则它将获得前JMC行为。这使得JMC功能不中断。
CLR默认假设所有内容都是非用户代码,并通过ICorDebug API将所有JMC决策推迟到调试器。调试器必须通过调用ICorDebug(Function | Class | Module)::SetJMCStatus()来标记用户代码。调试器可以使用他们想要的任何启发式方法来决定什么是用户代码,什么不是用户代码。因此,尽管DebuggerNonUserCode属性是在mscorlib中定义的,但CLR并不关注它。该属性专门用于为调试器提供方便的原始半标准协议,以将特定函数标记为非用户代码。调试器可以使用其他启发式方法,例如:

-来自项目系统/IDE的输入
-方法名(例如,使所有ToString()或属性getters为非用户代码)。
-其他自定义属性。例如,一个自定义属性可以使用一个字符串参数,调试器可以使用它来创建一组JMC方法。
-符号的存在。(通常不带符号的模块应视为非用户代码)。
-源代码级信息(因为调试器可以访问源代码)。

JMC状态可以在运行时切换。因此,调试器可以构建奇特的逻辑,在运行时切换函数组。这还允许调试器延迟设置JMC状态,直到用户第一次在该方法中停止。在ICorDebug级别,JMC与断点正交。断点将在非用户代码中命中,但调试器可以选择继续调试对象而不通知用户。(这就是VS的作用)。

原文地址:https://www.cnblogs.com/yilang/p/13905718.html