检测线程死锁的另一种方法(付界面卡死检测方法)

由来:之前笔者写了一篇《一个检测线程死锁的方法》用于检测自己的程序在使用锁的时候是否发生死锁的文章,但考虑到那个方法存在很大的局限性:

  1. 只有代码中使用了那个锁封装类对象才能检测到线程死锁。
  2. 考虑到性能问题,只是在debug模式下才开启了这个功能,在Release版下是无效的。全靠开发在调试模式下才能检测到是否存在死锁,实验场景和样本都太狭隘。
  3. 只做了临界区(Critical Section)锁的封装,互斥量(Mutex)之类的死锁并没有做相应的封装。
  4. 也是最重要的,在实际场景中,程序包含了sdk的代码,这部分代码不仅不会去使用那个锁封装,而且对于客户端来说,sdk部分是黑盒,是完全不可控的。也就是说如果sdk出现了死锁,那就完全未知了。

解决方法:其实windows有一些可以检测线程死锁状态的api,可以检测到线程的状态。这个API就是GetThreadWaitChain,名字来看大概就是获取线程的等待链信息。这个函数获取等待链信息(具体可以参考《windows核心编程》中的WaitChainTraversal相关内容):

               A、第1个节点:表示线程本身(如运行状态Runing或Blocked)

    B、第2个节点:该线程正在等待什么东西(如锁、进/线程、COM调用等等)

    C、第3个节点:这个锁被哪个线程挂有?

    D、第4个节点:挂有这个锁的线程又在等什么东西?周而复始的查下去……

也就是用他可以枚举到Critical Section、Mutex的死锁、Critical Section、Mutex的遗弃(Abandoned)状态(其实也可以认为是死锁状态的一种)、WaitForSingleObject/WaitForMultiObject等待线程或者进程卡死状态、sendmessage等待状态,如下图:

备注:该方法无法检测到Crt库封装的std::mutex以及std::recursive_mutex发生死锁时的检测,本人建议尽量不用这两个封装锁。

然后根据这个方法可以实时的获取一个进程的线程运行状态,据此我写了两个小工具:

  • 1.可视化的查看进程状态信息的“ProcessInfo"工具, 具体使用方法可查看:ProcessInfo问题排查辅助工具
  • 2.助手死锁状态监测以及界面卡死状态监测和自动上报后台监视进程“DeadLockInspector.exe”。

DeadLockInspector.exe: 这个进程是后台运行的助手性能监视进程,放在助手目录下并由助手启动,主要功能如下:

  1. 助手启动的时候可选择性启动这个进程,进程后台运行,无界面,对用户无打扰,助手退出后该后台进程也会自动退出。
  2. 该工具一旦监测到助手线程存在Critical Section、Mutex的死锁、Critical Section、Mutex的遗弃(Abandoned)状态就会马上生成死锁线程回溯的堆栈信息文件、助手的mini dump文件、助手主界面截图信息文件以及助手所有线程目前状态信息文件并一并打包成zip包后放到助手上传目录等待助手上传。
  3. 该工具一旦检测到助手主界面线程卡死超过5秒(时间可调)就会立即生成锁线程回溯的堆栈信息文件、助手的mini dump文件、助手主界面截图信息文件并一并打包成zip包后放到助手上传目录,等待助手上传。(程序会在助手卡死的30秒内每5秒钟生成一次助手的相应状态信息的文件,并最后一起打包)

             

性能问题:

  1. 程序独立出助手作为独立进程,主要是为了尽量减小对助手(客户端)稳定性和性能的影响。
  2. 程序每隔1s钟往助手发送一个消息,用Procexp.exe工具查看,对助手主界面线程CPU消耗影响在0.01以下,几乎可以忽略不计。
  3. 程序每隔10s钟轮询一次获取线程死锁状态信息,峰值CPU消耗大概为4.43%(助手线程数量较多的原因),持续时间0.5s以内。(由于考虑到会对CPU有一定的消耗,所以加长了轮询时间间隔)
     

界面卡死检测原理:定时发送Msg给目标界面,目标界面收到后马上回复,如果目标界面线程出现卡死,那么就不会回复相应的消息,以此判断主界面线程是否是处于卡死状态。

拓展:未来可以在这个程序上继续拓展一些助手性能检测功能,比如cpu消耗监测等等。

注:代码主要来源于《Windows核心编程》代码中的《04-ProcessInfo》以及《09-LockCop》章节 

发布了7 篇原创文章 · 获赞 1 · 访问量 6982

猜你喜欢

转载自blog.csdn.net/liaozhilong88/article/details/97626503