引言

大多数人知道 PDB 文件是用来帮助我们 debug 的,但也仅此而已。

本文主要介绍当你遇到 PDB 文件时(windows 开发中),你必须要知道的内容。

重要的事情说三遍

PDB 文件和源代码一样重要!!!

PDB 文件和源代码一样重要!!!

PDB 文件和源代码一样重要!!!

开始之前

首先定义两个概念:

  • 本地编译:在你本机开发环境中的编译。
  • 官方编译:在编译服务器上的编译。

这两种编译的区分很重要,因为调试本地编译往往很简单,但是问题往往出现在官方编译中。

官方编译至少需要有一个地方(Symbol Server)来存放编译出来的二进制及 PDB 文件。这样当某个版本发现任何问题,我们可以获取到对应的 PDB 文件进行调试。

没有匹配的 PDB 文件,调试器几乎不可能完成调试任务,或者你将付出高昂的代价才能解决问题。

更多关于 Symbol Server 的内容,参考 https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/june/bugslayer-symbols-and-crash-dumps

Visual Studio 和 WinDBG 知道如何访问 Symbol Server 并且如果二进制文件来自官方编译,那么调试器能够自动加载匹配的 PDF 文件。

在将 PDB 文件放在 Symbol Server 上之前,还需要做一件事:在官方编译出来的 PDB 文件上,通过 Source Server 工具,进行源文件索引(source indexing)。索引过程会嵌入版本控制命令用于拉取当前版本编译的源文件。这样,当你调试当前版本时,你就不用担心找不到版本的源文件。

更多关于 Source Server 的内容,参考 https://docs.microsoft.com/en-us/archive/msdn-magazine/2006/august/source-server-helps-you-kill-bugs-dead-in-visual-studio-2005

什么是 PDB 文件?

现在,我们可以开始介绍什么是 PDB 文件,以及调试器是如何查找 PDB 文件了。

实际的 PDB 文件格式是加密的,但是微软提供了 API,为调试器提供 PDB 文件的数据。

非托管的 C++ PDB 文件包含了以下信息:

  • 公有函数,私有函数和静态函数的地址
  • 全局变量的名称和地址
  • 参数和局部变量的名称以及栈上的偏移量
  • 类,结构体以及数据定义的类型信息
  • FPO(Frame Pointer Omission) 数据
  • 源文件的文件名及行信息。

而 .NET 的 PDB 文件只包含了两项内容:

  • 源文件的文件名及行信息
  • 局部变量的名称

其他所有信息已经存放在 .NET 元数据中,所以没有必要再 PDB 文件中冗余。

PDB 文件的加载

当模块被加载到进程的地址空间后,调试器会使用两种信息找到匹配的 PDB 文件。首先,当然是文件名。如果你加载 ZZZ.DLL,那么调试器就会查找 ZZZ.PDB。

更重要的是,调试器如何知道这就是匹配的 PDB 文件?这是通过比对内嵌于 PDB 文件和二进制文件中的 GUID 来确认的。

负责将 GUID 嵌入二进制和 PDB 文件的是编译器(.NET)或者链接器(C++)。想想,历史编译的版本如果没有保存 PDB 文件,你还能调试吗?答案是否定的,哪怕你没有修改源文件!你可能会想是否可以修改 PDB 文件的 GUID?很遗憾,答案也是否定的。

你可以查看二进制文件中的 GUID。使用 Visual Studio -> DUMPBIN 的命令行工具,你可以列出所有的 PE(Portable Executable) 文件内容。可以在 Visual Studio 的命令行工具中调用 DUMPBIN。

更多关于 DUMPBIN 的内容参考:https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/february/inside-windows-win32-portable-executable-file-format-in-detailhttps://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2

DUMPBIN 有很多命令行指令,其中显示 GUID 的指令是 /HEADERS。在输出内容中,对我们来说重要的是 Debug Directories 部分的内容:

Debug Directories

        Time Type        Size      RVA  Pointer
-------- ------- -------- -------- --------
6045C20E cv 60 00541AC8 5408C8 Format: RSDS, {DC80D058-127B-4379-B859-3F9F6978A4DB}, 1, C:ZZZ.pdb

