关于使用WinDebug查看线程死锁问题

         最近项目接近尾声,不过项目到了测试的时候大问题来了,偶尔界面直接卡死以至于后续无法测试,初步怀疑是哪里死锁了,由于自己对WinDebug不是很熟悉,只知道WinDebug有查找内存泄露问题、线程死锁等相关功能,于是吩咐下面的人用winDebug去查找问题,索性网络资源比较多,通过一番调研找到了WInDebug死锁查找的解决方法,不过作为项目开发经理,遇到死锁的事应该能够在其他同时不能解决的时候也能提供相应的帮助是理所当然的,于是带着这种心态去学习了WinDebug查找死锁的方法,总结如下:


死锁,成立的条件就是:

(1)起码有两把锁以上,假如仅有两把锁,且为锁A和锁B;

(2)线程1已经拿了一把锁A然后还想去拿锁B;线程2已经拿了一把锁B然后还想去拿锁A;

(3)没有拿到另一把锁不强行释放自己获取的锁;


于是,死锁来了~


实现所有的实例:

(1)初始化两把锁A和B,启动两个线程(可以一个主线程和一个子线程);

(2)第一个线程已经拿了锁A,第二个线程已经拿了锁B,第一线程尝试去拿锁B,第二线程尝试去拿锁A;


基于以上思想,我写了一个Demon,出现了死锁情况,然后使用WinDebug查找死锁情况,方法介绍如下:

(1)启用应用程序的用户堆栈功能-我使用winDebug开始没有启用导致WinDebug绑定进程失败;

          方法使用WinDebug目录下的gflags.exe:打开命令行,进入WinDebug目录运行:gflag.exe /i  调试exe的全路径 +ust 

          然后回车,ust 就是 user stack用户堆栈;

(2)打开WinDebug然后选择File-->Attach to Process 附加到需要调试的进程exe;

          如果需要查看所有线程堆栈那么在命令窗口输入:~*kv

输出如下所有线程堆栈:


          此时能打开所有线程的堆栈,如果需要看某一线程的堆栈,则输入*1kv也就是打印线程1的堆栈;从所有线程的堆栈中我们可以看到各个线程的堆栈信息,如果此时有堆栈锁,一般都在栈顶会有API调用:ntdll!RtlpWaitOnCriticalSection,也就是找到所有的该API调用我们就可以找到对应的死锁线程信息;

   0  Id: 11264.10f20 Suspend: 1 Teb: 7efdd000 Unfrozen
