生成.pdb文件的目的是什么?——发布相关

312
为什么在发布版编译时Visual Studio 2005会生成 .pdb 文件?我不会调试发布版本,所以为什么要生成它们?

23
为什么要在发布中生成PDB文件?这是为了当程序在用户环境崩溃时,你可以利用PDB文件获取调试信息。另一个价值在于,即使原作者不愿意调试,客户也可以使用PDB文件进行调试。 - Ian Boyd
1
@IanBoyd:那个评论的第二句话意味着你部署了PDB。在绝大多数情况下,这是不可取的。 - IInspectable
3
@IInspectable 或者是理想的 - Ian Boyd
@IanBoyd:绝大多数情况下不包括操作系统部署。此外,那些PDB文件不包含私有符号,当你生成PDB文件时,默认会包含它们。 - IInspectable
另一方面,发布PBD确实是可取的。理想情况下,每个人都会编写编译为IL的代码,这样我们就可以自己获取符号信息。但本地代码编译器仍然没有简单的方法来支持现场调试。 - Ian Boyd
1
@IanBoyd:“但本地代码编译器仍然没有简单的方法在现场支持调试。” - 顺便说一下,他们有了。例如,在使用任务管理器时,在WER的一部分或在任意时间拉取minidump,并让供应商进行分析。这不需要运输PDB。请参见崩溃转储分析获取信息。 - IInspectable
9个回答

474

因为没有PDB文件,除了地址级别调试外,不可能用任何其他方式来调试“发布”版本。 优化会对您的代码造成很大影响,如果出现问题(比如抛出异常),就很难找到罪魁祸首。甚至设置断点也非常困难,因为源代码行无法与生成的汇编代码一一匹配(甚至顺序都不同)。PDB文件有助于您和调试器,使事后调试变得更加容易。

您提到如果软件准备好发布,应该在此之前完成所有调试工作。虽然这是正确的,但还有几个重要的要点需要记住:

  1. 在发布之前,您应该使用“发布”版本测试和调试应用程序(同时保留优化,因为它们在“调试”配置下默认禁用)。这是因为开启优化有时会导致出现细微错误,而这些错误在其他情况下可能无法发现。进行此调试时,您需要PDB符号。

  2. 客户经常报告一些只在“理想”条件下出现的边缘案例和错误。这些东西几乎无法在实验室中复制,因为它们依赖于用户机器的某些奇怪配置。如果他们非常乐于助人,他们会报告抛出的异常并提供堆栈跟踪。或者他们甚至会借给您他们的机器来远程调试您的软件。在这两种情况下,您需要PDB文件来帮助您。

  3. 在启用优化的“发布”版本上,应始终进行分析。再次强调,PDB文件非常方便,因为它们允许将正在分析的汇编指令映射回您实际编写的源代码。

编译后无法再生成 PDB 文件*如果你在构建时没有创建它们,那么就失去了机会。 创建它们不会有任何影响。 如果您不想分发它们,可以从二进制文件中简单地省略它们。 但是,如果以后决定需要它们,那就太晚了。最好始终生成并存档副本,以防万一。

如果您真的想关闭它们,那是一种选择。 在项目的“属性”窗口中,为要更改的任何配置设置“调试信息”选项为“无”。

请注意,“Debug”和“Release”配置默认使用不同的设置来生成调试信息。 您将希望保留此设置。“调试信息”选项在 Debug 构建中设置为“full”,这意味着除了 PDB 文件外,还将嵌入调试符号信息到程序集中。 您还将获得支持编辑并继续等很酷的功能的符号。 在 Release 模式下,选择了“pdb-only”选项,这意味着仅包含 PDB 文件,而不会影响程序集的内容。 因此,在您的/bin目录中是否存在 PDB 文件并不像简单的有或没有那么简单。 但是假设您使用“pdb-only”选项,则 PDB 文件的存在不会以任何方式影响代码的运行时性能。

