Windbg跟踪临界区的BUG
2015-10-23 23:26
246 查看
最近跟踪了一个程序的界面卡死问题,该卡死偶尔出现,在抓到一次dump后用windbg载入分析,打印出函数调用堆栈后,一眼可以看出是临界区死锁了。
代码:
输出该临界区的信息:
代码:
Windbg指示是0x710号线程占有该临界区在,于是看看0x710号线程是那一个,结果发现报错,一般这种情况是线程已经退出或者被终止掉了。
代码:
该临界区死锁的位置部分代码如下:
代码:
m_vctLock是ATL一个对临界区简单封装的类,让同事仔细对m_vctLock所有加锁的位置进行检查,发现除了主线程外只有另外一个工作线程会使用,用windbg查了下改工作线程并未退出,线程ID也不为 0x710,难道又被Windbg给忽悠了?打印下该临界区的数据结构看看:
代码:
发现这里显示的拥有者线程号和上面不一致,试试看是那个线程:
代码:
6号线程正处于等待任务中,对照代码,6号线程有一个观察者的回调函数会调用到CDownloadListUI类中的m_vctLock锁,但是该回调函数已经执行完毕了。那么只有一种可能,就是锁泄露了,即Lock后没有解锁。再去看该回调函数,果然发现有一个很少出现的分支下没有调用Unlock释放临界区而直接返回了,这就造成了经典的锁泄露而引起死锁的bug。
但 !cs 命令在此种情况下却给出了错误的信息,很容易造成误导而怀疑是一个拥有该锁的线程退出了而引起的。这应该算是Windbg的一个bug吧。
进一步测试该情况,发现是当64位系统下的32位进程产生的dump会有此问题,32位系统下产生的dump使用 !cs 命令执行正常。
代码:
0:000:x86> kb ChildEBP RetAddr Args to Child 0032dd0c 779ed993 00000710 00000000 00000000 ntdll_779b0000!NtWaitForSingleObject+0x15 0032dd70 779ed877 00000000 00000000 024023f0 ntdll_779b0000!RtlpWaitOnCriticalSection+0x13e 0032dd98 58a2fac3 02404c50 856fd57e 024023f0 ntdll_779b0000!RtlEnterCriticalSection+0x150 0032dffc 58a0d4d7 856fea8a 00000000 001c41a0 SogouSoftware_589d0000!CDownloadListUI::UpdateDownloadListUI+0x43
输出该临界区的信息:
代码:
0:000:x86> !cs 02404c50 ----------------------------------------- Critical section = 0x0000000002404c50 (+0x2404C50) DebugInfo = 0x0000000000611e08 LOCKED LockCount = 0xFFFFFFFF WaiterWoken = Yes OwningThread = 0x0000000000000710 RecursionCount = 0x1A38 LockSemaphore = 0x2433B08 SpinCount = 0x0000000000000000
Windbg指示是0x710号线程占有该临界区在,于是看看0x710号线程是那一个,结果发现报错,一般这种情况是线程已经退出或者被终止掉了。
代码:
0:000:x86> ~~[710] ^ Illegal thread error in '~~[710]'
该临界区死锁的位置部分代码如下:
代码:
void CDownloadListUI::UpdateDownloadListUI() { m_vctLock.Lock(); vector<int> vecDeleteItems(GetCount()); // record index to be delete std::iota(vecDeleteItems.begin(), vecDeleteItems.end(), 0); ...... m_vctLock.UnLock(); }
m_vctLock是ATL一个对临界区简单封装的类,让同事仔细对m_vctLock所有加锁的位置进行检查,发现除了主线程外只有另外一个工作线程会使用,用windbg查了下改工作线程并未退出,线程ID也不为 0x710,难道又被Windbg给忽悠了?打印下该临界区的数据结构看看:
代码:
0:000:x86> dt _RTL_CRITICAL_SECTION 02404c50 DuiLib!_RTL_CRITICAL_SECTION +0x000 DebugInfo : 0x00611e08 _RTL_CRITICAL_SECTION_DEBUG +0x004 LockCount : 0n-6 +0x008 RecursionCount : 0n1 +0x00c OwningThread : 0x00001a38 Void +0x010 LockSemaphore : 0x00000710 Void +0x014 SpinCount : 0
发现这里显示的拥有者线程号和上面不一致,试试看是那个线程:
代码:
0:000:x86> ~~[1a38] 6 Id: 2058.1a38 Suspend: 0 Teb: 7ef94000 Unfrozen Start: SogouSoftware_589d0000!_threadstartex (58a5192d) Priority: 0 Priority class: 32 Affinity: f 0:000:x86> ~6s ntdll_779b0000!ZwWaitForMultipleObjects+0x15: 779d019d 83c404 add esp,4 0:006:x86> kb ChildEBP RetAddr Args to Child 0370fa5c 768615f7 00000002 0370faac 00000001 ntdll_779b0000!ZwWaitForMultipleObjects+0x15 0370faf8 773519f8 0370faac 0370fb20 00000000 KERNELBASE!WaitForMultipleObjectsEx+0x100 0370fb40 773541d8 00000002 7efde000 00000000 kernel32!WaitForMultipleObjectsExImplementation+0xe0 0370fb5c 589f6ba0 00000002 0370fb84 00000000 kernel32!WaitForMultipleObjects+0x18 0370fbd4 58a51907 58aab894 862df68e 00000000 SogouSoftware_589d0000!CThreadQueue<TagDownloadTask>::ThreadProc+0x100 0370fc0c 58a51991 00000000 0370fc24 7735336a SogouSoftware_589d0000!_callthreadstartex+0x1b [f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c @ 314] 0370fc18 7735336a 023f5170 0370fc64 779e9882 SogouSoftware_589d0000!_threadstartex+0x64 [f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c @ 292] 0370fc24 779e9882 023f5170 771cc6bb 00000000 kernel32!BaseThreadInitThunk+0xe 0370fc64 779e9855 58a5192d 023f5170 00000000 ntdll_779b0000!__RtlUserThreadStart+0x70 0370fc7c 00000000 58a5192d 023f5170 00000000 ntdll_779b0000!_RtlUserThreadStart+0x1b
6号线程正处于等待任务中,对照代码,6号线程有一个观察者的回调函数会调用到CDownloadListUI类中的m_vctLock锁,但是该回调函数已经执行完毕了。那么只有一种可能,就是锁泄露了,即Lock后没有解锁。再去看该回调函数,果然发现有一个很少出现的分支下没有调用Unlock释放临界区而直接返回了,这就造成了经典的锁泄露而引起死锁的bug。
但 !cs 命令在此种情况下却给出了错误的信息,很容易造成误导而怀疑是一个拥有该锁的线程退出了而引起的。这应该算是Windbg的一个bug吧。
进一步测试该情况,发现是当64位系统下的32位进程产生的dump会有此问题,32位系统下产生的dump使用 !cs 命令执行正常。
相关文章推荐
- Spring MV——Controller
- LiteHttp 第八节:处理异常和取消请求
- git删除push到远程服务器的commit
- navicat初步使用
- 两个队列实现栈
- LiteHttp 第七节:重试和重定向
- Minimum Size Subarray Sum 数组
- 如何获得mysql数据库的所有的列
- 【POJ 1141】Brackets Sequence
- 查看linux的出错信息
- [深入理解Java虚拟机]第七章 类加载的时机
- [PAT (Advanced Level) ]1014.Waiting in Line 解题文档
- LiteHttp 第六节:禁用网络和流量&耗时统计
- PyCharm4注册码
- java中Builder设计模式 原理和好处
- 毛笔日常保养注意事项
- LiteHttp 第五节:文件、位图的上传和下载
- Xcode上传App到Appstore时,报错Error ITMS-90049:"This bundle is invalid.The bundle...."
- 2015年ACM长春区域赛比赛感悟
- 对 Java Serializable(序列化)分析