死锁破解大招,解决程序卡死的所有烦恼!
Posted 51CTO技术栈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了死锁破解大招,解决程序卡死的所有烦恼!相关的知识,希望对你有一定的参考价值。
"卡死"本文指的是程序永久无响应。出现这种状况一般有3个原因:
主线程与其它线程相互等待死锁;
主线程被别人挂起;
主线程陷入死循环。
遇到这样的问题该如何排查?往往让人无从下手。常规的办法就是不断增加日志,然后逼近现场,但是如果现场偶发,则这个过程会比较漫长。而且有的时候卡死往往是系统级的,日志可能都不知道写哪里。碰到此类问题往往让人崩溃。
有没有一招制敌的秘籍呢?看了下面死锁案例破解过程,你可能就会觉得这类问题也是小菜一碟了。
案例:测试现场在执行日初始化时界面卡死。
开发已经定位到某个线程在准备调用 A.dll 的一个导出函数 B 时,加载 A.dll 卡死。虽然现场是必现的,但是由于是在加载 dll 时卡死,开发连加日志的地方都找不到。现在就可以参考一下案例中的破解过程。
【准备工具】
1、Procdump -- 抓现场 dump 的工具。
2、Windbg -- 分析 dump 的工具。Windbg 如何使用这里不作详细介绍。
1、用 procdump 工具抓取卡死进程的 dump。
2、DOS 界面执行命令,procdump -ma 进程 ID 保存路径。
【Windbg打开dump分析】
一、首先确定主线程的进程
输入 kv 命令
最后的调用堆栈:
▪ ntdll!RtlEnterCriticalSection 主线程在尝试进入临界区。
▪ ntdll!ZwWaitForSingleObject 然后等待内核对象。
可以看出主线程应该是发生死锁了。
往上回溯调用栈 rtl60!ClassesTThreadListLockList$qqrv+0xc
这个是 delphi 的 bpl 导出函数,bpl 导出函数可以直接通过名称就对应到源代码。
继续往前回溯
通过导出函数名字可以直接定位到函数 TWinControl.MainWndProc
即
然后 delphi 建一个空工程,带包 vcl,任意找一行代码下断点。
运行到断点,菜单 view → Debug Windows → CPU 打开 CPU 窗口。
对应源代码是
定位到函数 FreeDeviceContexts
输入指令!cs 03281d00 可以获得该临界的详细信息
其中 OwningThread 指是该临界被哪个线程所持有
31b0 是持有临界的线程 ID,对应 53 号线程
三、53号线程的进程排查
输入命令 ~53 kv,显示 53 号线程调用栈
调用栈 vcl60!FormsTCustomFormCreateWnd$qqrv+0x12d,可以直接定位到代码
源代码 SetWindowPos 和调用栈 user32!NtUserSetWindowPos+0x15 吻合
这说明 53 号线程正在创建一个 StayOnTop 类型的窗口
四、谁是 StayOnStop 类型的窗口
由于 vcl 和 rtl 代码无法确定创建了什么窗口,因此堆栈往上回溯,进入业务函数
这个需要源代码和 dump 的版本完全一致;
打开 delphi 调试状态,手工计算;
如果相同,则定位的源代码是正确的,否则这个办法走不通。
▲方法二:特征代码搜索法
特征代码搜索法的优点:源代码和 dump 版本不一致问题也不大,只要出问题的那小段代码没有改变,是可以定位到的。另外如果 C++ 的 DLL 符号文件缺失,或者不一致的情况下也可以启用这种办法定位代码。
下面在这个案例中演示下这种方法。
查看 dump 中 fixedincometrade!GetTmpStockSQL+0x3c947 的汇编代码。
另外选择特征码需具备唯一性,即在进程中,这段代码需要不容易重复,一般来说长度越长越不容易重复。
打开 delphi 开启 fixedincometrade 新进程(进程只要启动起来就可以),PID=2668
启动新 windbg,Attach 到进程 2668,选择非侵入式。
执行命令如下:
s -b 17710000 La04000 64 ff 30 64 89 20 83 2d
找到 uBuildReport.pas 1561 源代码。
可以看到单元的初始化在创建一个窗体。这个初始化是 dll 加载时自动执行的,和现场吻合。经检查该窗体也的确是 StayOnTop 类型的。
至此卡死原因已豁然开朗,找到了原因接下来的工作就简单了。
六、程序为什么会卡死
▲53号日初始化线程的执行轨迹如下:
加载fixedincometrade,执行单元初始化 →
创建StayOnTop窗口 →
CreateHandle先锁定CanvasList →
GetDeviceContext → TCustomForm.CreateWnd →
SetWindowPos 主线程发生交互
▲主线程的执行轨迹如下:
MainWndProc消息循环 →
FreeDeviceContexts →
等待 CanvasList 锁
死锁由此发生。而直接原因是因为线程去操作界面。
之所以标题称为破解,是因为从上面的排查过程来看,用这种方法,不用熟悉模块代码,不用了解业务场景,反向行之,只要捕捉到一次现场,就可以由现场而还原到代码环境,一击而中,无所遁形。
来源:http://rdc.hundsun.com/portal/article/702.html
IT技术群,期待你的加入
后台回复“入群”审核受邀
广 告
以上是关于死锁破解大招,解决程序卡死的所有烦恼!的主要内容,如果未能解决你的问题,请参考以下文章