* 正如Marc Sherman 在评论中指出,只要源代码没有改变(或者您可以从版本控制系统中检索到原始代码),您就可以重新构建它并生成匹配的 PDB 文件。至少通常情况下是这样的。这在大多数情况下运行良好,但是编译器不能保证每次编译相同的代码时都会生成相同的二进制文件,因此可能存在微妙的差异。更糟糕的是,如果您同时对工具链进行了任何升级(比如为 Visual Studio 应用了服务包),则 PDB 就更不可能匹配了。为了确保可靠地生成事后的 PDB 文件,您需要归档不仅版本控制系统中的源代码,还要归档整个构建工具链的二进制文件,以确保您能够精确地重新创建构建环境的配置。不言而喻,简单地创建和归档 PDB 文件要容易得多。


23
如果你的源代码没有更改,那么编译后就无法生成PDB文件。默认情况下,windbg不会加载这个PDB文件,但是你可以通过指定 /i 选项来强制加载它,像这样 .reload /i foo.dll。这将加载 foo.pdb 文件,即使它是在发布 foo.dll 后创建的。 - Marc Sherman
1
我注意到每次新的编译都有不同的哈希摘要,因此即使在相同的环境中,每个构建也会有轻微的差异。PDB的地址是否可能随着变化而不变,因此需要保留该构建的PDB?我只是提出这个想法,因为我真的不理解PDB的工作原理或为什么哈希在构建之间发生变化。 - thebunnyrules
3
在注释中,我链接了一篇文章,解释了“C#编译器从设计上永远不会生成相同的二进制文件。每次运行它时,C#编译器都会在每个程序集中嵌入一个全新生成的GUID,以确保没有两个程序集完全相同。”这就解释了为什么它具有不同的哈希值,因此具有不同的PDB文件。虽然可以使用十六进制编辑器修复,但它并不用户友好,而且超出了本回答的范围。 - Cody Gray
7
Roslyn 中有一个新功能叫做“确定性构建”(deterministic builds)。使用 "/deterministic" 标志会使编译器在给定相同输入时,以字节为单位精确地发出相同的 EXE / DLL。这意味着项目如果最初是使用此标志编译的,则只要要编译的代码相同,就可以重新编译为完全相同的二进制文件。您可以在 Roslyn 中的确定性构建 找到更详细的解释。 - K Smith
我从未发现发布版本的PDB文件有任何用处。 - Mike Cheel

105

PDB文件可以用于生成ReleaseDebug版本。 在VS2010中设置如下(在VS2005中也应该类似):

 

项目→属性→生成→高级→调试信息

只需将其更改为即可。


2
但是你为什么要这样做呢?如果你的软件已经准备好发布了,那么你应该在那时候完成所有的调试工作。 - m.edmondson
5
因为您可以调试生产问题。我们曾经不得不这样做。 - Aliostad
29
使用 PDB 文件来生成生产代码的优点是,当 .NET 抛出异常时,它将使用这些文件。它生成带有文件名和行号的堆栈跟踪,这通常非常方便! - Steven
7
@m.edmondson:是的,没错。你仍然会被告知抛出的异常是什么(比如 FileNotFoundException),但你将无法看到堆栈跟踪。这使得很难确定是哪一行代码导致了异常的抛出。 - Cody Gray
2
@Steven: 非常感谢你。我为为什么我的测试服务器在堆栈跟踪中不包括行号,而相同的代码在开发环境中会产生具有行号和文件名的异常而苦恼了几天!原来如此! - Punit Vora
显示剩余5条评论

7

如果没有.pdb文件,几乎无法在生产代码中进行调试;您必须依赖其他可能耗时费力的工具。我知道您可以使用跟踪或者windbg等工具,但这实际上取决于您想要实现什么目标。在某些情况下,您只需要通过生产数据来观察特定行为,而不会出现错误或异常,这就是.pdb文件派上用场的地方。如果没有它们,运行调试器将是不可能的。


