临界截面悬挂分析

Critical section hang analysis with Windbg

本文关键字:截面      更新时间:2023-10-16

当我的应用程序有一段时间没有响应时,我最近得到了一个通过procdump生成的转储文件。

当我对转储文件运行!锁时,我得到一个单独的条目,类似于:

0:000> !locks
CritSec +123456 at 00123456
WaiterWoken       No
LockCount         0
RecursionCount    2
OwningThread      aaaa
EntryCount        0
ContentionCount   0
*** Locked

只有一个清单。这是所有。当我进一步深入到:

0:000> dt RTL_CRITICAL_SECTION 00123456
MyModule!RTL_CRITICAL_SECTION
  +0x000 DebugInfo      : 0x00aabbcc _RTL_CRITICAL_SECTION_DEBUG
  +0x004 LockCount      : 0n-2
  +0x008 RecursionCount : 0n2
  +0x00c OwningThread   : 0x0000aaaa Void
  +0x010 LockSemaphore  : (null)
  +0x014 SpinCount      : 0

问题:

1)我应该把MyModule!RTL_CRITICAL_SECTION作为明确的线索,这个关键部分可能是在MyModule中定义的吗?

2)有没有办法让Windbg显示这个临界区的实际变量名?(即假设#1为真,并且这是一个应用程序代码定义/访问的CS。)

3)为什么上述清单中的LockCount值彼此不一致?(一个是0,另一个是-2)

4)我想我很了解LockCount,知道它不能低于-1。更不用说RecursionCount似乎与LockCount严重不一致。

我想最重要的问题是,我应该把这归咎于腐败的CS吗?

我可以很容易地用一个示例应用程序再现负LockCount的效果,下面是我的答案:

对您问题的回答

关于1)

我应该把MyModule!RTL_CRITICAL_SECTION作为明确的线索,这个临界区可能在MyModule中定义?

是的,这是你对临界区的定义,它可能与微软的定义一致,也可能不一致。使用微软的定义使用dt nt!_RTL_CRITICAL_SECTION

关于2)

有没有办法让Windbg显示这个临界区的实际变量名?(即假设#1为真,并且这是一个应用程序代码定义/访问的CS。)

可以,如果它被调用堆栈上的函数使用。使用.frame,导航到帧,使用?? variableName显示变量,例如

0:000> k L2
 # ChildEBP RetAddr  
00 0116faa4 00d67419 KERNELBASE!DebugBreak+0x2
01 0116fc5c 00d67ebe CriticalSectionLeaveTwice!main+0x109
0:000> .frame 1
01 0116fc5c 00d67ebe CriticalSectionLeaveTwice!main+0x109 [c:userstdocumentsvisual studio 2015projectscriticalsectionleavetwicecriticalsectionleavetwicecriticalsectionleavetwice.cpp @ 24]
0:000> ?? CriticalSection
struct _RTL_CRITICAL_SECTION
   +0x000 DebugInfo        : 0xffffffff _RTL_CRITICAL_SECTION_DEBUG
   +0x004 LockCount        : 0n-2
   +0x008 RecursionCount   : 0n2
   +0x00c OwningThread     : 0x00000d6c Void
   +0x010 LockSemaphore    : (null) 
   +0x014 SpinCount        : 0x20007d0
关于3)

为什么上述清单中的LockCount值彼此不一致?(一个是0,另一个是-2)

字段LockCount不再是一个真正的锁计数,如这个答案。相关部分:

在Microsoft Windows Server 2003 Service Pack 1和更高版本的Windows中,LockCount字段解析如下:

  • 最低位表示锁状态。如果该位为0,则锁定临界区;
  • 下一个位显示线程是否已经被这个锁唤醒。如果这个位是0,那么这个锁已经唤醒了一个线程;如果是1,表示没有线程被唤醒。
  • 剩余的位是等待锁的线程数的补充。

恕我直言,!locks命令应该为您做解释。

-2的特殊值是二进制的11111111 ... 1111110,所以最后一位是0,这意味着临界区被锁定。前面的位是1,所以没有线程被唤醒。剩余1的补码为0,对应!locks的锁计数输出

这意味着你分析的临界区没有涉及死锁。

关于4)

我想我很了解LockCount,知道它不能低于-1。更不用说RecursionCount似乎与LockCount严重不一致。

见# 3 .

关于未编号问题5)

我应该把这个归咎于损坏的CS吗?

。它似乎没有损坏。

使用!dlk进行死锁分析

检查它是否负责死锁,我建议使用sosex的(下载)!dlk命令。虽然它主要是一个。net的扩展,但我曾经要求使它在没有。net的情况下也能用于关键区——这个功能在SOSex的一个新版本中实现了。

如果找不到。net,它将输出一个警告,然后继续分析临界区:

在你的例子中,它可能看起来像这样:

0:000> !dlk
Unable to initialize .NET data interface. The CLR has not yet been loaded in the process (mscorwks/clr module not loaded).
Examining CriticalSections...
No deadlocks detected.

用法:

.load c:pathtososex.dll
!dlk

如果它标识死锁,则非常容易读取。如果不是,你仍然需要应用其他技术,也就是说,它不能保证没有其他类型的死锁(例如,如果等待链包含其他类型的同步对象,如线程、事件等)。

示例输出(不用于临界区,但将类似):

0:010> !dlk
Deadlock detected:
CLR thread 4 holds sync block 00000000024c6970 OBJ:000000007fff0f80[System.String] STRVAL=SYNC1
             waits sync block 00000000024c6928 OBJ:000000007fff0fa8[System.String] STRVAL=SYNC2
CLR thread 5 holds sync block 00000000024c6928 OBJ:000000007fff0fa8[System.String] STRVAL=SYNC2
             waits sync block 00000000024c6970 OBJ:000000007fff0f80[System.String] STRVAL=SYNC1
CLR Thread 4 is waiting at ConsoleTestApp.ConsoleTestApp.MonitorDeadlockThreadProc()+0xa4(IL) [C:devConsoleTestAppConsoleTestApp.cs, line 195]
CLR Thread 5 is waiting at ConsoleTestApp.ConsoleTestApp.MonitorDeadlockThreadProc()+0xa4(IL) [C:devConsoleTestAppConsoleTestApp.cs, line 195]
1 deadlock detected. 

挂分析

!analyze -hang和Debug Diag可能有助于挂起分析。