【C++】了解PDB

2024-09-05 00:32
文章标签 c++ 了解 pdb

本文主要是介绍【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.StackTraceif(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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1137464

相关文章

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

c++中的set容器介绍及操作大全

《c++中的set容器介绍及操作大全》:本文主要介绍c++中的set容器介绍及操作大全,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录​​一、核心特性​​️ ​​二、基本操作​​​​1. 初始化与赋值​​​​2. 增删查操作​​​​3. 遍历方

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决

C++11委托构造函数和继承构造函数的实现

《C++11委托构造函数和继承构造函数的实现》C++引入了委托构造函数和继承构造函数这两个重要的特性,本文主要介绍了C++11委托构造函数和继承构造函数的实现,具有一定的参考价值,感兴趣的可以了解一下... 目录引言一、委托构造函数1.1 委托构造函数的定义与作用1.2 委托构造函数的语法1.3 委托构造函

C++11作用域枚举(Scoped Enums)的实现示例

《C++11作用域枚举(ScopedEnums)的实现示例》枚举类型是一种非常实用的工具,C++11标准引入了作用域枚举,也称为强类型枚举,本文主要介绍了C++11作用域枚举(ScopedEnums... 目录一、引言二、传统枚举类型的局限性2.1 命名空间污染2.2 整型提升问题2.3 类型转换问题三、C