7

你为什么那么肯定你不会调试发布版本的呢?有时候(希望很少但确实会发生),你可能会收到一个客户的缺陷报告,而这个缺陷在调试版本中无法重现,可能由于不同的时间、微小的不同行为或其他原因。如果这个问题在发布版本中可重现,你将很高兴拥有匹配的pdb。


5
使用RDP、Webex等方式访问远程计算机,并在其中安装windbg。设置符号路径,就搞定了! - Marc Sherman
一个更详细指南的链接会更有帮助。这个一行式的操作可能会让人们(包括我自己)走错方向。大多数.NET开发者可能对Windbg一无所知。 - marknuzz
1
@m.edmondson - Visual Studio的某些版本具有执行远程调试的功能。您可以从调试菜单中在远程计算机上“附加到进程”。 - Matthew
远程调试生产应用程序实例是一个好主意吗?这样做会不会破坏线程的并行执行,并在调试时强制它们串行运行? - Kaveh Hadjari

4

此外,您可以利用崩溃转储来调试软件。客户将其发送给您,然后您可以使用它来识别源代码的确切版本 - 如果设置正确,Visual Studio甚至会使用崩溃转储提取正确的调试符号(和源代码)。请参阅微软关于符号存储的文档


2

1
“在发布或部署时不需要此文件”,除非某人在该发布版本中遇到崩溃,并且您从他们那里收到的崩溃报告不包含可用的堆栈跟踪...然后您会希望您最好还是将其包含进去。 - Nyerguds
不是这样的。如果没有.pdb文件,你会得到完整的堆栈跟踪,但没有源文件名称。在收到崩溃报告后,您可以在公司内部恢复它。如果您关心自己的知识产权并混淆源代码,您必须保存.pdb文件,但不要部署它们。 - TOP KEK

1
在一个多项目的解决方案中,通常希望有一个配置不生成任何PDB或XML文件。不必将每个项目的“调试信息”属性更改为“无”,而是添加一个仅在特定配置下运行的后期构建事件会更加有效率。
不幸的是,Visual Studio不允许您为不同的配置指定不同的后期构建事件。因此,我决定手动编辑启动项目的csproj文件,并添加以下内容(而不是任何现有的PostBuildEvent标记):
  <PropertyGroup Condition="'$(Configuration)' == 'Publish'">
    <PostBuildEvent>
        del *.pdb
        del *.xml
    </PostBuildEvent>
  </PropertyGroup>

很遗憾,这会使发布后事件文本框为空,并且在其中放置任何内容可能会产生不可预测的结果。

5
这将删除所有*.xml文件,请小心操作。 - Mariusz Jamro

1

调试符号(.pdb)和 XML 文档(.xml)文件占总大小的很大一部分,不应该包含在常规部署包中。 但如果需要,应该可以访问它们。

一种可能的方法:在 TFS 构建过程结束时,将它们移动到单独的工件中。


0

实际上,如果没有PDB文件和符号信息,就不可能创建成功的崩溃报告(内存转储文件),微软也无法完全了解问题的原因。

因此,拥有PDB可以改善崩溃报告。


但是如果没有.pdb文件,到底会缺少什么? - TOP KEK
编译后无法生成PDB文件。因此,每个软件版本major.minor[.build[.revision]]都应该保存在Microsoft,以便正确理解发生了什么,但这还不是全部。 - prosti
编译器不能保证每次编译相同的代码时生成完全相同的二进制文件。 - prosti
问题是崩溃报告中会缺少什么,以及如何影响崩溃报告。.NET pdb文件仅包含私有变量名称和源文件名称。其他所有内容(方法名称、签名等)将在元数据信息的堆栈跟踪中。 - TOP KEK
不含 PDB 文件也包含非私有数据:https://github.com/microsoft/microsoft-pdb。 - prosti
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接