知道了调试器如何确定匹配的 PDB 文件,下一步我们讨论调试器从哪里查找 PDB 文件。首先,调试器会在加载二进制文件的目录查找对应的 PDB 文件,如果没有找到,那么就查找PE 文件中 Debug Directories 内容里硬编码的 PDB 文件路径,在上面的输出示例中是 "C:ZZZ.pdb"(.NET 应用编译工具 MSBUILD会将 PDB 文件编译到 OBJ<Debug/Relase/...> 目录下,如果编译成功,再拷贝到 DEBUG 或者 RELEASE 目录)。如果在上述两个位置都没有找到,但是建立了 Symbol Server,那么调试器会在 Symbol Server 的缓存目录里继续查找。这种查找顺序也保证了本地编译和官方编译不会有冲突。

在 Visual Studio 中调试的时候,你可以在窗口 Modules 中的列 Symbol File 里看到 PDB 文件的位置。

对大多数应用来讲这种加载方式都没有问题。但是对于需要将程序集放入 GAC(Global Assembly Cache)的 .NET 应用,PDB 的加载就会变得有趣了。对于本地编译,调试器会在编译目录找到 PDB 文件,所以没什么问题。问题来源于当你想要在其他机器上调试本地编译版本。

在其他机器上调式,很多人会用 Gacutil.exe 将程序集放入 GAC,然后打开命令行在 "C:WINDOWSASSEMBLY" 下查找程序集的物理位置。但是基于 Any CPU 编译的程序集实际上会放入类似 "C:WindowsassemblyGAC_MSILExample1.0.0.0__682bc775ff82796a" 的路径。

上述路径中,Example 是程序集名称,1.0.0.0 是版本号,682bc775ff82796a 是公有秘钥令牌值(public key token value)。当你推断出这个路径后,你可以将 PDB 文件拷入这个目录然后调试器会加载它。

PDB 文件的内容

对于官方编译,因为有源文件索引工具,所以 PDB 文件中会存储版本控制命令,用于将源文件放入你配置的源文件缓存池。对于本地编译,PDB 文件中存储的是二进制文件对应的源文件的完整路径。换句话说,如果你使用 C:FOO 中的源文件 MYCODE.CPP,那么 PDB 文件中存储的就是 C:FOOMYCODE.CPP。

理论上,所有的官方编译会自动立马进行源文件索引,并将内容存储于 Symbol Server,以至于你都不用考虑源文件在哪。然而,有些开发团队在测试及其他环节中会考量编译结果是否满足使用的要求,在此之前,不会对 PDB 文件进行源文件索引。如果你确实需要调试未索引的版本,最好将源代码下载到本地时保证和编译服务器相同的磁盘和目录,否则,你可能会在调试时遇到麻烦。尽管 Visual Studio 调试器和 WinDBG 有配置源文件搜索路径的选项,但要配置正确并不容易。

引用

https://www.wintellect.com/pdb-files-what-every-developer-must-know/

