C++了解PDB

Posted Treysure

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++了解PDB相关的知识,希望对你有一定的参考价值。

What is the use of PDB file?

Introduction

本文帮助那些处于初级或中级水平,对PDBs格式文件的重要性和为什么需要PDBs格式文件了解不多的开发人员。

What is PDB

PDB是程序数据库文件的缩写。 PDB文件通常是在编译期间从源文件创建的。它将所有符号的列表存储在一个模块中,包含它们的地址以及可能的文件名和声明该符号的行。(来自维基)

Why PDB as a Separate File?

这些符号可以很好地嵌入二进制中,但这又会导致文件大小显著增加(有时是几兆字节)。为了避免这种额外的大小,现代编译器和早期的大型机调试系统将符号信息输出到一个单独的文件中;对于微软编译器来说,这个文件被称为PDB文件。

What Does the PDB File Contain

以下是PDB文件存储的一些重要信息:

    

    1. Local variable name - 局部变量名-为了证明PDB包含本地变量名,我们将使用反射器来反汇编程序集,其PDB存在于与程序集相同的文件夹中。反射器有一个选项,称为“显示PDB符号”,如截图所示,当选中时,也为该程序集加载相应的PDB。当您检查选项时,您可以看到反编译代码具有与实际代码相同的变量名,但是在没有PDB的情况下,或者当该选项未选中时,您的反编译代码中的本地变量将被替换为字符串变量的“STR”和“Num”。十进制等。

    2. 源文件名
    3. 信源的行号
    4. 源索引(稍后说明)

为了显示PDB包含源文件名和源代码行数(点2和3),首先运行以下控制台应用程序,其中PDB存在于同一文件夹中,第二步通过删除PDB文件。



namespace UnderstandingPDBs

    class Program
    
        static void Main(string[] args)
        
            try
            
                int sum = Add(5, 10);
                decimal value = Divide(10, 0);
            
 
            catch
            
            
        
 
        private static int Add(int i, int j)
        
            return i + j;
        
 
        private static decimal Divide(int i, int j)
        
            try
            
                return i / j;
            
            catch (Exception ex)
            
                LogError(ex);
                throw ex;
            
        
 
        private static void LogError(Exception ex)
        
            using (var txtWriter = new StreamWriter(@"dump.txt",true))
            
                string error = "Exception:" + ex.Message + 
                Environment.NewLine + "StackTrace:" + ex.StackTrace
                if(ex.InnerException!=null)
                    error=error+"INNER EXCEPTION:"+ex.InnerException;
                txtWriter.WriteLine(error);
            
        
    
  

With PDB, this is the exception thrown by the application:

Exception:Attempted to divide by zero.
StackTrace: at UnderstandingPDBs.Program.Divide(Int32 i, Int32 j) in 
C:\\Users\\Rishi\\Documents\\Visual Studio 2010\\Projects\\UnderstandingPDBs\\Program.cs:line 33

Without PDB, exception shows the following message:

Exception:Attempted to divide by zero.
StackTrace: at UnderstandingPDBs.Program.Divide(Int32 i, Int32 j)
---------

Clearly, the one with PDB shows line number and file name of the class where exception is thrown.

How PDB is Loaded by Debugger?

VisualStudio调试器期望PDB文件处于与DLL或EXE相同的文件夹之下。为程序集生成的PDB文件对于每个生成都是唯一的,这意味着即使在没有代码更改的情况下,也不能使用以前生成的PDB与在任何其他构建中创建的程序集一起使用。调试器通过将PDB中的特定GUID与二进制的GUID进行比较来找出PDB是否为二进制。这个GUID在编译过程中嵌入二进制和PDB,它们紧密地将PDB与其二进制连接起来。

Different Build Settings in Visual Studio

Visual Studio has 3 different Build Options which control the debug symbols generation:

  1. none: PDB files will not be generated.PDB文件将不会生成。
  2. pdb-only: The debug symbols will be only in PDB files and not in Binary仅PDB:调试符号只在PDB文件中,而不是二进制文件。
  3. Full: Along with symbols in PDB binary will also contain some debug symbols完整的:连同PDB二进制中的符号也将包含一些调试符号。

Full is the default option set in Visual Studio.

According to MSDN:

"If you use /debug:full, be aware that there is some impact on the speed and size of JIT optimized code and a small impact on code quality with /debug:full. We recommend /debug:pdbonly or no PDB for generating release code."“如果您使用/Debug:FULL,请注意,JIT优化代码的速度和大小会有一些影响,对代码质量的影响也会很小。我们建议/调试:PDBUN或无PDB生成发布代码。


Should We Deploy PDBs Along with Binaries?


如果交付物的大小不是一个问题,那么PDB与其他二进制文件一起部署是很好的,因为它有助于提供更多关于异常的信息,正如我们在上面的例子中看到的。这些PDBS在某些断断续续发生的某些崩溃中是非常有用的,对于某些用户来说,如果没有PDB,将使生命变得困难。
并不是必须部署PDB和二进制部署来获得关于异常的额外信息。同样可以使用符号服务器和源索引来实现,我将在下面的主题中进行讨论。

Security Risk with PDB?

任何有权访问DLL/EXE的人都可以很容易地做逆向工程,使用诸如反射器之类的工具生成或不使用PDB的源代码。因此,在这种情况下,不提供PDB将没有多大帮助。
如果部署了PDB并且用户不能访问二进制文件,那么向他们展示堆栈跟踪信息并让他们知道应用程序的内部结构不是一个好主意。

Symbol Server

符号服务器用于存储调试器已知的PDB文件,并可用于查找更多描述性的调用堆栈信息。

我们可以使用SyStur.EXE设置自己的符号服务器,它允许调试器找到与所讨论的二进制相关联的实际PDB。SytSuff.EXE包含在Window包的调试工具中。
微软还维护符号服务器,我们可以通过从微软的符号服务器加载PDB来使用。

How and Why to load Microsoft Symbol Store

当您在调试点和打开模块窗口停止执行时(如下所示),您将发现所有的DLL(外部或内部)加载到该断点为止,但是默认情况下的符号状态将显示“除了PDB之外无法找到或打开PDB文件”。这些是微软BCL二进制文件,因为我们的调试器找不到相关的PDB,所以这些二进制二进制文件没有加载。


若要加载这些符号,请转到“调试”>符号并检查微软符号服务器,并将此目录中的缓存符号作为任何共享文件夹,以便其可由所有开发人员使用。
由于这些二进制文件在您的应用程序之外,还需要在“调试”>“常规”菜单中取消“启用仅我的代码”。


那么这怎么可能有用呢?


可以在代码中放置断点,并在加载和不加载符号的情况下查看调用堆栈。
下图显示了没有加载符号的调用堆栈,它只显示了我的方法和BCL的方法(只是外部代码)。
在下面的屏幕截图中,你可以看到我已经加载了符号,现在符号的状态显示了“符号加载”。

与符号服务器一样,还有一个叫做源服务器的东西,用来检索用于构建任何特定应用程序的源文件的精确版本。二进制文件可以在构建时被索引,并且这些信息存储在PDB文件中,这有助于源服务器找到确切的源文件。

Points of Interest

PDB文件是微软专有文件,最少是文档化的。









以上是关于C++了解PDB的主要内容,如果未能解决你的问题,请参考以下文章

C++ 调试问题 - pdb 文件

Visual C++ PDB 文件的合理大小是多少?

未从同一目录加载 C++ PDB 符号

在 VS C++ 中使用 dll、pdb 文件和源代码进行调试

.pdb格式文件打开方式

在 Visual Studio C++ 2010 中找不到或打开 PDB 文件