使用“仅我的代码”仅调试用户代码
仅我的代码是一项 Visual Studio 调试功能,可自动单步跳过系统、框架和其他非用户代码调用。 在调用堆栈窗口中,“仅我的代码”将这些调用折叠到 [External Code] 帧中。
在 .NET 和 C++ 项目中,“仅我的代码”的工作方式有所不同。
启用或禁用“仅我的代码”
对于大多数编程语言,默认都启用“仅我的代码”。
- 要在 Visual Studio 中启用或禁用“仅我的代码”,请在“工具”>“选项”(或“调试”>“选项”)>“调试”>“常规”下,选择和取消选择“启用‘仅我的代码’”。
注意
启用‘仅我的代码’是全局设置,会应用于所有语言的全部 Visual Studio 项目。
“仅我的代码”调试
在调试会话期间,模块窗口会显示调试器将哪些代码模块视为“我的代码”(用户代码),以及其符号加载状态。 有关详细信息,请参阅熟悉调试器如何附加到应用。
在“调用堆栈”或“任务”窗口中,“仅我的代码”将非用户代码折叠到标签为 [External Code]
的灰色带批注代码帧 。
提示
必须处于调试会话才能打开模块、调用堆栈、任务或大多数其他调试窗口。 调试时,在调试>窗口下,选择要打开的窗口。
要查看折叠的 [External Code] 帧中的代码,请在调用堆栈或任务窗口中右键单击,然后从上下文菜单中选择显示外部代码。 展开的外部代码行将替换 [External Code] 帧。
注意
显示外部代码是当前用户探查器设置,会应用于用户打开的所有语言的全部项目。
双击调用堆栈窗口中展开的外部代码行,将在源代码中以绿色突出显示调用代码行。 对于 DLL 或其他未找到或未加载的模块,可能会打开“找不到符号或源代码”页面。
从 Visual Studio 2022 版本 17.7 开始,可通过在“调用堆栈”窗口中双击外部代码来自动编译 .NET 代码。 有关详细信息,请参阅在调试时从 .NET 程序集生成源代码。
.NET“仅我的代码”
在 .NET 项目中,“仅我的代码”使用符号 (.pdb) 文件和程序优化来区分用户和非用户代码。 .NET 调试器将优化的二进制文件和非加载的 .pdb 文件视为非用户代码。
还有三个编译器特性会影响 .NET 调试器视为用户代码的内容:
- DebuggerNonUserCodeAttribute 告知调试器应用它的代码不是用户代码。
- DebuggerHiddenAttribute 对调试器隐藏代码,即使“仅我的代码”关闭;
- DebuggerStepThroughAttribute 告知调试器单步跳过而不是单步调试应用它的代码。
.NET 调试器将所有其他代码视为用户代码。
在 .NET 调试期间:
- 对非用户代码执行调试>单步调试(或 F11)会单步跳过该代码,转到下一行用户代码。
- 对非用户代码执行调试>单步跳出(或 Shift+F11)会运行到下一行用户代码。
如果没有更多的用户代码,调试将继续进行直到结束、命中另一个断点或者引发错误。
如果调试器在非用户代码中中断(例如,使用调试>全部中断在非用户代码中暂停),则将显示没有源代码窗口。 然后可以使用调试>单步执行命令来执行下一行用户代码。
如果在非用户代码中出现未经处理的异常,则调试器会在生成异常的用户代码行处中断。
如果针对异常启用了第一机会异常,则在源代码中以绿色突出显示调用用户代码行。 调用堆栈窗口会显示标签为 [External Code] 的带批注帧。
C++“仅我的代码”
从 Visual Studio 2017 版本 15.8 开始,还支持使用“仅我的代码”来单步执行代码。 此功能还要求使用 /JMC(仅我的代码调试)编译器开关。 C++ 项目默认启用此开关。 对于“仅我的代码”中的调用堆栈窗口和调用堆栈支持,无需使用 /JMC 开关。
若要分类为用户代码,调试器必须加载包含用户代码的二进制文件的 PDB(使用 “模块 ”窗口检查加载状态)。
对于调用堆栈行为,例如在调用堆栈窗口中,C++ 中的“仅我的代码”仅将以下函数视为非用户代码:
- 在其符号文件中去除了源信息的函数。
- 符号文件指示没有对应于堆栈帧的源文件的函数。
- %VsInstallDirectory%\Common7\Packages\Debugger\Visualizers 文件夹下 *.natjmc 文件中指定的函数。
对于代码单步执行行为,C++ 中的“仅我的代码”仅将以下函数视为非用户代码:
- 调试器尚未加载相应 PDB 文件的函数。
- %VsInstallDirectory%\Common7\Packages\Debugger\Visualizers 文件夹下 *.natjmc 文件中指定的函数。
注意
为了在“仅我的代码”中支持代码单步执行,必须在 Visual Studio 15.8 预览版 3 或更高版本中使用 MSVC 编译器来编译 C++ 代码,并且必须启用 /JMC 编译器开关(默认启用)。 有关其他详细信息,请参阅自定义 C++ 调用堆栈和代码单步执行行为和此博客文章。 对于使用较旧编译器编译的代码,.natstepfilter 文件是独立于“仅我的代码”设置自定义代码单步执行的唯一方法。 请参阅自定义 C++ 单步执行行为。
在 C++ 调试期间,默认情况下会跳过非用户代码。 在 C++ 调试期间:
- 如果从非用户代码调用单步执行,则对非用户代码执行调试>单步执行(或 F11)会逐过程执行该代码,或运行到下一行用户代码。
- 对非用户代码执行调试>单步跳出(或 Shift+F11)会运行到下一行用户代码(当前堆栈帧外部)。
如果没有更多的用户代码,调试将继续进行直到结束、命中另一个断点或者引发错误。
如果调试器在非用户代码中中断(例如,使用调试>全部中断在非用户代码中暂停),则会继续在非用户代码中单步执行。
如果调试器遇到异常,它会在异常处停止,无论是处于用户还是非用户代码中。 会忽略异常设置对话框中的用户未处理异常选项。
自定义 C++ 调用堆栈和代码单步执行行为
对于 C++ 项目,可以指定调用堆栈窗口视为非用户代码的模块、源文件和函数,方法是在 *.natjmc 文件中进行指定。 如果使用最新的编译器,则此自定义也适用于代码单步执行(请参阅 C++“仅我的代码”)。
- 要为 Visual Studio 计算机的所有用户指定非用户代码,请将 .natjmc 文件添加到 %VsInstallDirectory%\Common7\Packages\Debugger\Visualizers 文件夹。
- 要为单个用户指定非用户代码,请将 .natjmc 文件添加到 %USERPROFILE%\My Documents\<Visual Studio version>\Visualizers 文件夹。
.natjmc 文件是具有以下语法的 XML 文件:
<?xml version="1.0" encoding="utf-8"?>
<NonUserCode xmlns="http://schemas.microsoft.com/vstudio/debugger/jmc/2015">
<!-- Modules -->
<Module Name="ModuleSpec" />
<Module Name="ModuleSpec" Company="CompanyName" />
<!-- Files -->
<File Name="FileSpec"/>
<!-- Functions -->
<Function Name="FunctionSpec" />
<Function Name="FunctionSpec" Module ="ModuleSpec" />
<Function Name="FunctionSpec" Module ="ModuleSpec" ExceptionImplementation="true" />
</NonUserCode>
模块元素属性
特性 | 说明 |
---|---|
Name |
必需。 模块的完整路径。 可以使用 Windows 通配符 ? (零个或一个字符)和 * (零个或多个字符)。 例如,应用于对象的<Module Name="?:\3rdParty\UtilLibs\*" /> 告知调试器中的所有模块都视为 \3rdParty\UtilLibs 外部代码的任何驱动器上。 |
Company |
可选。 发布在可执行文件中嵌入的模块的公司的名称。 可以使用此特性消除模块歧义。 |
文件元素属性
特性 | 说明 |
---|---|
Name |
必需。 要视为外部代码的源文件的完整路径。 可以在指定路径时使用 Windows 通配符 ? 和 * 。 |
函数元素属性
特性 | 说明 |
---|---|
Name |
必需。 要视为外部代码的函数的完全限定的名称。 可以在指定路径时使用 Windows 通配符 ? 和 * 。 |
Module |
可选。 包含函数的模块的名称或完整路径。 可以使用此特性区分具有相同名称的函数。 |
ExceptionImplementation |
设置为 true 时,调用堆栈显示的是引发异常的函数,而不是此函数。 |
独立于“仅我的代码”设置自定义 C++ 单步执行行为
在 C++ 项目中,可以通过在 *.natstepfilter 文件中将函数列为 NoStepInto 来指定要逐过程执行的函数。 *.natstepfilter 文件中列出的函数独立于“仅我的代码”设置。 NoStepInto 函数会告知调试器逐过程执行函数,即使它调用了一些 StepInto 函数或其他用户代码也是如此。 与 .natjmc 中列出的函数不同,调试器将单步执行 NoStepInto 函数中的第一行用户代码。
- 要为所有 Visual Studio 本地用户指定非用户代码,请将 .natstepfilter 文件添加到 %VsInstallDirectory%\Common7\Packages\Debugger\Visualizers 文件夹。
- 要为单个用户指定非用户代码,请将 .natstepfilter 文件添加到 %USERPROFILE%\My Documents\<Visual Studio version>\Visualizers 文件夹。
注意
某些第三方扩展可能会禁用 .natstepfilter 功能。
.natstepfilter 文件是具有以下语法的 XML 文件:
<?xml version="1.0" encoding="utf-8"?>
<StepFilter xmlns="http://schemas.microsoft.com/vstudio/debugger/natstepfilter/2010">
<Function>
<Name>FunctionSpec</Name>
<Action>StepAction</Action>
</Function>
<Function>
<Name>FunctionSpec</Name>
<Module>ModuleSpec</Module>
<Action>StepAction</Action>
</Function>
</StepFilter>
元素 | 说明 |
---|---|
Function |
必需。 将一个或多个函数指定为非用户函数。 |
Name |
必需。 ECMA-262 格式的正则表达式,指定要匹配的完整函数名。 例如:<Name>MyNS::MyClass::.*</Name> 告知调试器将 MyNS::MyClass 中的所有方法都视为非用户代码。 匹配区分大小写。 |
Module |
可选。 ECMA-262 格式的正则表达式,指定包含函数的模块的完整路径。 匹配不区分大小写。 |
Action |
必需。 以下区分大小写的值之一:NoStepInto - 告知调试器单步跳过函数。StepInto - 告知调试器单步调试函数,为匹配的函数重写任何其他 NoStepInto 。 |
有关 .natstepfilter 和 .natjmc 文件的其他信息
从 Visual Studio 2022 版本 17.6 开始,可以将 .natjmc 和 .natstepfilter 文件直接添加到解决方案或项目中。
.natstepfilter 和 .natjmc 文件中的语法错误不会在调试器的“输出”窗口中报告。
与 .natvis 文件不同,.natstepfilter 和 .natjmc 文件不会热重载。 相反,这些文件将在调试会话开始时重新加载。
对于模板函数,在名称中使用
<.*>
或<.*
可能有所帮助。
JavaScript“仅我的代码”
对于 Visual Studio 2022 中的 .esproj 项目,Visual Studio Code 使用 launch.json 文件来配置和自定义调试器。 launch.json 是调试器配置文件。
Visual Studio 仅将调试器附加到用户代码。 对于 .esproj 项目,可以使用 launch.json 中的设置在 Visual Studio skipFiles
中配置用户代码(即“仅我的代码”设置)。 此设置与 VS Code 中的launch.json 设置相同。 有关 skipFiles 的详细信息,请参阅跳过不感兴趣的代码。