一、准备工作
MS引用:在 Visual Studio 调试器(C#、C++、Visual Basic、F#)中指定符号 (.pdb) 和源文件
MS引用:为 C#、ASP.NET 或 Visual Basic 项目 (.NET Framework) 生成符号文件
MS引用:用于控制编译器输出的 C# 编译器选项
1、首先,我们需要一个控制台程序,代码很简单,ConsoleApp1控制台程序在主程序中,调用ClassLibrary1类库中Class1下的Do方法,如下:
2、生成AnyCPU或任意目标平台,代码的目录结构如下:
3、然后在bin/Debug目录下,双击运行控制台程序,按照代码逻辑,程序堆栈会停在“Console.ReadLine();”这一行;
4、(验证正常调试过程.pdb的用处)打开VS,通过“继续但无需代码”进入,选择“调试”-“附加到进程”,输入程序集名称,找到该进程,选择“附加”;
5、附加进程ok后,点击调试的“全部中断”,看到断点停留在了“Console.ReadLine();”这一行:
二、移除.pdb会如何
6、保持程序状态,需要做的是:
①将“..\bin\Debug\ConsoleApp1.pdb”重命名为“ConsoleApp1_Temp.pdb”
②将“..\obj\Debug\ConsoleApp1.pdb”重命名为“ConsoleApp1_Temp.pdb”
(obj下的是源.pdb文件,bin中的是源.pdb文件的副本;同样obj和bin下的exe也是如此;此处可以去了解下编译的过程,是先生成obj下的内容,再将运行时需要的文件拷贝到bin下)
(编译时,.pdb的路径会在被记录在exe程序中,这也是为什么要做②的原因)
7、VS重新进行附加进程,点击“全部中断”,会得到“处于中断模式”的结果,但由于缺少.pdb所以无法定位具堆栈:
三、移除本地代码会如何
7、保持程序状态,恢复在步骤6中所做的,对obj和bin下.pdb的重命名,然后需要做的是:
①将源代码文件“Program.cs”重命名为“Program_Temp.cs”
8、VS重新进行附加进程,点击“全部中断”,会得到“未找到源”的结果,能够在“局部变量”和“调用堆栈”选项卡中看到内容,却没有对应源代码定位:
四、总结
首先,为什么选择附加到进程,而不是在工程中将“活动解决方案平台配置”设置为“Debug”进行启动?——Debug模式每次启动时,obj下的源.pdb会默认生成,并拷贝到bin输出目录中。
其次,上述步骤是在“生产环境下使用.pdb加上源码进行调试”的场景。(有人会说生产环境下不是应该放Release版本吗,也不会有.pdb啊。那就接着往下看,实际是因为默认生成Release时被优化编码导致不能准确命中断点,并且在“生成”-“高级设置”下未选择编译出pdb而已)。
最后,.pdb记录的是调试信息,调试信息需要与源代码对应。实时调试时,需要将.pdb和要调试的单个或多个源码文件拷贝到生产环境目标机器上,选择附加进程后,exe或dll作为模块,pdb作为模块的符号(“调试”-“窗口”-“模块”-右键“exe或dll”模块名称-选择.pdb路径),源代码作为调试源文件(“解决方案资源管理器”-右键“解决方案'Solution1'(0个项目)”-“属性”-“调试源文件”-添加.cs所在文件夹),即可完成命中断点后跳转到所在代码行。
具体操作见引用:PDB是什么文件?PDB文件格式详解
五、拓展——Debug和Release的区别
五-一、正常调试Release
1、右键“ConsoleApp1”工程-“属性”-“生成”-“高级”;
2、在默认情况下,将“配置”设置为Debug和Release时,我们能看到的区别在于“优化编码”这一项,Debug时默认“不选中”即“不优化编码”;Release时默认“选中”即“优化编码”。
3、此时,按上述流程同样对Release下的exe进行调试,会得到提示,并选择“继续调试”:
4、点击“全部中断”,并不会命中当前堆栈“Console.ReadLine();”这一行,且“局部变量”和“调用堆栈”标签页中并无内容。
五-二、取消“优化编码”调试Release
不再贴图,直接放出结果,如同我们调试Debug一样。
五-三、结论
Debug和Release在使用VS调试时,主要区别在于是否进行了“优化编码”(实际上即使配置完全一致,对比16进制仍会返现两者不尽相同,比如生成时间戳、代码段地址)。
而优化导致的结果,个人理解就是:
是否能够通过.pdb和源码,在正常的生产环境中,附加到进程并“准确”命中断点(优化编码后的部分断点也能命中)
六、拓展——AnyCPU(首选32位)/x86/x64/ARM的区别
(以下引用MS原文)
PlatformTarget
指定 CLR 的哪个版本可以运行程序集。
XML复制
<PlatformTarget>anycpu</PlatformTarget>
- anycpu(默认值)将程序集编译成可在任意平台上运行。 您的应用程序将尽可能作为 64 位进程运行;当只有 32 位模式可用时,才会回退到 32 位。
- anycpu32bitpreferred 将程序集编译成可在任意平台上运行。 在同时支持 64 位和 32 位应用程序的系统上,您的应用程序将以32 位模式运行。 只能为面向 .NET Framework 4.5 或更高版本的项目指定此选项。
- ARM 将程序集编译成可以在具有高级 RISC 计算机 (ARM) 处理器的计算机上运行。
- ARM64 编译程序集以在由 64 位 CLR 在具有支持 A64 指令集的高级 RISC 计算机 (ARM) 处理器的计算机上运行。
- x64 将程序集编译成可由支持 AMD64 或 EM64T 指令集的计算机上的 64 位 CLR 运行。
- x86 将程序集编译成可由 32 位、x86 可兼容 CLR 运行。
- Itanium 将程序集编译成可由配有 Itanium 处理器的计算机上的 64 位 CLR 运行。
在 64 位 Windows 操作系统上:
- 用 x86 编译的程序集将在 WOW64 下运行的 32 位 CLR 上执行。
- 用 anycpu 编译的 DLL 将在加载它的进程所在的同一 CLR 上执行。
- 用 anycpu 编译的可执行文件将在 64 位 CLR 上执行。
- 用 anycpu32bitpreferred 编译的可执行文件将在 32 位 CLR 上执行。
anycpu32bitpreferred 设置只对可执行文件 (.EXE) 有效,并且需要 .NET Framework 4.5 或更高版本。