ChildEBP RetAddr  Args to Child              
003eecd8 77709e2e 00000124 00000000 00000000 ntdll!ZwWaitForSingleObject+0x15 (FPO: [3,0,0])
003eed3c 77709d12 00000000 00000000 00000001 ntdll!RtlpWaitOnCriticalSection+0x13e (FPO: [Non-Fpo])
003eed64 0113e289 0161cd80 c3c8e76a 00000001 ntdll!RtlEnterCriticalSection+0x150 (FPO: [Non-Fpo])
003eee7c 011548ff 003ef808 003eeeb4 757d62fa LockDemon!CLockDemonDlg::OnInitDialog+0x179 (FPO: [Non-Fpo]) (CONV: thiscall) [e:\work\c++\test\lockdemon\lockdemon\lockdemondlg.cpp @ 122]
003eee88 757d62fa 002d1984 00000110 001d1b26 LockDemon!AfxDlgProc+0x3f (CONV: stdcall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\dlgcore.cpp @ 28]
003eeeb4 757ff9df 01119035 002d1984 00000110 USER32!InternalCallWinProc+0x23
003eef30 757ff784 00000000 01119035 002d1984 USER32!UserCallDlgProcCheckWow+0xd7 (FPO: [Non-Fpo])
003eef80 757ff889 0409a140 00000000 00000110 USER32!DefDlgProcWorker+0xb7 (FPO: [Non-Fpo])
003eefa0 757d62fa 002d1984 00000110 001d1b26 USER32!DefDlgProcW+0x29 (FPO: [Non-Fpo])
003eefcc 757d6d3a 77734308 002d1984 00000110 USER32!InternalCallWinProc+0x23
003ef044 757e0d27 00000000 77734308 002d1984 USER32!UserCallWinProcCheckWow+0x109 (FPO: [Non-Fpo])
003ef07c 757e0d4d 77734308 002d1984 00000110 USER32!CallWindowProcAorW+0xab (FPO: [Non-Fpo])
003ef09c 0115e4d4 77734308 002d1984 00000110 USER32!CallWindowProcW+0x1b (FPO: [Non-Fpo])
003ef0c0 0115c5fd 00000110 001d1b26 00000000 LockDemon!CWnd::DefWindowProcW+0x34 (CONV: thiscall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 1043]
003ef0dc 01156235 003ef808 003ef0f8 012282d3 LockDemon!CWnd::Default+0x3d (CONV: thiscall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 274]
003ef100 011603e5 001d1b26 00000000 c3c8fb46 LockDemon!CDialog::HandleInitDialog+0xd5 (CONV: thiscall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\dlgcore.cpp @ 673]
003ef250 0115fb62 00000110 001d1b26 00000000 LockDemon!CWnd::OnWndMsg+0x835 (CONV: thiscall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 2018]
003ef270 0115c400 00000110 001d1b26 00000000 LockDemon!CWnd::WindowProc+0x32 (CONV: thiscall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 1755]
003ef2ec 0115cb16 003ef808 002d1984 00000110 LockDemon!AfxCallWndProc+0xf0 (CONV: stdcall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 240]
003ef30c 757d62fa 002d1984 00000110 001d1b26 LockDemon!AfxWndProc+0xa6 (CONV: stdcall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 403]
003ef338 757d6d3a 01127310 002d1984 00000110 USER32!InternalCallWinProc+0x23
003ef3b0 757d6de8 00000000 01127310 002d1984 USER32!UserCallWinProcCheckWow+0x109 (FPO: [Non-Fpo])
003ef40c 757d6e44 0409a140 00000000 00000110 USER32!DispatchClientMessage+0xe0 (FPO: [Non-Fpo])
003ef448 776e010a 003ef460 00000000 003ef694 USER32!__fnDWORD+0x2b (FPO: [Non-Fpo])
003ef45c 0409a140 00000000 00000110 001d1b26 ntdll!KiUserCallbackDispatcher+0x2e (FPO: [0,0,0])
WARNING: Frame IP not in any known module. Following frames may be wrong.
003ef4c0 7580206f 0409a140 00000000 01127310 0x409a140
003ef594 758010d3 00f00000 00000006 000000a4 USER32!InternalCreateDialog+0xb9f (FPO: [Non-Fpo])
003ef5b8 757ec659 00f00000 0163fdc8 00000000 USER32!CreateDialogIndirectParamAorW+0x33 (FPO: [Non-Fpo])
003ef5d8 01155513 00f00000 0163fdc8 00000000 USER32!CreateDialogIndirectParamW+0x1b (FPO: [Non-Fpo])
003ef6a0 01155e39 0163fdc8 00000000 00f00000 LockDemon!CWnd::CreateDlgIndirect+0x263 (CONV: thiscall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\dlgcore.cpp @ 312]
003ef714 0113da0d c3c8f1a6 00000000 00000000 LockDemon!CDialog::DoModal+0x199 (CONV: thiscall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\dlgcore.cpp @ 576]
003ef8b0 0153da94 757d9ee1 003ef8c0 00280026 LockDemon!CLockDemonApp::InitInstance+0xad (CONV: thiscall) [e:\work\c++\test\lockdemon\lockdemon\lockdemon.cpp @ 64]
003ef8d4 0153d98a 00f00000 00000000 006710b4 LockDemon!AfxWinMain+0x84 (CONV: stdcall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\winmain.cpp @ 37]
003ef8ec 014c1015 00f00000 00000000 006710b4 LockDemon!wWinMain+0x1a (CONV: stdcall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\appmodul.cpp @ 34]
003ef990 014c0e9f 003ef9a4 76e0336a 7efde000 LockDemon!__tmainCRTStartup+0x165 (CONV: cdecl) [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 263]
003ef998 76e0336a 7efde000 003ef9e4 777092b2 LockDemon!wWinMainCRTStartup+0xf (CONV: cdecl) [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 182]
003ef9a4 777092b2 7efde000 61654582 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
003ef9e4 77709285 0111a958 7efde000 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
003ef9fc 00000000 0111a958 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])


   1  Id: 11264.11284 Suspend: 1 Teb: 7efda000 Unfrozen
