【问题标题】:Critical section hang analysis with Windbg使用 Windbg 进行关键部分挂起分析
【发布时间】:2016-08-10 17:02:10
【问题描述】:

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

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

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 吗?

【问题讨论】:

    标签: c++ windbg critical-section


    【解决方案1】:

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

    回答您的问题

    关于 1)

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

    是的,这是您对关键部分的定义,它可能符合也可能不符合 Microsoft 的定义。要使用微软的定义,请使用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:\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 进行死锁分析

    要检查它是否导致死锁,我建议使用's (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
    

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

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

    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 可能有助于挂起分析。

    【讨论】:

    • 我理解递归计数如何增加。但是如果递归计数为 2,那么锁计数怎么可能为 0(或 -2,具体取决于您相信的清单)?尤其是考虑线程是否调用 LeaveCriticalSection 递归和锁定计数都下降。
    • @ForeverLearning:我已经复制了你的案例并更新了我的答案。
    • 你是忍者!感谢所有的帮助。我会对此进行更长时间的调查并报告。
    • 如果我没看错的话,-2 的LockCount 字段意味着一个线程持有锁并且没有线程在等待它。这不是几乎排除了这个特定关键部分涉及死锁的可能性吗?
    • @HarryJohnston:完全正确。我添加了-2的解释和这个结论。
    猜你喜欢
    • 1970-01-01
    • 2014-11-18
    • 2021-07-24
    • 2014-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多