-
-
WinDbg的 !CS -l 和 !locks 使用练习
-
2024-2-19 21:15 1682
-
最近尝试分析一个程序死锁的问题,发现无论是使用!cs -l 还是 !locks都是没有任何有效的输出,于是怀疑是不是自己的WinDbg版本或环境配置上出了问题,导致了这两个命令失效,所以有了本篇简单的用例来测试WinDbg命令,顺便针对这两个分析线程死锁的高频命令做一个详细的笔记。如果有对WinDbg调试道友,可以加个好友一起讨论讨论。
测试部分代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | CRITICAL_SECTION cs1, cs2; void DeadlockThread1( int threadId) { EnterCriticalSection(&cs1); std::cout << "Thread " << threadId << " entered cs1\n" ; Sleep( 100 ); / / 让线程暂停一段时间,以便另一个线程有机会执行 EnterCriticalSection(&cs2); std::cout << "Thread " << threadId << " entered cs2\n" ; LeaveCriticalSection(&cs2); LeaveCriticalSection(&cs1); } void DeadlockThread2( int threadId) { EnterCriticalSection(&cs2); std::cout << "Thread " << threadId << " entered cs2\n" ; Sleep( 100 ); / / 让线程暂停一段时间,以便另一个线程有机会执行 EnterCriticalSection(&cs1); std::cout << "Thread " << threadId << " entered cs1\n" ; LeaveCriticalSection(&cs1); LeaveCriticalSection(&cs2); } |
因为两个线程函数同时拉起,导致了临界区死锁问题。
使用WinDbg启动程序,并使用命令 !cs -l 进行查看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 0 : 006 > !cs - l - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DebugInfo = 0x773e4a40 Critical section = 0x00075058 (CriticalSectionDead!cs1 + 0x0 ) LOCKED LockCount = 0x1 WaiterWoken = No OwningThread = 0x0000367c RecursionCount = 0x1 LockSemaphore = 0xFFFFFFFF SpinCount = 0x020007d0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DebugInfo = 0x773e4a60 Critical section = 0x00075070 (CriticalSectionDead!cs2 + 0x0 ) LOCKED LockCount = 0x1 WaiterWoken = No OwningThread = 0x0000338c RecursionCount = 0x1 LockSemaphore = 0xFFFFFFFF SpinCount = 0x020007d0 |
分析命令输出各字段的含义:
- !cs -l 是一个 WinDbg 的扩展命令,用于列出所有当前存在的临界区以及它们的状态。
- Critical Section 这行显示了一个临界区的地址,以及它属于哪个模块和哪个临界区变量。在这里,临界区属于 CriticalSectionDead 模块,并且它的名字是 cs1。
- LockCount 表示这个临界区被锁定的次数。
- SpinCount 表示在尝试获取临界区锁定时,线程会进行自旋(忙等待)的次数。如果自旋后仍然无法获取锁,线程就会进入睡眠状态。
- OwningThread 显示当前拥有这个临界区的线程的线程 ID。
- RecursionCount 表示拥有这个临界区的线程已经重新进入这个临界区的次数。在某些情况下,一个线程可能会多次进入同一个临界区,这个计数就是用来跟踪这种情况的。
另外,分析死锁还有 !locks
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 0 : 006 > !locks CritSec CriticalSectionDead!cs1 + 0 at 00075058 WaiterWoken No LockCount 1 RecursionCount 1 OwningThread 367c EntryCount 0 ContentionCount 1 * * * Locked CritSec CriticalSectionDead!cs2 + 0 at 00075070 WaiterWoken No LockCount 1 RecursionCount 1 OwningThread 338c EntryCount 0 ContentionCount 1 * * * Locked Scanned 5 critical sections |
分析命令输出各字段的含义:
- !locks 显示调试目标进程中所有被锁定的临界区(Critical Sections)的信息。
- EntryCount 表示尝试进入临界区的次数。0 通常表示没有线程尝试进入这个临界区,但这可能是一个不准确的值,因为它取决于具体的调试器扩展实现。
- ContentionCount 表示线程在尝试获取这个临界区时发生争用的次数。1 表示发生了一次争用。
上面的两个命令基本分析出了死锁的状态,接下来使用 ~*kvn 来查看所有线程的堆栈情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | 0 : 006 > ~ * kvn 0 Id : 3a10 . 1b00 Suspend: 1 Teb: 00d26000 Unfrozen # ChildEBP RetAddr Args to Child 00 00f5fd30 76dbada9 000000e8 00000000 00000000 ntdll!NtWaitForSingleObject + 0xc (FPO: [ 3 , 0 , 0 ]) 01 00f5fda4 76dbad02 000000e8 ffffffff 00000000 KERNELBASE!WaitForSingleObjectEx + 0x99 (FPO: [SEH]) 02 00f5fdb8 0007150e 000000e8 ffffffff 0111ef58 KERNELBASE!WaitForSingleObject + 0x12 (FPO: [Non - Fpo]) 03 00f5fdd0 00071700 00000001 01116cd0 0111ef58 CriticalSectionDead!main + 0x4e (FPO: [ 0 , 0 , 4 ]) (CONV: cdecl) [E:\debug - demo - set \DeadLocks\CriticalSectionDead\main.cpp @ 37 ] 04 (Inline) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CriticalSectionDead!invoke_main + 0x1c (Inline Function @ 00071700 ) (CONV: cdecl) [D:\a\_work\ 1 \s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78 ] 05 00f5fe18 766bfcc9 00d23000 766bfcb0 00f5fe84 CriticalSectionDead!__scrt_common_main_seh + 0xfa (FPO: [Non - Fpo]) (CONV: cdecl) [D:\a\_work\ 1 \s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288 ] 06 00f5fe28 77327c5e 00d23000 3d12d987 00000000 KERNEL32!BaseThreadInitThunk + 0x19 (FPO: [Non - Fpo]) 07 00f5fe84 77327c2e ffffffff 77348c18 00000000 ntdll!__RtlUserThreadStart + 0x2f (FPO: [SEH]) 08 00f5fe94 00000000 00071788 00d23000 00000000 ntdll!_RtlUserThreadStart + 0x1b (FPO: [Non - Fpo]) 1 Id : 3a10 .ef4 Suspend: 1 Teb: 00d29000 Unfrozen # ChildEBP RetAddr Args to Child 00 0130f778 772f5bd0 000000c4 01115678 00000010 ntdll!NtWaitForWorkViaWorkerFactory + 0xc (FPO: [ 5 , 0 , 0 ]) 01 0130f938 766bfcc9 01114f60 766bfcb0 0130f9a4 ntdll!TppWorkerThread + 0x2a0 (FPO: [Non - Fpo]) 02 0130f948 77327c5e 01114f60 3cd7dea7 00000000 KERNEL32!BaseThreadInitThunk + 0x19 (FPO: [Non - Fpo]) 03 0130f9a4 77327c2e ffffffff 77348c18 00000000 ntdll!__RtlUserThreadStart + 0x2f (FPO: [SEH]) 04 0130f9b4 00000000 772f5930 01114f60 00000000 ntdll!_RtlUserThreadStart + 0x1b (FPO: [Non - Fpo]) 2 Id : 3a10 . 3524 Suspend: 1 Teb: 00d2c000 Unfrozen # ChildEBP RetAddr Args to Child 00 0140fd74 772f5bd0 000000c4 01115af0 00000010 ntdll!NtWaitForWorkViaWorkerFactory + 0xc (FPO: [ 5 , 0 , 0 ]) 01 0140ff34 766bfcc9 01114f60 766bfcb0 0140ffa0 ntdll!TppWorkerThread + 0x2a0 (FPO: [Non - Fpo]) 02 0140ff44 77327c5e 01114f60 3ca7d8a3 00000000 KERNEL32!BaseThreadInitThunk + 0x19 (FPO: [Non - Fpo]) 03 0140ffa0 77327c2e ffffffff 77348c18 00000000 ntdll!__RtlUserThreadStart + 0x2f (FPO: [SEH]) 04 0140ffb0 00000000 772f5930 01114f60 00000000 ntdll!_RtlUserThreadStart + 0x1b (FPO: [Non - Fpo]) 3 Id : 3a10 . 38ec Suspend: 1 Teb: 00d2f000 Unfrozen # ChildEBP RetAddr Args to Child 00 0154f6b0 772f5bd0 000000c4 01116918 00000010 ntdll!NtWaitForWorkViaWorkerFactory + 0xc (FPO: [ 5 , 0 , 0 ]) 01 0154f870 766bfcc9 01114f60 766bfcb0 0154f8dc ntdll!TppWorkerThread + 0x2a0 (FPO: [Non - Fpo]) 02 0154f880 77327c5e 01114f60 3cb3dfdf 00000000 KERNEL32!BaseThreadInitThunk + 0x19 (FPO: [Non - Fpo]) 03 0154f8dc 77327c2e ffffffff 77348c18 00000000 ntdll!__RtlUserThreadStart + 0x2f (FPO: [SEH]) 04 0154f8ec 00000000 772f5930 01114f60 00000000 ntdll!_RtlUserThreadStart + 0x1b (FPO: [Non - Fpo]) 4 Id : 3a10 . 367c Suspend: 1 Teb: 00d32000 Unfrozen # ChildEBP RetAddr Args to Child 00 0168fae0 7731f999 00075074 00000000 00075070 ntdll!NtWaitForAlertByThreadId + 0xc (FPO: [ 2 , 0 , 0 ]) 01 0168fb10 7731f6ed 00000000 00000000 00075070 ntdll!RtlpWaitOnAddressWithTimeout + 0x64 (FPO: [Non - Fpo]) 02 0168fbb0 7730011a 000713a0 00000001 00000001 ntdll!RtlpWaitOnCriticalSection + 0x18d (FPO: [Non - Fpo]) 03 0168fbe8 772fff69 00000000 0168fc04 000713ef ntdll!RtlpEnterCriticalSectionContended + 0x1aa (FPO: [Non - Fpo]) 04 0168fbf4 000713ef 00075070 000713a0 0168fc14 ntdll!RtlEnterCriticalSection + 0x49 (FPO: [ 1 , 0 , 0 ]) 05 0168fc04 766bfcc9 00000001 766bfcb0 0168fc70 CriticalSectionDead!DeadlockThread1 + 0x4f (FPO: [Non - Fpo]) (CONV: cdecl) [E:\debug - demo - set \DeadLocks\CriticalSectionDead\main.cpp @ 12 ] 06 0168fc14 77327c5e 00000001 3c8fdb73 00000000 KERNEL32!BaseThreadInitThunk + 0x19 (FPO: [Non - Fpo]) 07 0168fc70 77327c2e ffffffff 77348c18 00000000 ntdll!__RtlUserThreadStart + 0x2f (FPO: [SEH]) 08 0168fc80 00000000 000713a0 00000001 00000000 ntdll!_RtlUserThreadStart + 0x1b (FPO: [Non - Fpo]) 5 Id : 3a10 . 338c Suspend: 1 Teb: 00d35000 Unfrozen # ChildEBP RetAddr Args to Child 00 017cf7c8 7731f999 0007505c 00000000 00075058 ntdll!NtWaitForAlertByThreadId + 0xc (FPO: [ 2 , 0 , 0 ]) 01 017cf7f8 7731f6ed 00000000 00000000 00075058 ntdll!RtlpWaitOnAddressWithTimeout + 0x64 (FPO: [Non - Fpo]) 02 017cf898 7730011a 00071430 00000002 00000002 ntdll!RtlpWaitOnCriticalSection + 0x18d (FPO: [Non - Fpo]) 03 017cf8d0 772fff69 017cf8e8 0007147f 00075058 ntdll!RtlpEnterCriticalSectionContended + 0x1aa (FPO: [Non - Fpo]) 04 017cf8d8 0007147f 00075058 00071430 017cf8f8 ntdll!RtlEnterCriticalSection + 0x49 (FPO: [ 1 , 0 , 0 ]) 05 017cf8e8 766bfcc9 00000002 766bfcb0 017cf954 CriticalSectionDead!DeadlockThread2 + 0x4f (FPO: [Non - Fpo]) (CONV: cdecl) [E:\debug - demo - set \DeadLocks\CriticalSectionDead\main.cpp @ 23 ] 06 017cf8f8 77327c5e 00000002 3c9bde57 00000000 KERNEL32!BaseThreadInitThunk + 0x19 (FPO: [Non - Fpo]) 07 017cf954 77327c2e ffffffff 77348c18 00000000 ntdll!__RtlUserThreadStart + 0x2f (FPO: [SEH]) 08 017cf964 00000000 00071430 00000002 00000000 ntdll!_RtlUserThreadStart + 0x1b (FPO: [Non - Fpo]) # 6 Id: 3a10.35dc Suspend: 1 Teb: 00d38000 Unfrozen # ChildEBP RetAddr Args to Child 00 0190f948 7736dba9 3c77de7b 7736db70 7736db70 ntdll!DbgBreakPoint (FPO: [ 0 , 0 , 0 ]) 01 0190f978 766bfcc9 00000000 766bfcb0 0190f9e4 ntdll!DbgUiRemoteBreakin + 0x39 (FPO: [Non - Fpo]) 02 0190f988 77327c5e 00000000 3c77dee7 00000000 KERNEL32!BaseThreadInitThunk + 0x19 (FPO: [Non - Fpo]) 03 0190f9e4 77327c2e ffffffff 77348c18 00000000 ntdll!__RtlUserThreadStart + 0x2f (FPO: [SEH]) 04 0190f9f4 00000000 7736db70 00000000 00000000 ntdll!_RtlUserThreadStart + 0x1b (FPO: [Non - Fpo]) |
根据!cs -l可知OwningThread = 0x0000367c,线程367c拥有临界区Critical section = 0x000750585058,结合上面线程的栈帧发现,当前线程试图进入临界区5070.见4号线程栈帧04第一个参数.同理,有5号线程拥有临界区Critical section = 0x00075070 (CriticalSectionDead!cs2+0x0),但是试图进入4号线程的临界区。综上所述,两个线程各自的拥有的临界区未退出,想要访问对方的临界区而形成交叉,导致自旋锁失败而产生死锁.
开头提到遇到 !cs -l 和 !locks 无反应的程序已上传到附件TestDeadLocks.7z,如有有大佬经过,可有偿.
关于TestDeadLocks.7z起初是想复现 [原创]调试TerminateThread导致的死锁 这位大佬的文章,遇到了现在的问题。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2024-2-19 22:31
被_THINCT编辑
,原因:
赞赏
他的文章
[转帖]WinDbg使用笔记
2384
谁下载
谁下载
看原图