Visual Studio开发工具----调试技巧-符号文件(.pdb)
3.8.1 概述
程序数据库(.pdb)文件(也称为符号文件)将项目源代码中的标识符和语句映射为已编译应用程序中的相应标识符和指令。
当您使用标准的Debug构建配置从Visual Studio IDE构建项目时,编译器会创建适当的符号文件。
pdb文件包含允许对应用程序的调试配置进行增量链接的调试和项目状态信息。
Visual Studio调试器在调试时使用.pdb文件来确定两个关键信息:
- 在Visual Studio IDE中显示的源文件名和行号。
- 在应用程序中要停下来的地方。
符号文件还显示源文件的位置,以及显示源文件位置的服务器(可选)。
调试器只加载与应用程序构建时创建的.pdb文件完全匹配的.pdb文件(即原始的.pdb文件或副本)。
3.8.2 为什么要求符号文件“完全”匹配生成它们的二进制文件
以下是MSDN的一篇回答:
“最近,我的一位同事丢失了他的一个二进制文件的符号文件。由于他需要调试该二进制文件,因此将这些符号取回至关重要,因为没有它们的调试几乎是不可能的。他决定尝试使用先前版本中的符号文件,其源代码完全匹配。令他非常恼火的是,他发现Visual Studio拒绝加载它们。他问我:
“为什么Visual Studio不能加载使用完全相同源代码的二进制符号文件?”
在回答这个问题之前,我将进一步讨论这个问题:
“如果我连续两次构建一个组件,中间不更改源代码,那么我肯定会得到该组件的两个完全相同的副本,正确吗?”
令人惊讶的是,这个问题的答案是:“不,那不一定是正确的”。最明显的原因是,内部时间戳将是不同的。但是,即使不考虑这些,答案仍然是相同的,代码的实际布局可能是不同的。
原因是,编译器编写者更感兴趣的是生成正确运行的代码和快速生成代码,而不是确保生成的代码在硬盘上的布局是相同的。由于有许多不同的方法和实现来优化代码,一个构建总是有可能比另一个构建有更多的时间来做一些额外的或不同的事情。因此,对于相同的功能,最终的结果可能是一组不同的位。
下面是一个非常简单的例子。假设您正在构建的组件由一个函数和一个变量组成。
最终生成的文件为以下两种情况,这两种情况有区别吗?
或
在功能上,这一点都不重要,但是对于调试器来说,这非常重要。实际上,出错可能会严重影响调试会话。在本例中,如果在运行由build #2构建的组件时,使用来自build #1的符号文件来尝试确定全局变量的值,会发生什么情况?调试器将参考符号文件并返回地址0020所引用的值。不幸的是,全局变量不在组件构建#2中的那个地址。而是一些组成MyFunc()指令流的值。
调试器依赖于了解组件的内部布局。所以,我现在可以回答我同事最初的问题了:
“因为Visual Studio和你的理智都依赖于它”。
PS:在实践中,您不太可能看到编译器/链接器输出的差异。因此,在理论上,使用不匹配的符号是可能的。然而,编译器/链接器的确定性不能保证,我们的调试器不能依赖它。此外,我们不愿承担允许支持某种覆盖的成本。因此,如果你发现你不得不使用一个不匹配的符号文件,你可以使用WinDbg。”
3.8.3 符号文件的位置和加载行为
在远程设备上调试托管代码时,所有符号文件必须位于本地计算机上,或者位于调试器选项中指定的位置。
在Visual Studio IDE中调试项目时,调试器会自动加载位于项目文件夹中的符号文件。
调试器还会在以下位置搜索符号文件:
- 在DLL或可执行文件(.exe)中指定的位置。默认情况下,如果您已经在计算机上构建了DLL或.exe文件,则链接程序会将关联的.pdb文件的完整路径和文件名放在DLL或.exe文件中。调试器检查该位置是否存在符号文件。
- 与DLL或.exe文件相同的文件夹.
- 在调试器选项中为符号文件指定的任何位置。
3.8.3.1 配置符号位置和加载选项
在“工具”> “选项”> “调试”> “符号”页面上,您可以:
- 为Microsoft,Windows或第三方组件指定并选择搜索路径和符号服务器。
- 指定您要或不希望调试器自动加载符号的模块。