【问题标题】:Detecting application hang检测应用程序挂起
【发布时间】:2010-08-09 04:58:34
【问题描述】:

我有一个用 C++ 编写的非常大、复杂(百万+ LOC)的 Windows 应用程序。我们每天都会收到一些关于应用程序已锁定并且必须强制关闭的报告。

虽然我们有大量关于崩溃的报告,但我想将其扩展为包括这些挂起场景 - 即使有大量日志记录,我们也无法找到其中一些问题的根本原因。我们可以清楚地看到活动在哪里停止 - 但不是为什么它停止了,即使在评估所有线程的输出时也是如此。

问题在于检测何时发生挂起。到目前为止,我能想到的最好的方法是看门狗线程(因为我们有证据表明后台线程会继续运行而没有问题),它会定期用自定义消息 ping 主窗口,并确认它是在一个及时时尚。这只会捕获 GUI 线程挂起,但这似乎是其中大多数发生的地方。如果在可配置的时间范围内未收到回复,我们将捕获内存和堆栈转储,并为用户提供继续等待或重新启动应用程序的选项。

有没有人知道比以这种方式定期轮询主窗口更好的方法?它看起来非常笨拙,但我还没有看到可以在我们的平台上运行的替代方案——Windows XP 和 Windows 2003 Server。我看到 Vista 对此有更好的工具,但不幸的是,这对我们没有帮助。

可以说,我们已经对此进行了广泛的诊断,并且只取得了有限的成功。请注意,实时附加 windbg 不是一种选择,因为我们要到事件发生数小时或数天后才能获得报告。我们将能够检索内存转储和日志文件,但仅此而已。

任何超出我上述计划的建议将不胜感激。

【问题讨论】:

  • 当它挂起时所有线程都锁定了吗?应用是否继续生成日志文件?
  • 您希望应用程序本身检测到它已挂起还是有一个单独的进程来监控应用程序选项?
  • 该应用程序确实 - 在大多数情况下 - 继续在主窗口以外的其他线程中生成日志记录。在极少数情况下,似乎所有日志记录都跨线程停止。我们希望让应用程序进行自我监控。

标签: c++ windows


【解决方案1】:

答案很简单:SendMessageTimeout

使用此 API,您可以向窗口发送消息并等待超时,然后再继续;如果应用程序在超时之前响应,则它仍在运行,否则它会挂起。

【讨论】:

  • 谢谢,我不知道这个——这很适合我已经计划的内容。
  • 看起来mq_timedsend() 可能相当于Linux。我只是根据我正在阅读的内容说话,而不是根据经验说话。 linux.die.net/man/3/mq_send
【解决方案2】:

一种选择是始终在您自己的“调试器”下运行您的程序。某些程序(例如 GetRight)执行此操作是为了进行复制保护,但您也可以执行此操作来检测挂起。本质上,您在程序中包含一些代码以通过调试 API 附加到进程,然后使用该 API 定期检查挂起。当程序第一次启动时,它会检查是否附加了一个调试器,如果没有,它会运行自己的另一个副本并附加到它 - 所以第一个实例除了充当调试器之外什么都不做,第二个实例是“真正的“一个。

您如何实际检查挂起是另一个完整的问题,但是访问调试 API 应该有某种方法可以合理有效地检查堆栈是否已更改(即,不加载所有符号)。不过,您可能只需要每隔几分钟左右执行一次,因此即使效率不高也可能没问题。

这是一个有点极端的解决方案,但应该是有效的。打开和关闭此行为也很容易 - 如果您愿意,可以使用命令行开关或#define。我确信已经有一些代码可以执行此类操作,因此您可能不必从头开始。

【讨论】:

  • 谢谢,我会看看这个。我担心所涉及的开销。该应用程序已经非常庞大,并且由于终端服务环境的共享特性,我们已经到了增加任何显着内存或 CPU 开销都可能出现问题的地步。
【解决方案3】:

一个建议:

假设问题是由锁定引起的,您可以从看门狗线程中转储您的互斥锁和信号量状态。通过一些工作(跟踪您的调用图),您可以确定您是如何到达死锁的,哪些调用路径相互阻塞,等等。

【讨论】:

  • 谢谢,在我们检测到我们被锁定的事实之后,这是一个很好的建议 - 但我正在寻找一种可靠的方法来做到这一点。
【解决方案4】:

虽然故障转储分析似乎为识别问题提供了一种解决方案,但根据我的经验,这很少会产生很多成果,因为它缺乏关于崩溃前发生的事情的足够明确的细节。即使使用您提出的工具,它也只能提供所发生事情的间接证据。我敢打赌,原因是未受保护的共享数据,因此锁定跟踪不会显示它。

根据我的经验,找到这一点的最有效方法是将应用程序的逻辑提炼到其本质,并确定必须在哪里发生冲突。有多少个线程? GUI有几个?线程在多少点交互?是的,这是很好的旧办公桌检查。可以在一两天内确定主要的可疑互动,然后让一小部分怀疑者相信互动是正确的。

【讨论】:

  • 是的,我们当然已经尝试过(并继续尝试),但到目前为止,我们还无法以原始形式或“精简”形式复制它。即使在确定了潜在的可疑交互之后,我们仍然无法强制该场景发生。我不认为转储会成为灵丹妙药,而是武器库中的另一个强大武器——即使没有转储包含的任何其他数据,堆栈跟踪也可以产生大量信息。
  • 我并不是要制作可运行的蒸馏代码。我的意思是提炼代码到它的功能核心和交互,就像在一张纸或白板上:task1: initialize; loop; wait_for_signal; perform_listbox_update; until (terminated);
  • 我们也从这个角度着手。很多问题在于,似乎有几种不同的路径会导致这种情况——所以我们面临的不仅仅是一个根本原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-11
  • 2016-08-17
  • 2011-07-04
  • 2011-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多