关于 PDB 文件你需要知道什么?的更多相关文章

  1. Microsoft Visual Studio PDB文件相关事宜

    Microsoft Visual Studio PDB:调试的符号文件,程序数据库 (PDB) 文件保存着调试和项目状态信息,使用这些信息可以对程序的调试配置: 当以 /ZI 或 /Zi(用于 C/C ...

  2. Visual Studio 不生成.vshost.exe和.pdb文件的方法【转】

    Visual Studio 不生成.vshost.exe和.pdb文件的方法[转] 使用Visual Studio编译工程时,默认设置下,即使选择了「Release」时也会生成扩展名为「.vshost ...

  3. windbg不识别pdb文件符号

    一开始配置完毕后 输入reload  但不识别 输入reload -f 还是不识别 输入reload -f 模块名 继续不识别 !sym noisy 查看 输入reload 发现有了一堆的查找路径 把 ...

  4. Visual Studio无法查找或打开 PDB 文件解决办法

    Visual Studio无法查找或打开 PDB 文件解决办法 用VS调试程序时,有时会在VS底部的“输出”框中提示“无法查找或打开 PDB 文件”.这该怎么解决呢? 下面,我们以VS2013为例,来 ...

  5. .pdb文件的使用方法

    1.Demo1:用DLL_01生成my.dll.my.pdb.my.lib文件. 2.Demo2:在DLL_01_APP_02中使用DLL_01的dll. 步骤: 1.vs2008打开DLL_01_A ...

  6. 2016-07-07: 重新编译时vc90.pdb不是创建此预编译头时使用的pdb文件

    使用VS2008在一个解决方案中包含多个项目时,当设置多个项目的中间目录为同一个目录时,在增量编译时出现"重新编译时vc90.pdb不是创建此预编译头时使用的pdb文件,请重新创建预编译头问 ...

  7. VS2013 编译程序时提示 无法查找或打开 PDB 文件

    "Draw.exe"(Win32):  已加载"C:\Users\YC\Documents\Visual Studio 2013\Projects\Draw\Debug\ ...

  8. PDB文件:每个开发人员都必须知道的

    PDB Files: What Every Developer Must Knowhttp://www.wintellect.com/CS/blogs/jrobbins/archive/2009/05 ...

  9. Visual Studio 不生成.vshost.exe和.pdb文件的方法

    使用Visual Studio编译工程时,默认设置下,即使选择了「Release」时也会生成扩展名为「.vshost.exe」和「.pdb」的文件. 一.先解释一下各个文件的作用: .pdb文件: 程 ...

  10. 解决“C:\Windows\System32\ntdll.dll”。无法查找或打开 PDB 文件问题

    这些提示的问题完全没有必要去理会,因为一般情况下你点击本地windows调试,会报出这样问题很正常. 网上一些介绍什么要去选项卡栏勾选window连接器什么鬼,不建议用该方式,一旦你勾选那个方式虽然不 ...

随机推荐

  1. MySQL 事务日志

    重做日志(Redo log) 重做日志(Redo log),也叫做前滚日志,存放在如下位置,轮询使用,记录着内存中数据页的变化,在事务 ACID 过程中,主要实现的是 D(Durability)的作用 ...

  2. codeforces 920E(非原创)

    E. Connected Components? time limit per test 2 seconds memory limit per test 256 megabytes input sta ...

  3. vue 二级子路由跳转不了 bug

    vue 二级子路由跳转不了 bug @click.prevent 阻止原生事件的冒泡 <li class="tools-hover-box-list-item" v-for= ...

  4. 如何在手机上实现 H5 页面全屏显示

    如何在手机上实现 H5 页面全屏显示 fullscreen 隐藏头部地址栏 隐藏底部导航栏 refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才 ...

  5. 你所不知道的 JS: null , undefined, NaN, true==1=="1",false==0=="",null== undefined

    1 1 1 === 全相等(全部相等) ==  值相等(部分相等) demo: var x=0; undefined var y=false; undefined if(x===y){ console ...

  6. c++ 动态解析PE导出表

    测试环境是x86 main #include <iostream> #include <Windows.h> #include <TlHelp32.h> #incl ...

  7. Baccarat中挖矿、兑换和做市的三角关系是什么?

    NGK在这波DeFi潮中,推出了Baccarat,为用户带来了流动性挖矿收益,今天笔者就讲一讲Baccarat中挖矿.兑换和做市的关系. 兑换和做市是什么关系呢?众所周知,换币者,是用一种货币去换另一 ...

  8. 适合Linux嵌入式项目的代码构建与依赖管理工具——cazel

    前言 我们知道,现在有很多流行的优秀代码构建工具,如CMake.jetkins.bazel等.这些不同的构建工具在其应用的领域起到了举足轻重的作用. 但是,如果仔细研究就会发现,在嵌入式领域,构建工具 ...

  9. Python爬虫_qq音乐示例代码

    import requests url = 'https://c.y.qq.com/soso/fcgi-bin/client_search_cp' for x in range(5): headers ...

  10. Scrapy项目_阳光热线问政平台

    目的: 爬取阳光热线问政平台问题中每个帖子的标题.详情URL.详情内容.图片以及发布时间 步骤: 1.创建爬虫项目 1 scrapy startproject yangguang 2 cd yangg ...