ChildEBP RetAddr  Args to Child              
0631fe7c 759714ab 00000104 00000000 00000000 ntdll!ZwWaitForSingleObject+0x15 (FPO: [3,0,0])
0631fee8 76e01194 00000104 ffffffff 00000000 KERNELBASE!WaitForSingleObjectEx+0x98 (FPO: [Non-Fpo])
0631ff00 76e01148 00000104 ffffffff 00000000 kernel32!WaitForSingleObjectExImplementation+0x75 (FPO: [Non-Fpo])
0631ff14 7235107b 00000104 ffffffff 76e010ff kernel32!WaitForSingleObject+0x12 (FPO: [Non-Fpo])
WARNING: Stack unwind information not available. Following frames may be wrong.
0631ff58 7235290a 72377db8 76e0336a 72377db8 CKSee+0x107b
0631ff6c 777092b2 72377db8 676a43ca 00000000 CKSee!Kinkoo_GetInterface+0x13aa
0631ffac 77709285 72352900 72377db8 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
0631ffc4 00000000 72352900 72377db8 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])


   2  Id: 11264.9238 Suspend: 1 Teb: 7efd7000 Unfrozen
ChildEBP RetAddr  Args to Child              
064efbf4 77709e2e 00000120 00000000 00000000 ntdll!ZwWaitForSingleObject+0x15 (FPO: [3,0,0])
064efc58 77709d12 00000000 00000000 005819a0 ntdll!RtlpWaitOnCriticalSection+0x13e (FPO: [Non-Fpo])
064efc80 01140f78 0161cd68 00000000 00000000 ntdll!RtlEnterCriticalSection+0x150 (FPO: [Non-Fpo])
064efd58 014c22e3 003ef808 c5b8f482 00000000 LockDemon!MyFunc+0x48 (FPO: [Non-Fpo]) (CONV: stdcall) [e:\work\c++\test\lockdemon\lockdemon\lockdemondlg.cpp @ 79]
064efd94 014c2254 00000000 064efdac 76e0336a LockDemon!_callthreadstartex+0x53 (CONV: cdecl) [f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c @ 348]
064efda0 76e0336a 005819a0 064efdec 777092b2 LockDemon!_threadstartex+0xa4 (CONV: stdcall) [f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c @ 331]
064efdac 777092b2 005819a0 6715418a 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
064efdec 77709285 014c21b0 005819a0 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
064efe04 00000000 014c21b0 005819a0 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])

#  3  Id: 11264.1175c Suspend: 1 Teb: 7ef9f000 Unfrozen
ChildEBP RetAddr  Args to Child              
0580fcf8 7776fb96 64db414e 00000000 00000000 ntdll!DbgBreakPoint (FPO: [0,0,0])
0580fd28 76e0336a 00000000 0580fd74 777092b2 ntdll!DbgUiRemoteBreakin+0x3c (FPO: [Non-Fpo])
0580fd34 777092b2 00000000 64db4112 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
0580fd74 77709285 7776fb5a 00000000 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
0580fd8c 00000000 7776fb5a 00000000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])


由此可以看出总共有0-3号4个线程,绿色的表示线程线程索引号,蓝色的表示线程的地址(11264.1175c中11264位线程ID,1175c为线程地址),当然所有线程打印出来一个一个找锁不容易观察到死锁,为了能直接打印进程中线程之间的死锁,直接使用如下命令

首先,查看进程中所有的锁,输入命令 :  !locks,如下:
0:003> !locks

此时打印出进程中所有的锁信息:

CritSec LockDemon!g_LockA+0 at 0161cd80
WaiterWoken        No
LockCount          1
RecursionCount     1
OwningThread       9238
EntryCount         0
ContentionCount    1
*** Locked


CritSec LockDemon!g_LockB+0 at 0161cd68
WaiterWoken        No
LockCount          1
RecursionCount     1
OwningThread       10f20
EntryCount         0
ContentionCount    1
*** Locked


第一把锁地址为0161cd80,LockCount表示占有该锁的数目,RecursionCount  表示拥有者线程进入该锁的次数(进入拿到锁后还可以进入多次),OwningThread 拥有者线程为9238,也就是说0161cd80锁被地址为9238线程占用,也就是线程索引号为2 ,线程ID为11264线程地址为9238 的线程已经拿了锁0161cd80,却在等待锁 0161cd68 

第二把锁地址为0161cd68,LockCount表示占有该锁的数目,RecursionCount  表示拥有者线程进入该锁的次数(进入拿到锁后还可以进入多次),OwningThread 拥有者线程为10f20,也就是说0161cd68锁被地址为10f20线程占用,也就是线程索引号为0 ,线程ID为11264线程地址为10f20的线程已经拿了锁0161cd68,却在等待锁 0161cd80 


这样死锁就这么被确定下来,线程0和线程2互锁!


堆栈列信息解释如下:

   2  Id: 11264.9238 Suspend: 1 Teb: 7efd7000 Unfrozen
