热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

无法查找或打开pdb文件。_CrashDump调试:SymbolServer/SourceServer、PDB原理分析

背景UE4引擎时不时要魔改编译。可能大一点的项目是难以避免的吧┓(∀)┏工程C会自动编译,有持续集成平台做统一的编译和分发。这样可以不用每个人都编译引擎和工程C了&#

63d484917573093b381f5b6056274e50.png

背景

  1. UE4引擎时不时要魔改编译。可能大一点的项目是难以避免的吧 ┓( ´∀` )┏
  2. 工程C++会自动编译,有持续集成平台做统一的编译和分发。这样可以不用每个人都编译引擎和工程C++了,代码安全性和开发效率能得到保障;
  3. 每当要调试别人发的Dump,就要满世界找dll,找符号,找代码;

目的

搭建符号服务器,调试分析Crash Dump。

如果你也有跟我一样的痛点,那接下来就来看看怎么解决吧。

接下来,我会先介绍怎么用,最后再说说原理和一些技巧。


搭建符号服务器(如已有请略过)

  1. 在编译机上安装WinDbg,后面需要用到其中的symstoreagestore

Download Debugging Tools for Windows - WinDbg - Windows drivers

注意安装这个版本:(这里才有我们需要的symstoreagestore

82978ac8836c21cf8d75d65d32406af7.png

安装好之后,应该可以在这里找到这两个工具,请根据机器的架构选用合适的版本。

5f61f08c3f18f4a9f5bcbd21f449a456.png

2. 准备一个脚本,用symstore把Binaries和PDB存入符号服务器,比如这样:

@echo off
rem 脚本传入参数(按顺序):
setlocal EnableDelayedExpansion
rem echo BIN_DIR:%1rem SYMSTOREPATH 符号服务器地址
rem PRODUCT 你的产品名
rem VERSION 你产品的版本
rem COMMENT 你想加的注释
rem TIMEOUT_DAYS 清理多少天前的符号
rem SYMSTORE_EXE symstore的文件地址
rem AGESTORE_EXE agestore的文件地址
set SYMSTOREPATH=D:SymbolStore
set PRODUCT="sakura"
set VERSION="0.0.0"
set COMMENT="none"
set TIMEOUT_DAYS=60
set SYMSTORE_EXE="C:Program Files (x86)Windows Kits10Debuggersx64symstore.exe"
set AGESTORE_EXE="C:Program Files (x86)Windows Kits10Debuggersx64agestore.exe" -yset BIN_DIR=%~f1%SYMSTORE_EXE% > nul 2> nul
if [%ERRORLEVEL%] EQU [9009] (echo "symstore.exe 没找到,请包含它所在文件夹到PATH"exit /b 0
)echo ============== 清除旧符号 ==============
echo 清除%TIMEOUT_DAYS%天前的符号
%AGESTORE_EXE% -days=%TIMEOUT_DAYS% %SYMSTOREPATH%
echo.echo =============== 上传符号 ===============
echo ++ 本地上传目录: %BIN_DIR%
echo ++ 符号服务器: %SYMSTOREPATH%
echo.echo ++++++++
echo ++ 上传exe到符号服务器
%SYMSTORE_EXE% add /r /f %BIN_DIR%*.exe /s %SYMSTOREPATH% /t %PRODUCT% /v %VERSION% /c %COMMENT%
echo.echo ++++++++
echo ++ 上传dll到符号服务器
%SYMSTORE_EXE% add /r /f %BIN_DIR%*.dll /s %SYMSTOREPATH% /t %PRODUCT% /v %VERSION% /c %COMMENT%
echo.echo ++++++++
echo ++ 上传pdb到符号服务器
%SYMSTORE_EXE% add /r /f %BIN_DIR%*.pdb /s %SYMSTOREPATH% /t %PRODUCT% /v %VERSION% /c %COMMENT%
echo.echo ++ 上传成功!!!!
echo.

在每次编译完之后,运行。

bat脚本会用agestore自动清理过期的符号。

> upload_symbols.bat ...UE4EpicEngineBinaries
> upload_symbols.bat ...UE4EpicEnginePlugins

3. 设置好符号服务器的文件服务器,可以简单地用Samba协议(Windows共享文件夹)或者用IIS或者Apache弄一个。


在VS中设置符号服务器

以VS2019为例,在 工具->选项->调试->符号 把刚才的服务器地址(确保有访问权限)设置好:

5fb0d35885b5ba615300916d211d7651.gif

小技巧:勾选Load only specified modules,可以显著加速调试载入module的时间。在需要查看符号时,再右键加载所需的module:

b441e27f68871a003b1055272506a01e.png

就能看到符号名字了:

d5dcef8cff146b024b9761fcf1e42748.png

参考官方文档:

https://docs.microsoft.com/en-us/windows/win32/debug/using-symstore


源码匹配

此时很可能因为pdb中的源码路径和你本地的不一致,导致找不到源码,如下图:

72f81f086692e245c198c6f488298bdd.png

此时除了一个个地去找源码路径之外,有两种方法可以让VS自动定位到所需的源码:

  1. 在Solution设置中配置Debug Source Files查找路径。

这种方法简单但有局限,能应付大部分情况。如下图:

01d16932d2aeda88e45b1e853483cc21.png

2. Source Server(推荐)

假如产生这个Dump的Binaries已经很旧了,源码已经被改的天翻地覆,除了在本地通过版本控制系统把代码手动还原回去,这种比较笨的办法之外,还有没有更好的做法呢?
又假如要调试的程序本地并没有全部代码,但想大致看一下问题出在哪(比如在QA机器上出现的Crash想准确分发给对应的工程师),有没有更好的做法呢?

有的,答案是Source Server。

Source Server配置方法

编译时:Source Indexing

概要:(以SVN为例)在编译完成后,上传符号之前,用svnindex.cmd工具将版本控制信息写入PDB。

运行要求:

  1. Debugging Tools for Windows(在上面搭建符号服务器时已安装)
  2. ActiveState Perl(Indexing所需的工具,比如:svn.pm,用Perl运行)
  3. 准备svn.exe,并加入到PATH

在PDB生成出来后,上传符号前,执行下面的操作:

svnindex.cmd /source= /symbols= /debug

27ecb73bb2bd37946e67e390850e4b16.png

此时应该会看到PDB被修改了。

可以用pdbstr.exe检查被写入的内容:

pdbstr.exe -r -p:

-s:srcsrv

此时的PDB将比编译生成的PDB多出一些信息。

a61b92160151333f699550595caf84d7.png

PDB就准备完成了。

注意svnindex.cmd的source路径只能是SVN根目录。

调试时:从版本控制服务器自动下载源码

  1. 准备svn.exe,并加入到PATH;
  2. 在VS中启用Source Server的支持;

3b76e830ef7e536128a51584c464bad9.png

设置完成。

在调试Dump时,会提取PDB中的指令。这可能会带来代码注入风险。比如你调试一个未知来源的程序,自带PDB,这个PDB中是可以嵌入任意代码的。

c50be7a392a15d8550caf0b6732ddaa0.png

VS会弹窗提醒。解决方法可以是:

修改svn.pm文件中的SVN_EXTRACT_CMD,将"cmd /c"删掉,直接执行svn.exe,而不是通过cmd.exe。
修改VS安装目录下的srcsrv.ini,将svn.exe加入[trusted command]:

[trusted commands]
svn.exe

至此,代码就可以自动从版本控制服务器上获取了。(^-^)V

代码会缓存到AppData下,不会自动清理,时间长了请自行手动清理。

7421f78bc7bed51364034b2600288c94.png

参考资料

Source Server

Using a Source Server

Enable source server support

Source Server + Subversion = Easy Assembly Debugging


原理介绍

通过前面的操作,也基本能猜出个大概了。这篇文章写得挺详细的,我就不赘述了,只简单讲讲自己的理解:

搭建自己的符号服务器

PDB:

PDB里面记录了一系列的调试辅助信息,与Build的Binary一一对应。比如:

  • publics and exports
  • global symbols
  • local symbols
  • type data
  • source files
  • line numbers

最关键的,无非就是代码段地址,对应的源码路径、函数签名和行号。这样在调试的时候,就知道当前的代码地址,对应哪个代码的哪个函数的哪一行了。

DLL和PDB中会有相同的GUID,通过GUID在符号服务器上组织目录存放文件,调试时根据GUID来找对应的PDB下载。

Source Server重建索引时(重新执行`svnindex.cmd`)PDB文件会被修改(哪怕对应的Binary一点都不变,PDB中的索引日期也会变),但GUID会保持不变。

更新后的PDB会被symstore重新上传符号服务器(而不是相同跳过),会覆盖掉符号服务器上GUID相同的旧PDB,不会因此导致硬盘空间不足,但需要注意IO和流量问题。

Symbol Files - Win32 apps

Crash Dump:

Dump简单来说就是进程的内存镜像。把这个进程的虚拟内存(代码段、数据段、堆、栈、全局段等等)部分或者全部转储到磁盘文件,以便进行死后调试。

577928c309432c4a48b31d7d7a48cd03.png

https://medium.com/@shoheiyokoyama/understanding-memory-layout-4ef452c2e709

User-Mode Dump Files - Windows drivers

其中,又分为Full Dump和Mini Dump,两者最大的区别在于Heap是否做转储。

因为Heap通常非常大,在UE4下动辄10GB以上。而其他内存段相对小很多,比如线程的Stack一般就是几MB。

所以,在Mini Dump下,因为Heap都被剔除了,所以在调试Dump时,看到的是???内存不可访问,比如UE4的FString内部是一个TArray,其字符串Data就是在Heap上的,所以调试Dump时都不可见。

那问题又来了,这样的Mini Dump有什么用呢?如果是因为资源问题导致的Crash,怎么知道是哪个资源呢?

小技巧:可以用在UE4程序后加命令行参数-fullcrashdump来告诉引擎,崩溃时生成Full Dump。这样就可以勉强弥补Mini Dump所带来的不足。

生成Dump

UE4程序Crash的时候会自动生成Dump,因为引擎中提供了Crash Handler:

38710d77b0decd6aca83f5b3a01ca9b6.png

但假如我想随意生成Dump呢?比如没有Crash而是假死了,怎么知道问题出在哪呢?

有很多种方式可以做到,我个人比较常用的是:

  • 用VS Attach上去,然后Debug->Break All->Save Dump As。可以创建Mini Dump或者Full Dump;
  • 用任务管理器,右键对应的进程->Create dump file。只能创建Full Dump;

ea4347773f7dbd94eaed9691474389d9.png

其他的方法可以参考这里:

https://www.wintellect.com/how-to-capture-a-minidump-let-me-count-the-ways/


结束

感谢阅读!希望大家能有所收获,不正之处还请指教。



推荐阅读
  • 优化联通光猫DNS服务器设置
    本文详细介绍了如何为联通光猫配置DNS服务器地址,以提高网络解析效率和访问体验。通过智能线路解析功能,域名解析可以根据访问者的IP来源和类型进行差异化处理,从而实现更优的网络性能。 ... [详细]
  • 如何配置Unturned服务器及其消息设置
    本文详细介绍了Unturned服务器的配置方法和消息设置技巧,帮助用户了解并优化服务器管理。同时,提供了关于云服务资源操作记录、远程登录设置以及文件传输的相关补充信息。 ... [详细]
  • 本文深入探讨了Linux系统中网卡绑定(bonding)的七种工作模式。网卡绑定技术通过将多个物理网卡组合成一个逻辑网卡,实现网络冗余、带宽聚合和负载均衡,在生产环境中广泛应用。文章详细介绍了每种模式的特点、适用场景及配置方法。 ... [详细]
  • Docker的安全基准
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • PHP 5.2.5 安装与配置指南
    本文详细介绍了 PHP 5.2.5 的安装和配置步骤,帮助开发者解决常见的环境配置问题,特别是上传图片时遇到的错误。通过本教程,您可以顺利搭建并优化 PHP 运行环境。 ... [详细]
  • CMake跨平台开发实践
    本文介绍如何使用CMake支持不同平台的代码编译。通过一个简单的示例,我们将展示如何编写CMakeLists.txt以适应Linux和Windows平台,并实现跨平台的函数调用。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 邮件(带附件,模拟文件上传,跨服务器)发送核心代码1.测试邮件发送附件接口***测试邮件发送附件*@parammultipartFile*@return*@RequestMappi ... [详细]
  • 掌握远程执行Linux脚本和命令的技巧
    本文将详细介绍如何利用Python的Paramiko库实现远程执行Linux脚本和命令,帮助读者快速掌握这一实用技能。通过具体的示例和详尽的解释,让初学者也能轻松上手。 ... [详细]
  • 国内BI工具迎战国际巨头Tableau,稳步崛起
    尽管商业智能(BI)工具在中国的普及程度尚不及国际市场,但近年来,随着本土企业的持续创新和市场推广,国内主流BI工具正逐渐崭露头角。面对国际品牌如Tableau的强大竞争,国内BI工具通过不断优化产品和技术,赢得了越来越多用户的认可。 ... [详细]
  • Windows 系统下 MySQL 8.0.11 的安装与配置
    本文详细介绍了在 Windows 操作系统中安装和配置 MySQL 8.0.11 的步骤,包括环境准备、安装过程以及后续配置,帮助用户顺利完成数据库的部署。 ... [详细]
  • 网络攻防实战:从HTTP到HTTPS的演变
    本文通过一系列日记记录了从发现漏洞到逐步加强安全措施的过程,探讨了如何应对网络攻击并最终实现全面的安全防护。 ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
author-avatar
松原电信曹玉威_203
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有