注意LockCount字段的值-11:
0:000> dt _RTL_CRITICAL_SECTION 0x7c8897a0 mfc80u!_RTL_CRITICAL_SECTION +0x000 DebugInfo : 0x7c8897c0 _RTL_CRITICAL_SECTION_DEBUG +0x004 LockCount : -11
将其格式化为二进制:
0:003> .formats -0n11Evaluate expression: Hex: fffffff5 Decimal: -11 Octal: 37777777765 Binary: 11111111 11111111 11111111 11110101按照LockCount在Svr2K3 SP1起的定义:
综合以上信息,可以作出这样一种猜测:
3号线程(1788.af8 )曾经和另外一个线程一起等待LdrLockLoaderLock这个关键区,当用于LdrLockLoaderLock的线程离开这个关键区时,系统唤醒了另外一个线程,但这个线程在关键区内意外退出了,系统在清理线程时,擦除了LdrLockLoaderLock中OwningThread中的登记......
那么这另外一个线程是谁呢?这从DUMP文件就较难获悉了,可以考虑以下方法:
1)启用MSI的诊断日志:http://msdn.microsoft.com/en-us/library/aa372847(VS.85).aspx
2)如果是在调试目标,那么把g_dmDiagnosticMode改为1,启用调试信息输出
3)使用如下命令之一,启用验证器的死锁检测功能(参见《软件调试》第19章):
appverif.exe -enable locks -for TestApplicaiton.exe gflags.exe -v IMAGE /enable TestApplicaiton.exe LOCK_CHECKS
因为等待最终是发生在内核态的,等待对象的很多属性也只有内核模式才看得到,所以最好同时配以内核调试,在抓取转储时,同时抓取内核转储。
每个CS结构都有一个对应的Event对象,当需要等待进入临界区时,等待线程就开始等待这个事件,LockSemaphore 字段就是这个Event对象的句柄:
0:000> dt _RTL_CRITICAL_SECTION 0x7c8897a0 mfc80u!_RTL_CRITICAL_SECTION +0x000 DebugInfo : 0x7c8897c0 _RTL_CRITICAL_SECTION_DEBUG +0x004 LockCount : -11 +0x008 RecursionCount : 0 +0x00c OwningThread : (null) +0x010 LockSemaphore : 0x00000238 +0x014 SpinCount : 0
在内核转储会话中,可以先切换到出问题的进程,然后执行!handle 238显示这个Event对象的详细信息,其中包括这个对象的内核态地址。然后可以使用!object命令进一步观察这个对象。
也可以使用dt -r _KEVENT 对象地址来观察Event对象,其中包含对象的信号状态和等待这个对象的线程列表。