使用Windbg进行关键部分挂起分析

时间:2016-08-10 17:02:11

标签: c++ windbg critical-section

当我的应用程序暂时没有响应时,我最近得到了一个通过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吗?

1 个答案:

答案 0 :(得分:6)

我可以轻松地使用示例应用程序重现负面LockCount的效果,以下是我的答案:

回答您的问题

关于1)

  

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

是的,这是您对关键部分的定义,可能符合也可能不符合Microsoft的定义。要使用Microsoft的定义,请使用dt nt!_RTL_CRITICAL_SECTION

关于2)

  

有没有办法让Windbg显示这个关键部分的实际变量名? (即假设#1为真,这是应用程序代码已定义/访问的CS。)

是的,如果它被调用堆栈上的函数使用。使用.frame,导航到frme,并使用?? 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:\users\t\documents\visual studio 2015\projects\criticalsectionleavetwice\criticalsectionleavetwice\criticalsectionleavetwice.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不再是真正的锁定计数,如中所述 this answer。相关部分:

  

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

     
      
  • 最低位显示锁定状态。如果该位为0,则临界区被锁定;如果为1,则关键部分未锁定。
  •   
  • 下一位显示是否已为此锁定唤醒线程。如果该位为0,则该锁已被唤醒;如果是1,则没有线程被唤醒。
  •   
  • 其余位是等待锁定的线程数的补码。
  •   

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

-2的特定值是二进制的11111111 ... 1111110,因此最后一位为0,表示临界区被锁定。之前的位是1,因此没有线程被唤醒。剩下的一个补码是0,它对应于!locks的锁定计数输出。

这意味着,您分析的关键部分没有陷入死锁。

关于4)

  

我想我已经足够了解LockCount,知道它不能低于-1。更不用说RecursionCount似乎与LockCount差不多了。

见#3。

关于未编号的问题5)

  

我应该把这个搞砸到腐败的CS吗?

没有。它似乎没有被破坏。

使用!dlk进行死锁分析

要检查它是否对死锁负责,我建议使用download!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:\path\to\sosex.dll
!dlk

如果它识别出死锁,那么它很容易阅读。如果没有,你仍然需要应用其他技术,即它不能保证没有其他类型的死锁(例如,如果等待链包括其他类型的同步对象,如线程,事件......)。 / p>

示例输出(不是关键部分,但类似):

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:\dev\ConsoleTestApp\ConsoleTestApp.cs, line 195]
CLR Thread 5 is waiting at ConsoleTestApp.ConsoleTestApp.MonitorDeadlockThreadProc()+0xa4(IL) [C:\dev\ConsoleTestApp\ConsoleTestApp.cs, line 195]

1 deadlock detected. 

挂起分析

!analyze -hangDebug Diag可能有助于悬挂分析。