【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++ move 的作用详解及陷阱最佳实践

《C++move的作用详解及陷阱最佳实践》文章详细介绍了C++中的`std::move`函数的作用,包括为什么需要它、它的本质、典型使用场景、以及一些常见陷阱和最佳实践,感兴趣的朋友跟随小编一起看... 目录C++ move 的作用详解一、一句话总结二、为什么需要 move?C++98/03 的痛点⚡C++

详解C++ 存储二进制数据容器的几种方法

《详解C++存储二进制数据容器的几种方法》本文主要介绍了详解C++存储二进制数据容器,包括std::vector、std::array、std::string、std::bitset和std::ve... 目录1.std::vector<uint8_t>(最常用)特点:适用场景:示例:2.std::arra

C++构造函数中explicit详解

《C++构造函数中explicit详解》explicit关键字用于修饰单参数构造函数或可以看作单参数的构造函数,阻止编译器进行隐式类型转换或拷贝初始化,本文就来介绍explicit的使用,感兴趣的可以... 目录1. 什么是explicit2. 隐式转换的问题3.explicit的使用示例基本用法多参数构造

C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解

《C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解》:本文主要介绍C++,C#,Rust,Go,Java,Python,JavaScript性能对比全面... 目录编程语言性能对比、核心优势与最佳使用场景性能对比表格C++C#RustGoJavapythonjav

C++打印 vector的几种方法小结

《C++打印vector的几种方法小结》本文介绍了C++中遍历vector的几种方法,包括使用迭代器、auto关键字、typedef、计数器以及C++11引入的范围基础循环,具有一定的参考价值,感兴... 目录1. 使用迭代器2. 使用 auto (C++11) / typedef / type alias

C++ scoped_ptr 和 unique_ptr对比分析

《C++scoped_ptr和unique_ptr对比分析》本文介绍了C++中的`scoped_ptr`和`unique_ptr`,详细比较了它们的特性、使用场景以及现代C++推荐的使用`uni... 目录1. scoped_ptr基本特性主要特点2. unique_ptr基本用法3. 主要区别对比4. u

C++11中的包装器实战案例

《C++11中的包装器实战案例》本文给大家介绍C++11中的包装器实战案例,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录引言1.std::function1.1.什么是std::function1.2.核心用法1.2.1.包装普通函数1.2.

C++多线程开发环境配置方法

《C++多线程开发环境配置方法》文章详细介绍了如何在Windows上安装MinGW-w64和VSCode,并配置环境变量和编译任务,使用VSCode创建一个C++多线程测试项目,并通过配置tasks.... 目录下载安装 MinGW-w64下载安装VS code创建测试项目配置编译任务创建 tasks.js

C++ 多态性实战之何时使用 virtual 和 override的问题解析

《C++多态性实战之何时使用virtual和override的问题解析》在面向对象编程中,多态是一个核心概念,很多开发者在遇到override编译错误时,不清楚是否需要将基类函数声明为virt... 目录C++ 多态性实战:何时使用 virtual 和 override?引言问题场景判断是否需要多态的三个关

C++简单日志系统实现代码示例

《C++简单日志系统实现代码示例》日志系统是成熟软件中的一个重要组成部分,其记录软件的使用和运行行为,方便事后进行故障分析、数据统计等,:本文主要介绍C++简单日志系统实现的相关资料,文中通过代码... 目录前言Util.hppLevel.hppLogMsg.hppFormat.hppSink.hppBuf