ChildEBP RetAddr  Args to Child              
064efbf4 77709e2e 00000120 00000000 00000000 ntdll!ZwWaitForSingleObject+0x15 (FPO: [3,0,0])
064efc58 77709d12 00000000 00000000 005819a0 ntdll!RtlpWaitOnCriticalSection+0x13e (FPO: [Non-Fpo])
064efc80 01140f78 0161cd68 00000000 00000000 ntdll!RtlEnterCriticalSection+0x150 (FPO: [Non-Fpo])
064efd58 014c22e3 003ef808 c5b8f482 00000000 LockDemon!MyFunc+0x48 (FPO: [Non-Fpo]) (CONV: stdcall) [e:\work\c++\test\lockdemon\lockdemon\lockdemondlg.cpp @ 79]
064efd94 014c2254 00000000 064efdac 76e0336a LockDemon!_callthreadstartex+0x53 (CONV: cdecl) [f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c @ 348]
064efda0 76e0336a 005819a0 064efdec 777092b2 LockDemon!_threadstartex+0xa4 (CONV: stdcall) [f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c @ 331]
064efdac 777092b2 005819a0 6715418a 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
064efdec 77709285 014c21b0 005819a0 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
064efe04 00000000 014c21b0 005819a0 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])

第三列也就是后面API的第一个参数,对于API 即RtlEnterCriticalSection的第一个参数也就是第三列就是锁的地址,也就是线程id为2(线程地址为0x9238)的线程在等锁0x0161cd68 


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
WinDBG 是个非常强大的调试器,它设计了极其丰富的功能来支持各种调试任务,包括用户 态调试、 内核态调试、 调试转储文件、 远程调试等等。 WinDBG 具有非常大的灵活性和可扩展性, 用来满足各种各样的调试需求,比如用户可以自由定义调试事件的处理方式,编写调试扩展模块 来定制和补充 WinDBG 的调试功能。 尽管 WinDBG 是个典型的窗口程序, 但是它的大多数调试功能还是以手工输入命令的方式来 工作的。目前版本的 WinDBG 共提供了 20 多条标准命令, 140 多条元命令( Meta-commands), 和难以计数的大量扩展命令。学习和灵活使用这些命令是学习 WinDBG 的关键,也是难点。 上一章我们从设计的角度分析了 WinDBG ,本章将从使用(用户)的角度介绍 WinDBG 。我 们先介绍工作空间的概念和用法(第 1 节),然后介绍命令的分类和不同种类的命令提示符(第 2 节)。 第 3 节介绍不同的调试模式, 也就是如何与不同特征的调试目标建立调试会话。 第 4 节介绍 上下文的概念和在调试时应该如何切换和控制上下文。第 5 节介绍调试事件和如何定制调试事件 的处理方式。 从第 6 节到第 9 节我们将分别介绍如何在 WinDBG 中完成典型的调试操作, 比如控 制调试目标(第 6 节)、设置断点(第 7 节)、观察栈(第 8 节)以及如何观察和修改数据(第 9 节)。
WinDbg是一款由微软公司开发的强大的调试工具,用于分析和调试Windows操作系统的内核和用户模式崩溃、挂起和性能问题。下面是WinDbg的使用教程: 1. 下载和安装:从微软官网下载最新版的Windows Driver Kit(WDK),并安装到你的计算机上。 2. 配置符号文件路径:在WinDbg中,选择 "File" -> "Symbol File Path",然后添加Microsoft的符号服务器的地址,以便在调试过程中能够正确地解析和显示符号。 3. 打开调试目标:选择 "File" -> "Attach to a Process",然后选择你想要调试的进程或者直接运行一个可执行文件。 4. 设置断点:在代码中找到你想要设置断点的位置,然后在WinDbg中使用 "bu" 命令设置一个断点。例如:bu <函数名>。 5. 开始调试:点击 "Go" 或按下 "F5" 键开始执行程序,并在断点处停下。 6. 分析崩溃信息:当程序崩溃时,WinDbg会停在断点处,并显示崩溃信息。可以使用命令 "k" 来查看函数调用栈,"r" 来查看寄存器值,"dv" 来查看变量的值。 7. 单步调试:可以使用命令 "p" 或 "t" 来逐行执行代码。"p" 命令会逐行执行,而 "t" 命令会逐过程执行。 8. 内核调试:如果你想调试Windows内核,需要在启动时选择 "Debugging Mode",然后使用串口或网络调试连接到WinDbg。 总之,WinDbg是一款强大的调试工具,可以帮助程序员快速定位和解决Windows系统的崩溃和性能问题。但它的使用需要一定的经验和技巧,建议在使用前先阅读相关的文档和教程,以便更好地利用它的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝壳里的沙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值