热门标签 | 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/


结束

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



推荐阅读
  • C/C++ 应用程序的安装与卸载解决方案
    本文介绍了如何使用Inno Setup来创建C/C++应用程序的安装程序,包括自动检测并安装所需的运行库,确保应用能够顺利安装和卸载。 ... [详细]
  • 本文介绍了Tomcat的基本操作,包括启动、关闭及首次访问的方法,并详细讲解了如何在IDEA中创建Web项目,配置Servlet及其映射,以及如何将项目部署到Tomcat。 ... [详细]
  • 本文探讨了使用Python实现监控信息收集的方法,涵盖从基础的日志记录到复杂的系统运维解决方案,旨在帮助开发者和运维人员提升工作效率。 ... [详细]
  • 本文介绍了如何使用Node.js通过两种不同的方法连接MongoDB数据库,包括使用MongoClient对象和连接字符串的方法。每种方法都有其特点和适用场景,适合不同需求的开发者。 ... [详细]
  • Asynchronous JavaScript and XML (AJAX) 的流行很大程度上得益于 Google 在其产品如 Google Suggest 和 Google Maps 中的应用。本文将深入探讨 AJAX 在 .NET 环境下的工作原理及其实现方法。 ... [详细]
  • Docker安全策略与管理
    本文探讨了Docker的安全挑战、核心安全特性及其管理策略,旨在帮助读者深入理解Docker安全机制,并提供实用的安全管理建议。 ... [详细]
  • 本文详细介绍了Oracle 11g中的创建表空间的方法,以及如何设置客户端和服务端的基本配置,包括用户管理、环境变量配置等。 ... [详细]
  • 本文介绍了SIP(Session Initiation Protocol,会话发起协议)的基本概念、功能、消息格式及其实现机制。SIP是一种在IP网络上用于建立、管理和终止多媒体通信会话的应用层协议。 ... [详细]
  • 本文详细介绍了在Windows系统中如何配置Nginx以实现高效的缓存加速功能,包括关键的配置文件设置和示例代码。 ... [详细]
  • Windows Phone 弹出窗口实现方案
    在当前版本的 Silverlight for Windows Phone 中,由于缺乏对 ChildWindow 的支持,开发者需要采用其他方法来实现弹出窗口的功能。本文将探讨几种有效的解决方案。 ... [详细]
  • 本文作为《WM平台上使用Sybase Anywhere 11》系列的第二篇,将继续探讨在Windows Mobile (WM) 系统中如何高效地操作Sybase Anywhere 11数据库。继上一篇关于安装与基本测试的文章之后,本篇将深入讲解数据库的具体操作方法。 ... [详细]
  • 本文介绍了如何利用X_CORBA实现远程对象调用,并通过多个示例程序展示了其功能与应用,包括基础的Hello World示例、文件传输工具以及一个完整的聊天系统。 ... [详细]
  • c#  项目文件,C#viual studio使用方法
    一、项目文件1)Properties节点下主要存放的是当前程序集相关的信息,如版本号、标题等。双击”Properties“,打开如下项目属 ... [详细]
  • NPM 脚本 'start' 退出,未显示 create-react-app 服务器正在监听请求
    遇到 NPM 脚本 'start' 退出且未显示 create-react-app 服务器正在监听请求的问题,请求帮助。 ... [详细]
  • C#中使用Dotfuscator Pro进行代码混淆
    由于Visual Studio自带的混淆工具功能有限,本文将介绍如何使用Dotfuscator Pro进行更高级的代码混淆。包括设置字符串加密、添加可执行文件和动态链接库、调整混淆选项等步骤。 ... [详细]
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社区 版权所有