1.背景
最近由于线上的程序发生了死锁,而且重现的概率很低,正好客户反馈一个任务超时了,登上线上环境发现有一个“僵尸”进程,占用内存不波动,cpu仍在占用,
那么用创建转储文件,用windbg调试吧。
2.准备
2.1.下载windbg
- 需要下载Windows 调试工具 (WinDbg):Windows 10 SDK,安装时候根据需要,可以只安装Debugging Tools For Windows,即windbg;
- 如果已下载并安装Windows 10 SDK,而没有安装Windbg,那么在控制面板》程序》Windows Software Development Kit》右键选择更改》change》勾选Debugging Tools For Windows》安装 即可;
可参考官方文章:下载 Windows 调试工具
2.2.相关知识
可参考官方文章:
Windows 调试入门
WinDbg 入门(内核模式)
WinDbg 入门(用户模式)
使用 WinDbg 进行调试
后文还会贴出一些有参考价值的博客文章;
2.3.所需文件
2.3.1.DMP文件
.dump(创建转储文件)
- 可以为进程创建转储文件(dmp),既可以在任务有管理中选中进程》右键》创建转储文件;
- 也可以用windbg附加到进程(附加后会让程序暂停,注意这时候要用windbg的g(go)或者step out、step over等让程序继续运行,运行完再创建dmp文件)让程序运行到出错位置后,windbg会发现异常并中断,然后输入:.dump /f e:dump/XXXX.dmp,生成全信息的dmp文件;
- 当然转储文件有多种,详细参考官方文章;
2.3.2.符号文件
需要符号文件,就像VS里面设置符号加载,这里可以包含调试目标程序的符号文件(pdb)路径、microsoft符号文件及符号缓存目录;借助符号文件和源代码可以准确的定位堆栈位置及异常位置;
在windbg中符号路径设置里注意事项:SRV*e:\symbols*http://msdl.microsoft.com/download/symbols;C:\Users\heuwz\source\repos\x64\Release
其中*e:\symbols*为符号缓存目录(就像VS中设置符号缓存目录),http://msdl.microsoft.com/download/symbols为microsoft符号服务器,C:\Users\heuwz\source\repos\x64\Release为目标程序符号(pdb文件)所在目录;
在Windbg中Ctrl+s快捷键可以打开设置符号路径面板;
设置时可以合理的利用面板中的Browser按钮选择本地目录,这样可以最大化的避免手动输入可能带来的格式问题;
2.3.3.源代码文件
需要结合符号文件和源代码文件来分析确定堆栈位置、异常位置等再源代码中的位置,从而提供更准确的调试信息,供使用者进一步的分析问题;
设置源代码文件路径(ctrl+p),仅仅是相关的代码文件就可;
同样可以合理的利用面板中的Browser按钮选择本地目录,这样可以最大化的避免手动输入可能带来的格式问题;
3.开始
为方便说明,下面以一个例子进行讲解,
3.1.准备程序
代码(会引发异常)如下,
#include "pch.h"
#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;
class TestClass
{
// 该类仅做示例用,有很多不规范的地方,比如构造函数,析构函数,赋值重载,成员字段公开等问题
// 读者可批判的看待
public:
int NTest;
};
int main()
{
std::cout << "Hello World!\n";
Sleep(50000);
TestClass* testPtr = 0;
// 出问题了
testPtr->NTest = 3;
std::cout << testPtr->NTest << std::endl;
}
编译生成可执行程序;
3.2.生成DMP文件
- windbg发现异常并中断,然后输入:.dump /f e:dump/XXXX.dmp,来生成全信息的dmp文件;更多命令可参见.dump(创建转储文件)
- 成功生成DMP文件,包含程序异常时的信息;
3.3.调试分析问题
让用户把生成的DMP文件发给自己,然后自己继续用Windbg+DMP文件+符号+源代码文件来调试分析问题;
(1)亲测,可以先设置源码路径和符号路径,再去打开dmp文件,会自动定位到异常位置;
(2)亲测,也可以先打开dmp文件;再设置符号路径(ctrl+s),注意设置完后勾选reload;再设置源代码文件路径(ctrl+p),仅仅是相关的代码文件就可,也会自动定位到异常位置;(3)符号路径设置里注意事项:SRV*e:\symbols*http://msdl.microsoft.com/download/symbols;C:\Users\heuwz\source\repos\x64\Release
其中SRV后面**中间的路径为符号缓存路径,会将通过后面符号器下载的符号缓存到这个位置;后面紧跟的是微软符号服务器;再后面是调试的dmp文件对应exe的符号路径;
(4)设置符号路径和代码文件路径时可以用browser按钮来设置,这样会避免格式出错问题;
当然如果是死锁问题,那么需要输入命令:!analyze -v -hang
回车,等待分析完,然后结合其他命令,如,
~*kb:显示当前进程所有线程的堆栈;
~1kb:显示一号线程堆栈;
d:命令显示esp寄存器指向的内存;
!cs -s:如果可能的话,显示每个临界区的初始堆栈回溯;
!cs -l:仅显示锁定的临界区;
!locks:查看所有的线程占用的锁情况;
~~[ce84]s:切换到线程ce84;
kb:查看该线程的函数调用栈情况;
定位各线程的堆栈和对应在代码中的位置,然后分析产生死锁的原因;
3.4.其他
可以用windbg分析dmp,也可以直接用vs打开dmp文件来分析,
实测过程中,
(1)用windows管理器的“创建转储文件”生成的dmp,既可以用vs打开,也可以用windbg打开;
(2)用windbg附加到进程的方式(附加后会让程序暂停,注意这时候要用windbg的g(go)或者step out、step over等让程序继续运行,运行完再创建dmp文件)
让程序运行到出错位置后,windbg会发现异常并中断,然后输入:.dump /f e:dump/XXXX.dmp,来生成全信息的dmp文件;
这种方式的dmp文件可以用windbg分析,但用vs打开会提示版本过旧,无法打开;原因可能如下使用 Visual Studio 打开转储文件
4.参考资料
除了官方文章外,学习过程中还参考了如下文章,
4.1.参考较多的文章
1、使用windbg抓取崩溃文件和分析的过程
https://blog.csdn.net/breaksoftware/article/details/13989025
备注:实操性强;
2、winDbg定位异常崩溃和线程死锁三步骤
https://blog.csdn.net/lv_yjie2011/article/details/41706231
备注:这一篇博客属于导航类的博客,导航到了其他博客,也十分有帮助;
3、WinDbg非常简单的调试dmp文件
https://blog.csdn.net/u010340160/article/details/84452408
备注:这篇博客说的没错,很简单,但对于新手来说太简单了,很多相关的概念和原理没说,只是一个流程,照着做;
4、记录windbg调试临界区死锁问题
https://www.cnblogs.com/pro-love/p/10936029.html
备注:实操性强,有帮助;
5、windbg解决线程死锁
https://blog.csdn.net/woshiyuanlei/article/details/47657313
备注:介绍了丰富的windbg命令,根据需要可以参考;
4.2.其他参考文章
1、17.windbg-!cs、~~[TID](经典死锁)
https://blog.csdn.net/hgy413/article/details/7572097
2、Windbg中查看函数参数
https://blog.csdn.net/wonderdaydream/article/details/73554644
3、Windbg找出死锁
https://www.cnblogs.com/leo_wl/p/3406212.html
4、windbg的使用四(Windbg检查死锁 )
https://blog.csdn.net/chenchong_219/article/details/25081379
5、windbg调试死锁,好迷茫,请教
https://bbs.csdn.net/topics/390740501?page=1
6、win10ltsc版基本信息及优点介绍
http://www.somode.com/softjc/9504.html
7、VC2005中fopen的ccs=UNICODE实测<zt> + UTF8 如何转变为 ansi
https://www.cnblogs.com/slash/archive/2010/06/27/1766225.html
8、多线程死锁调试小技巧
https://www.cnblogs.com/zhuyp1015/p/3618863.html
9、Detecting reflective DLL loading with Windows Defender ATP
https://www.microsoft.com/security/blog/2017/11/13/detecting-reflective-dll-loading-with-windows-defender-atp/
10、Application Control for Windows
https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/windows-defender-application-control
11、What's new in Windows 10 Enterprise 2019 LTSC
https://docs.microsoft.com/en-us/windows/whats-new/ltsc/whats-new-windows-10-2019
12、windbg调试std::mutex死锁问题!!!
https://blog.csdn.net/jiangdong2007/article/details/89513524
可以再研究下符号服务器的搭建,这样方便给客户调试问题;
5.结论
很多时候,用户使用程序出现问题了 或者 线上机器运行程序出现问题了,不方便在用户电脑上/线上机器调试问题,如果再在用户电脑/线上机器上安装VS,拉取源代码仓库,那就更难可行并且问题风险多多,操作复杂;
那么这时候可以让用户转储为DMP文件,并提供程序的版本号等信息,然后自己用Windbg或者VS调试分析问题,当然这样又会引发两个怎么让工作效率更高更可靠的问题,
1、用户使用程序崩溃了要手动联系开发方并手动生成DMP文件发发给开发方吗?
2、符号文件该怎么管理?用户告诉你了版本号,你还能找到符号文件吗?
当然这两个问题,大公司都有解决方案;
欢迎讨论。转载注明出处。